diff options
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander/matcher.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/matcher.rs | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs new file mode 100644 index 000000000..100a3b0e0 --- /dev/null +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs | |||
@@ -0,0 +1,215 @@ | |||
1 | use crate::{ | ||
2 | mbe_expander::{Binding, Bindings, Fragment}, | ||
3 | tt_cursor::TtCursor, | ||
4 | ExpandError, | ||
5 | }; | ||
6 | |||
7 | use ra_parser::FragmentKind::*; | ||
8 | use ra_syntax::SmolStr; | ||
9 | |||
10 | impl Bindings { | ||
11 | fn push_optional(&mut self, name: &SmolStr) { | ||
12 | // FIXME: Do we have a better way to represent an empty token ? | ||
13 | // Insert an empty subtree for empty token | ||
14 | let tt = tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] }.into(); | ||
15 | self.inner.insert(name.clone(), Binding::Fragment(Fragment::Tokens(tt))); | ||
16 | } | ||
17 | |||
18 | fn push_empty(&mut self, name: &SmolStr) { | ||
19 | self.inner.insert(name.clone(), Binding::Empty); | ||
20 | } | ||
21 | |||
22 | fn push_nested(&mut self, idx: usize, nested: Bindings) -> Result<(), ExpandError> { | ||
23 | for (key, value) in nested.inner { | ||
24 | if !self.inner.contains_key(&key) { | ||
25 | self.inner.insert(key.clone(), Binding::Nested(Vec::new())); | ||
26 | } | ||
27 | match self.inner.get_mut(&key) { | ||
28 | Some(Binding::Nested(it)) => { | ||
29 | // insert empty nested bindings before this one | ||
30 | while it.len() < idx { | ||
31 | it.push(Binding::Nested(vec![])); | ||
32 | } | ||
33 | it.push(value); | ||
34 | } | ||
35 | _ => { | ||
36 | return Err(ExpandError::BindingError(format!( | ||
37 | "could not find binding `{}`", | ||
38 | key | ||
39 | ))); | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | Ok(()) | ||
44 | } | ||
45 | |||
46 | fn merge(&mut self, nested: Bindings) { | ||
47 | self.inner.extend(nested.inner); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub(super) fn match_lhs( | ||
52 | pattern: &crate::Subtree, | ||
53 | input: &mut TtCursor, | ||
54 | ) -> Result<Bindings, ExpandError> { | ||
55 | let mut res = Bindings::default(); | ||
56 | for pat in pattern.token_trees.iter() { | ||
57 | match pat { | ||
58 | crate::TokenTree::Leaf(leaf) => match leaf { | ||
59 | crate::Leaf::Var(crate::Var { text, kind }) => { | ||
60 | let kind = kind.as_ref().ok_or(ExpandError::UnexpectedToken)?; | ||
61 | match match_meta_var(kind.as_str(), input)? { | ||
62 | Some(fragment) => { | ||
63 | res.inner.insert(text.clone(), Binding::Fragment(fragment)); | ||
64 | } | ||
65 | None => res.push_optional(text), | ||
66 | } | ||
67 | } | ||
68 | crate::Leaf::Punct(punct) => { | ||
69 | if !input.eat_punct().map(|p| p.char == punct.char).unwrap_or(false) { | ||
70 | return Err(ExpandError::UnexpectedToken); | ||
71 | } | ||
72 | } | ||
73 | crate::Leaf::Ident(ident) => { | ||
74 | if input.eat_ident().map(|i| &i.text) != Some(&ident.text) { | ||
75 | return Err(ExpandError::UnexpectedToken); | ||
76 | } | ||
77 | } | ||
78 | crate::Leaf::Literal(literal) => { | ||
79 | if input.eat_literal().map(|i| &i.text) != Some(&literal.text) { | ||
80 | return Err(ExpandError::UnexpectedToken); | ||
81 | } | ||
82 | } | ||
83 | }, | ||
84 | crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => { | ||
85 | // Dirty hack to make macro-expansion terminate. | ||
86 | // This should be replaced by a propper macro-by-example implementation | ||
87 | let mut limit = 65536; | ||
88 | let mut counter = 0; | ||
89 | |||
90 | let mut memento = input.save(); | ||
91 | |||
92 | loop { | ||
93 | match match_lhs(subtree, input) { | ||
94 | Ok(nested) => { | ||
95 | limit -= 1; | ||
96 | if limit == 0 { | ||
97 | log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator); | ||
98 | break; | ||
99 | } | ||
100 | |||
101 | memento = input.save(); | ||
102 | res.push_nested(counter, nested)?; | ||
103 | counter += 1; | ||
104 | if counter == 1 { | ||
105 | if let crate::RepeatKind::ZeroOrOne = kind { | ||
106 | break; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | if let Some(separator) = separator { | ||
111 | if !input | ||
112 | .eat_seperator() | ||
113 | .map(|sep| sep == *separator) | ||
114 | .unwrap_or(false) | ||
115 | { | ||
116 | input.rollback(memento); | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | Err(_) => { | ||
122 | input.rollback(memento); | ||
123 | break; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | match kind { | ||
129 | crate::RepeatKind::OneOrMore if counter == 0 => { | ||
130 | return Err(ExpandError::UnexpectedToken); | ||
131 | } | ||
132 | _ if counter == 0 => { | ||
133 | // Collect all empty variables in subtrees | ||
134 | collect_vars(subtree).iter().for_each(|s| res.push_empty(s)); | ||
135 | } | ||
136 | _ => {} | ||
137 | } | ||
138 | } | ||
139 | crate::TokenTree::Subtree(subtree) => { | ||
140 | let input_subtree = | ||
141 | input.eat_subtree().map_err(|_| ExpandError::UnexpectedToken)?; | ||
142 | if subtree.delimiter != input_subtree.delimiter { | ||
143 | return Err(ExpandError::UnexpectedToken); | ||
144 | } | ||
145 | |||
146 | let mut input = TtCursor::new(input_subtree); | ||
147 | let bindings = match_lhs(&subtree, &mut input)?; | ||
148 | if !input.is_eof() { | ||
149 | return Err(ExpandError::UnexpectedToken); | ||
150 | } | ||
151 | |||
152 | res.merge(bindings); | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | Ok(res) | ||
157 | } | ||
158 | |||
159 | fn match_meta_var(kind: &str, input: &mut TtCursor) -> Result<Option<Fragment>, ExpandError> { | ||
160 | let fragment = match kind { | ||
161 | "path" => Path, | ||
162 | "expr" => Expr, | ||
163 | "ty" => Type, | ||
164 | "pat" => Pattern, | ||
165 | "stmt" => Statement, | ||
166 | "block" => Block, | ||
167 | "meta" => MetaItem, | ||
168 | "item" => Item, | ||
169 | _ => { | ||
170 | let tt = match kind { | ||
171 | "ident" => { | ||
172 | let ident = input.eat_ident().ok_or(ExpandError::UnexpectedToken)?.clone(); | ||
173 | tt::Leaf::from(ident).into() | ||
174 | } | ||
175 | "tt" => input.eat().ok_or(ExpandError::UnexpectedToken)?.clone(), | ||
176 | "lifetime" => input.eat_lifetime().ok_or(ExpandError::UnexpectedToken)?.clone(), | ||
177 | "literal" => { | ||
178 | let literal = input.eat_literal().ok_or(ExpandError::UnexpectedToken)?.clone(); | ||
179 | tt::Leaf::from(literal).into() | ||
180 | } | ||
181 | // `vis` is optional | ||
182 | "vis" => match input.try_eat_vis() { | ||
183 | Some(vis) => vis, | ||
184 | None => return Ok(None), | ||
185 | }, | ||
186 | _ => return Err(ExpandError::UnexpectedToken), | ||
187 | }; | ||
188 | return Ok(Some(Fragment::Tokens(tt))); | ||
189 | } | ||
190 | }; | ||
191 | let tt = input.eat_fragment(fragment).ok_or(ExpandError::UnexpectedToken)?; | ||
192 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; | ||
193 | Ok(Some(fragment)) | ||
194 | } | ||
195 | |||
196 | fn collect_vars(subtree: &crate::Subtree) -> Vec<SmolStr> { | ||
197 | let mut res = Vec::new(); | ||
198 | |||
199 | for tkn in subtree.token_trees.iter() { | ||
200 | match tkn { | ||
201 | crate::TokenTree::Leaf(crate::Leaf::Var(crate::Var { text, .. })) => { | ||
202 | res.push(text.clone()); | ||
203 | } | ||
204 | crate::TokenTree::Subtree(subtree) => { | ||
205 | res.extend(collect_vars(subtree)); | ||
206 | } | ||
207 | crate::TokenTree::Repeat(crate::Repeat { subtree, .. }) => { | ||
208 | res.extend(collect_vars(subtree)); | ||
209 | } | ||
210 | _ => {} | ||
211 | } | ||
212 | } | ||
213 | |||
214 | res | ||
215 | } | ||