1use std::{cell::{Cell, RefCell}, mem::{forget, take}};
2
3use rowan::{GreenNode, GreenNodeBuilder, SyntaxKind};
4
5#[derive(Debug)]
6pub struct QuietGuard<'b, 'a>(&'b ParseState<'a>);
7
8impl<'b, 'a> std::ops::Deref for QuietGuard<'b, 'a> {
9 type Target = &'b ParseState<'a>;
10
11 fn deref(&self) -> &Self::Target {
12 &self.0
13 }
14}
15
16impl<'b, 'a> Drop for QuietGuard<'b, 'a> {
17 fn drop(&mut self) {
18 self.0.quiet.update(|n| n+1);
19 }
20}
21
22#[derive(Debug)]
23pub struct RuleGuard<'b, 'a> {
24 state: &'b ParseState<'a>,
25 len: usize,
26 quiet: u32,
27 kind: SyntaxKind,
28}
29
30impl<'b, 'a> RuleGuard<'b, 'a> {
31 fn end_set(&self) {
32 self.state.quiet.set(self.quiet);
33 }
34
35 pub fn accept_token(self, text: &'a str) {
36 self.end_set();
37 self.state.action(Action::Token(self.kind, text));
38 forget(self);
39 }
40
41 pub fn accept_none(self) {
42 self.end_set();
43 forget(self);
44 }
45
46 pub fn accept(self) {
47 self.end_set();
48 self.state.action(Action::Finish);
49 forget(self);
50 }
51}
52
53impl<'b, 'a> Drop for RuleGuard<'b, 'a> {
54 fn drop(&mut self) {
55 let RuleGuard { state, len, quiet, kind: _ } = *self;
56 state.actions.borrow_mut().truncate(len);
57 state.quiet.set(quiet);
58 }
59}
60
61#[derive(Debug, Clone)]
62pub enum Action<'a> {
63 Token(SyntaxKind, &'a str),
64 Start(SyntaxKind),
65 Finish,
66}
67
68#[derive(Debug, Default)]
69pub struct ParseState<'a> {
70 actions: RefCell<Vec<Action<'a>>>,
71 quiet: Cell<u32>,
72 builder: GreenNodeBuilder<'static>,
73}
74
75impl<'a> ParseState<'a> {
76 pub fn clear(&mut self) {
77 let mut actions = take(&mut self.actions);
78 actions.get_mut().clear();
79 *self = Self {
80 actions,
81 quiet: 0.into(),
82 ..Default::default()
83 }
84 }
85
86 pub fn action(&self, action: Action<'a>) {
87 if self.quiet.get() == 0 {
88 self.actions.borrow_mut().push(action);
89 }
90 }
91
92 pub fn quiet(&self) -> QuietGuard<'_, 'a> {
93 QuietGuard(self)
94 }
95
96 pub fn guard(&self, kind: impl Into<SyntaxKind>) -> RuleGuard<'_, 'a> {
97 let kind = kind.into();
98 let guard = self.guard_token(kind);
99 self.action(Action::Start(kind));
100 guard
101 }
102
103 pub fn guard_token(&self, kind: impl Into<SyntaxKind>) -> RuleGuard<'_, 'a> {
104 let len = self.actions.borrow().len();
105 RuleGuard { state: self, len, quiet: self.quiet.get(), kind: kind.into() }
106 }
107
108 pub fn guard_none(&self) -> RuleGuard<'_, 'a> {
109 let kind = SyntaxKind(0);
110 self.guard_token(kind)
111 }
112
113 pub fn finish(&mut self) -> GreenNode {
114 for action in self.actions.get_mut().drain(..) {
115 match action {
116 Action::Token(kind, text) => {
117 if !text.is_empty() {
118 self.builder.token(kind, text)
119 }
120 }
121 Action::Start(kind) => self.builder.start_node(kind),
122 Action::Finish => self.builder.finish_node(),
123 }
124 }
125 take(&mut self.builder).finish()
126 }
127}
128
129pub mod support {
130 use rowan::{Language, NodeOrToken, SyntaxNode, SyntaxToken};
131
132 pub fn tokens<L>(node: &SyntaxNode<L>, kind: L::Kind) -> impl Iterator<
133 Item = SyntaxToken<L>
134 >
135 where L: Language
136 {
137 node.children_with_tokens()
138 .filter_map(NodeOrToken::into_token)
139 .filter(move |token| token.kind() == kind)
140 }
141}
142pub use support::tokens;
143
144#[macro_export]
169macro_rules! match_options {
170 (@arms($expr:tt)
171 $($level1:ident).+ $(as $name1:tt)?
172 $({$(
173 $($level2:ident).+ $(as $name2:tt)?
174 $({$(
175 $($level3:ident).+ $(as $name3:tt)?
176 $({$(
177 $($level4:ident).+ $(as $name4:tt)?
178 ),+ $(,)?})?
179 ),+ $(,)?})?
180 ),+ $(,)?})?
181 $(if $guard:expr)?
182 => $code:expr
183 $(, $($t:tt)*)?
184 ) => {
185 if let ::core::option::Option::Some(__level1) = $expr $(.$level1())+
186 $($(
187 && let ::core::option::Option::Some(__level2) = __level1 $(.$level2())+
188 $($(
189 && let ::core::option::Option::Some(__level3) = __level2 $(.$level3())+
190 $($(
191 && let ::core::option::Option::Some($crate::match_options! { @last$(($name4))? $($level4)+ }) = __level3 $(.$level4())+
192 )+)?
193 && let $crate::match_options! { @last$(($name3))? $($level3)+ } = __level3
194 )+)?
195 && let $crate::match_options! { @last$(($name2))? $($level2)+ } = __level2
196 )+)?
197 && let $crate::match_options! { @last$(($name1))? $($level1)+ } = __level1
198 $(&& $guard)?
199 {
200 $code
201 } $(else {
202 $crate::match_options! {
203 @arms($expr) $($t)*
204 }
205 })?
206 };
207 (@last $i1:ident) => { $i1 };
208 (@last $i1:ident $i2:ident) => { $i2 };
209 (@last $i1:ident $i2:ident $i3:ident) => { $i3 };
210 (@last $i1:ident $i2:ident $i3:ident $i4:ident) => { $i4 };
211 (@last($renamed:pat) $($_:tt)*) => { $renamed };
212 (@arms($expr:tt)) => {};
213 (@arms($expr:tt) _ => $e:expr $(,)?) => {$e};
214
215 (match $expr:tt {$($t:tt)+}) => {{
216 #[allow(irrefutable_let_patterns)]
217 {
218 $crate::match_options! {
219 @arms($expr) $($t)*
220 }
221 }
222 }};
223
224 ($i:ident $expr:tt) => { $i $expr };
226 (match $expr:tt {}) => { match $expr };
227}
228
229
230#[macro_export]
242macro_rules! match_ast {
243 (@arms($node:tt)
244 $( $path:ident )::+ ($it:pat)
245 $(if $guard:expr)?
246 => $res:expr
247 $(, $($t:tt)*)?
248 ) => {
249 if let ::core::option::Option::Some($it) = $($path::)+cast($node.clone())
250 $(&& $guard)?
251 {
252 $res
253 } else {
254 $crate::match_ast! {
255 @arms($node) $($($t)*)?
256 }
257 }
258 };
259
260 (@arms($node:expr) _ => $catch_all:expr $(,)?) => { $catch_all };
261
262 (match $node:tt {$($t:tt)+}) => {{
263 $crate::match_ast! {
264 @arms($node) $($t)*
265 }
266 }};
267
268 ($i:ident $expr:tt) => { $i $expr };
270 (match $expr:tt {}) => { match $expr };
271}
272
273#[test]
274#[allow(unused)]
275fn feature() {
276 struct Foo;
277 struct Bar;
278 struct Baz;
279 impl Foo { fn bar(&self) -> Bar { Bar } }
280 impl Bar { fn baz(&self) -> Option<Baz> { Some(Baz) } }
281 impl Baz { fn foo(&self) -> Option<Foo> { Some(Foo) } }
282 impl Baz { fn bar(&self) -> Option<Bar> { Some(Bar) } }
283 match_options!{match Foo {
284 bar.baz as m { foo as _, bar as _ } => {}
285 }};
286}
287
288#[cfg(test)]
289fn _foo<L: rowan::Language, Ty: rowan::ast::AstNode<Language = L>>(node: &rowan::SyntaxNode<L>) {
290 let x = true;
291 match_ast! {match node {
292 Ty(ty) if x => { ty.syntax().kind(); },
293 Ty(ty) => _ = ty.syntax().kind(),
294 _ => panic!()
295 }}
296}