parser/
event.rs

1//! This module provides a way to construct a `File`.
2//! It is intended to be completely decoupled from the
3//! parser, so as to allow to evolve the tree representation
4//! and the parser algorithm independently.
5use std::mem;
6
7use crate::{
8    SyntaxKind::{self, *},
9    output::Output,
10};
11
12/// `Parser` produces a flat list of `Event`s.
13/// They are converted to a tree-structure in
14/// a separate pass, via `TreeBuilder`.
15#[derive(Debug, PartialEq)]
16pub(crate) enum Event {
17    /// This event signifies the start of the node.
18    /// It should be either abandoned (in which case the
19    /// `kind` is `TOMBSTONE`, and the event is ignored),
20    /// or completed via a `Finish` event.
21    ///
22    /// All tokens between a `Start` and a `Finish` would
23    /// become the children of the respective node.
24    ///
25    /// For left-recursive syntactic constructs, the parser produces
26    /// a child node before it sees a parent. `forward_parent`
27    /// saves the position of current event's parent.
28    ///
29    /// Consider this path
30    ///
31    /// foo::bar
32    ///
33    /// The events for it would look like this:
34    ///
35    /// ```text
36    /// START(PATH) IDENT('foo') FINISH START(PATH) T![::] IDENT('bar') FINISH
37    ///       |                          /\
38    ///       |                          |
39    ///       +------forward-parent------+
40    /// ```
41    ///
42    /// And the tree would look like this
43    ///
44    /// ```text
45    ///    +--PATH---------+
46    ///    |   |           |
47    ///    |   |           |
48    ///    |  '::'       'bar'
49    ///    |
50    ///   PATH
51    ///    |
52    ///   'foo'
53    /// ```
54    ///
55    /// See also `CompletedMarker::precede`.
56    Start {
57        kind: SyntaxKind,
58        forward_parent: Option<u32>,
59    },
60
61    /// Complete the previous `Start` event
62    Finish,
63
64    /// Produce a single leaf-element.
65    /// `n_raw_tokens` is used to glue complex contextual tokens.
66    /// For example, lexer tokenizes `>>` as `>`, `>`, and
67    /// `n_raw_tokens = 2` is used to produced a single `>>`.
68    Token {
69        kind: SyntaxKind,
70        n_raw_tokens: u8,
71    },
72    /// When we parse `foo.0.0` or `foo. 0. 0` the lexer will hand us a float literal
73    /// instead of an integer literal followed by a dot as the lexer has no contextual knowledge.
74    /// This event instructs whatever consumes the events to split the float literal into
75    /// the corresponding parts.
76    FloatSplitHack {
77        ends_in_dot: bool,
78    },
79    Error {
80        msg: String,
81    },
82}
83
84impl Event {
85    pub(crate) fn tombstone() -> Self {
86        Event::Start { kind: TOMBSTONE, forward_parent: None }
87    }
88}
89
90/// Generate the syntax tree with the control of events.
91pub(super) fn process(mut events: Vec<Event>) -> Output {
92    let mut res = Output::default();
93    let mut forward_parents = Vec::new();
94
95    for i in 0..events.len() {
96        match mem::replace(&mut events[i], Event::tombstone()) {
97            Event::Start { kind, forward_parent } => {
98                // For events[A, B, C], B is A's forward_parent, C is B's forward_parent,
99                // in the normal control flow, the parent-child relation: `A -> B -> C`,
100                // while with the magic forward_parent, it writes: `C <- B <- A`.
101
102                // append `A` into parents.
103                forward_parents.push(kind);
104                let mut idx = i;
105                let mut fp = forward_parent;
106                while let Some(fwd) = fp {
107                    idx += fwd as usize;
108                    // append `A`'s forward_parent `B`
109                    fp = match mem::replace(&mut events[idx], Event::tombstone()) {
110                        Event::Start { kind, forward_parent } => {
111                            forward_parents.push(kind);
112                            forward_parent
113                        }
114                        _ => unreachable!(),
115                    };
116                    // append `B`'s forward_parent `C` in the next stage.
117                }
118
119                for kind in forward_parents.drain(..).rev() {
120                    if kind != TOMBSTONE {
121                        res.enter_node(kind);
122                    }
123                }
124            }
125            Event::Finish => res.leave_node(),
126            Event::Token { kind, n_raw_tokens } => {
127                res.token(kind, n_raw_tokens);
128            }
129            Event::FloatSplitHack { ends_in_dot } => {
130                res.float_split_hack(ends_in_dot);
131                let ev = mem::replace(&mut events[i + 1], Event::tombstone());
132                assert!(matches!(ev, Event::Finish), "{ev:?}");
133            }
134            Event::Error { msg } => res.error(msg),
135        }
136    }
137
138    res
139}