]>
Commit | Line | Data |
---|---|---|
064997fb FG |
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. | |
064997fb FG |
5 | use std::mem; |
6 | ||
7 | use crate::{ | |
8 | output::Output, | |
9 | SyntaxKind::{self, *}, | |
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)] | |
16 | pub(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 | }, | |
9ffffee4 FG |
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 | }, | |
064997fb FG |
79 | Error { |
80 | msg: String, | |
81 | }, | |
82 | } | |
83 | ||
84 | impl 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. | |
91 | pub(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 | } | |
9ffffee4 FG |
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 | } | |
064997fb FG |
134 | Event::Error { msg } => res.error(msg), |
135 | } | |
136 | } | |
137 | ||
138 | res | |
139 | } |