diff options
Diffstat (limited to 'crates/parser/src/event.rs')
-rw-r--r-- | crates/parser/src/event.rs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs new file mode 100644 index 000000000..a7d06a815 --- /dev/null +++ b/crates/parser/src/event.rs | |||
@@ -0,0 +1,130 @@ | |||
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. | ||
5 | //! | ||
6 | //! The `TreeSink` trait is the bridge between the parser and the | ||
7 | //! tree builder: the parser produces a stream of events like | ||
8 | //! `start node`, `finish node`, and `FileBuilder` converts | ||
9 | //! this stream to a real tree. | ||
10 | use std::mem; | ||
11 | |||
12 | use crate::{ | ||
13 | ParseError, | ||
14 | SyntaxKind::{self, *}, | ||
15 | TreeSink, | ||
16 | }; | ||
17 | |||
18 | /// `Parser` produces a flat list of `Event`s. | ||
19 | /// They are converted to a tree-structure in | ||
20 | /// a separate pass, via `TreeBuilder`. | ||
21 | #[derive(Debug)] | ||
22 | pub(crate) enum Event { | ||
23 | /// This event signifies the start of the node. | ||
24 | /// It should be either abandoned (in which case the | ||
25 | /// `kind` is `TOMBSTONE`, and the event is ignored), | ||
26 | /// or completed via a `Finish` event. | ||
27 | /// | ||
28 | /// All tokens between a `Start` and a `Finish` would | ||
29 | /// become the children of the respective node. | ||
30 | /// | ||
31 | /// For left-recursive syntactic constructs, the parser produces | ||
32 | /// a child node before it sees a parent. `forward_parent` | ||
33 | /// saves the position of current event's parent. | ||
34 | /// | ||
35 | /// Consider this path | ||
36 | /// | ||
37 | /// foo::bar | ||
38 | /// | ||
39 | /// The events for it would look like this: | ||
40 | /// | ||
41 | /// | ||
42 | /// START(PATH) IDENT('foo') FINISH START(PATH) T![::] IDENT('bar') FINISH | ||
43 | /// | /\ | ||
44 | /// | | | ||
45 | /// +------forward-parent------+ | ||
46 | /// | ||
47 | /// And the tree would look like this | ||
48 | /// | ||
49 | /// +--PATH---------+ | ||
50 | /// | | | | ||
51 | /// | | | | ||
52 | /// | '::' 'bar' | ||
53 | /// | | ||
54 | /// PATH | ||
55 | /// | | ||
56 | /// 'foo' | ||
57 | /// | ||
58 | /// See also `CompletedMarker::precede`. | ||
59 | Start { | ||
60 | kind: SyntaxKind, | ||
61 | forward_parent: Option<u32>, | ||
62 | }, | ||
63 | |||
64 | /// Complete the previous `Start` event | ||
65 | Finish, | ||
66 | |||
67 | /// Produce a single leaf-element. | ||
68 | /// `n_raw_tokens` is used to glue complex contextual tokens. | ||
69 | /// For example, lexer tokenizes `>>` as `>`, `>`, and | ||
70 | /// `n_raw_tokens = 2` is used to produced a single `>>`. | ||
71 | Token { | ||
72 | kind: SyntaxKind, | ||
73 | n_raw_tokens: u8, | ||
74 | }, | ||
75 | |||
76 | Error { | ||
77 | msg: ParseError, | ||
78 | }, | ||
79 | } | ||
80 | |||
81 | impl Event { | ||
82 | pub(crate) fn tombstone() -> Self { | ||
83 | Event::Start { kind: TOMBSTONE, forward_parent: None } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | /// Generate the syntax tree with the control of events. | ||
88 | pub(super) fn process(sink: &mut dyn TreeSink, mut events: Vec<Event>) { | ||
89 | let mut forward_parents = Vec::new(); | ||
90 | |||
91 | for i in 0..events.len() { | ||
92 | match mem::replace(&mut events[i], Event::tombstone()) { | ||
93 | Event::Start { kind: TOMBSTONE, .. } => (), | ||
94 | |||
95 | Event::Start { kind, forward_parent } => { | ||
96 | // For events[A, B, C], B is A's forward_parent, C is B's forward_parent, | ||
97 | // in the normal control flow, the parent-child relation: `A -> B -> C`, | ||
98 | // while with the magic forward_parent, it writes: `C <- B <- A`. | ||
99 | |||
100 | // append `A` into parents. | ||
101 | forward_parents.push(kind); | ||
102 | let mut idx = i; | ||
103 | let mut fp = forward_parent; | ||
104 | while let Some(fwd) = fp { | ||
105 | idx += fwd as usize; | ||
106 | // append `A`'s forward_parent `B` | ||
107 | fp = match mem::replace(&mut events[idx], Event::tombstone()) { | ||
108 | Event::Start { kind, forward_parent } => { | ||
109 | if kind != TOMBSTONE { | ||
110 | forward_parents.push(kind); | ||
111 | } | ||
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 | sink.start_node(kind); | ||
121 | } | ||
122 | } | ||
123 | Event::Finish => sink.finish_node(), | ||
124 | Event::Token { kind, n_raw_tokens } => { | ||
125 | sink.token(kind, n_raw_tokens); | ||
126 | } | ||
127 | Event::Error { msg } => sink.error(msg), | ||
128 | } | ||
129 | } | ||
130 | } | ||