diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-08 15:18:57 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-08 15:18:57 +0100 |
commit | ac6ab0758731d0555fbf1b1a918abd3e12c8169d (patch) | |
tree | 05f568cbd925dbbd578f9414fd9e4ea3634b68e4 /crates/ra_mbe/src | |
parent | 1ca7a744eb512e6b900988cba871dcd3d90d447f (diff) | |
parent | 8ed710457875e6f580a0ddf6ab29c6b10d389a41 (diff) |
Merge #1105
1105: [WIP] Implement ra_mbe meta variables support r=matklad a=edwin0cheng
This PR implements the following meta variable support in `ra_mba` crate (issue #720):
- [x] `path`
- [ ] `expr`
- [ ] `ty`
- [ ] `pat`
- [ ] `stmt`
- [ ] `block`
- [ ] `meta`
- [ ] `item`
*Implementation Details*
In the macro expanding lhs phase, if we see a meta variable type, we try to create a `tt:TokenTree` from the remaining input. And then we use a special set of `ra_parser` to parse it to `SyntaxNode`.
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_mbe/src')
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 55 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 5 | ||||
-rw-r--r-- | crates/ra_mbe/src/subtree_parser.rs | 61 | ||||
-rw-r--r-- | crates/ra_mbe/src/subtree_source.rs | 521 | ||||
-rw-r--r-- | crates/ra_mbe/src/syntax_bridge.rs | 254 | ||||
-rw-r--r-- | crates/ra_mbe/src/tt_cursor.rs | 6 |
6 files changed, 671 insertions, 231 deletions
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 4203929d4..a21ea4dbc 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -15,10 +15,13 @@ macro_rules! impl_froms { | |||
15 | } | 15 | } |
16 | } | 16 | } |
17 | 17 | ||
18 | mod tt_cursor; | 18 | // mod tt_cursor; |
19 | mod mbe_parser; | 19 | mod mbe_parser; |
20 | mod mbe_expander; | 20 | mod mbe_expander; |
21 | mod syntax_bridge; | 21 | mod syntax_bridge; |
22 | mod tt_cursor; | ||
23 | mod subtree_source; | ||
24 | mod subtree_parser; | ||
22 | 25 | ||
23 | use ra_syntax::SmolStr; | 26 | use ra_syntax::SmolStr; |
24 | 27 | ||
@@ -379,4 +382,54 @@ SOURCE_FILE@[0; 40) | |||
379 | // [let] [s] [=] ["rust1"] [;] | 382 | // [let] [s] [=] ["rust1"] [;] |
380 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); | 383 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); |
381 | } | 384 | } |
385 | |||
386 | #[test] | ||
387 | fn test_two_idents() { | ||
388 | let rules = create_rules( | ||
389 | r#" | ||
390 | macro_rules! foo { | ||
391 | ($ i:ident, $ j:ident) => { | ||
392 | fn foo() { let a = $ i; let b = $j; } | ||
393 | } | ||
394 | } | ||
395 | "#, | ||
396 | ); | ||
397 | assert_expansion(&rules, "foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
398 | } | ||
399 | |||
400 | // The following tests are port from intellij-rust directly | ||
401 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
402 | |||
403 | #[test] | ||
404 | fn test_path() { | ||
405 | let rules = create_rules( | ||
406 | r#" | ||
407 | macro_rules! foo { | ||
408 | ($ i:path) => { | ||
409 | fn foo() { let a = $ i; } | ||
410 | } | ||
411 | } | ||
412 | "#, | ||
413 | ); | ||
414 | assert_expansion(&rules, "foo! { foo }", "fn foo () {let a = foo ;}"); | ||
415 | assert_expansion( | ||
416 | &rules, | ||
417 | "foo! { bar::<u8>::baz::<u8> }", | ||
418 | "fn foo () {let a = bar ::< u8 > ::baz ::< u8 > ;}", | ||
419 | ); | ||
420 | } | ||
421 | |||
422 | #[test] | ||
423 | fn test_two_paths() { | ||
424 | let rules = create_rules( | ||
425 | r#" | ||
426 | macro_rules! foo { | ||
427 | ($ i:path, $ j:path) => { | ||
428 | fn foo() { let a = $ i; let b = $j; } | ||
429 | } | ||
430 | } | ||
431 | "#, | ||
432 | ); | ||
433 | assert_expansion(&rules, "foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
434 | } | ||
382 | } | 435 | } |
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 2dafd68f6..ce41d7225 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -139,6 +139,11 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
139 | Binding::Simple(tt::Leaf::from(ident).into()), | 139 | Binding::Simple(tt::Leaf::from(ident).into()), |
140 | ); | 140 | ); |
141 | } | 141 | } |
142 | "path" => { | ||
143 | let path = | ||
144 | input.eat_path().ok_or(ExpandError::UnexpectedToken)?.clone(); | ||
145 | res.inner.insert(text.clone(), Binding::Simple(path.into())); | ||
146 | } | ||
142 | _ => return Err(ExpandError::UnexpectedToken), | 147 | _ => return Err(ExpandError::UnexpectedToken), |
143 | } | 148 | } |
144 | } | 149 | } |
diff --git a/crates/ra_mbe/src/subtree_parser.rs b/crates/ra_mbe/src/subtree_parser.rs new file mode 100644 index 000000000..ce39a40bb --- /dev/null +++ b/crates/ra_mbe/src/subtree_parser.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | use crate::subtree_source::SubtreeTokenSource; | ||
2 | |||
3 | use ra_parser::{TokenSource, TreeSink}; | ||
4 | use ra_syntax::{SyntaxKind}; | ||
5 | |||
6 | struct OffsetTokenSink { | ||
7 | token_pos: usize, | ||
8 | } | ||
9 | |||
10 | impl TreeSink for OffsetTokenSink { | ||
11 | fn token(&mut self, _kind: SyntaxKind, n_tokens: u8) { | ||
12 | self.token_pos += n_tokens as usize; | ||
13 | } | ||
14 | fn start_node(&mut self, _kind: SyntaxKind) {} | ||
15 | fn finish_node(&mut self) {} | ||
16 | fn error(&mut self, _error: ra_parser::ParseError) {} | ||
17 | } | ||
18 | |||
19 | pub(crate) struct Parser<'a> { | ||
20 | subtree: &'a tt::Subtree, | ||
21 | cur_pos: &'a mut usize, | ||
22 | } | ||
23 | |||
24 | impl<'a> Parser<'a> { | ||
25 | pub fn new(cur_pos: &'a mut usize, subtree: &'a tt::Subtree) -> Parser<'a> { | ||
26 | Parser { cur_pos, subtree } | ||
27 | } | ||
28 | |||
29 | pub fn parse_path(self) -> Option<tt::TokenTree> { | ||
30 | self.parse(ra_parser::parse_path) | ||
31 | } | ||
32 | |||
33 | fn parse<F>(self, f: F) -> Option<tt::TokenTree> | ||
34 | where | ||
35 | F: FnOnce(&dyn TokenSource, &mut dyn TreeSink), | ||
36 | { | ||
37 | let mut src = SubtreeTokenSource::new(self.subtree); | ||
38 | src.start_from_nth(*self.cur_pos); | ||
39 | let mut sink = OffsetTokenSink { token_pos: 0 }; | ||
40 | |||
41 | f(&src, &mut sink); | ||
42 | |||
43 | self.finish(sink.token_pos, &mut src) | ||
44 | } | ||
45 | |||
46 | fn finish(self, parsed_token: usize, src: &mut SubtreeTokenSource) -> Option<tt::TokenTree> { | ||
47 | let res = src.bump_n(parsed_token); | ||
48 | *self.cur_pos += res.len(); | ||
49 | |||
50 | let res: Vec<_> = res.into_iter().cloned().collect(); | ||
51 | |||
52 | match res.len() { | ||
53 | 0 => None, | ||
54 | 1 => Some(res[0].clone()), | ||
55 | _ => Some(tt::TokenTree::Subtree(tt::Subtree { | ||
56 | delimiter: tt::Delimiter::None, | ||
57 | token_trees: res, | ||
58 | })), | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs new file mode 100644 index 000000000..4b37c2bda --- /dev/null +++ b/crates/ra_mbe/src/subtree_source.rs | |||
@@ -0,0 +1,521 @@ | |||
1 | use ra_parser::{TokenSource}; | ||
2 | use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*}; | ||
3 | use std::cell::{RefCell}; | ||
4 | |||
5 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
6 | struct TtToken { | ||
7 | pub kind: SyntaxKind, | ||
8 | pub is_joint_to_next: bool, | ||
9 | pub text: SmolStr, | ||
10 | pub n_tokens: usize, | ||
11 | } | ||
12 | |||
13 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
14 | enum WalkCursor { | ||
15 | DelimiterBegin(Option<TtToken>), | ||
16 | Token(usize, Option<TtToken>), | ||
17 | DelimiterEnd(Option<TtToken>), | ||
18 | Eof, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug)] | ||
22 | struct SubTreeWalker<'a> { | ||
23 | pos: usize, | ||
24 | stack: Vec<(&'a tt::Subtree, Option<usize>)>, | ||
25 | cursor: WalkCursor, | ||
26 | last_steps: Vec<usize>, | ||
27 | subtree: &'a tt::Subtree, | ||
28 | } | ||
29 | |||
30 | impl<'a> SubTreeWalker<'a> { | ||
31 | fn new(subtree: &tt::Subtree) -> SubTreeWalker { | ||
32 | let mut res = SubTreeWalker { | ||
33 | pos: 0, | ||
34 | stack: vec![], | ||
35 | cursor: WalkCursor::Eof, | ||
36 | last_steps: vec![], | ||
37 | subtree, | ||
38 | }; | ||
39 | |||
40 | res.reset(); | ||
41 | res | ||
42 | } | ||
43 | |||
44 | fn is_eof(&self) -> bool { | ||
45 | self.cursor == WalkCursor::Eof | ||
46 | } | ||
47 | |||
48 | fn reset(&mut self) { | ||
49 | self.pos = 0; | ||
50 | self.stack = vec![(self.subtree, None)]; | ||
51 | self.cursor = WalkCursor::DelimiterBegin(convert_delim(self.subtree.delimiter, false)); | ||
52 | self.last_steps = vec![]; | ||
53 | |||
54 | while self.is_empty_delimiter() { | ||
55 | self.forward_unchecked(); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | // This funciton will fast forward the cursor, | ||
60 | // Such that backward will stop at `start_pos` point | ||
61 | fn start_from_nth(&mut self, start_pos: usize) { | ||
62 | self.reset(); | ||
63 | self.pos = start_pos; | ||
64 | self.cursor = self.walk_token(start_pos, 0, false); | ||
65 | |||
66 | while self.is_empty_delimiter() { | ||
67 | self.forward_unchecked(); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | fn current(&self) -> Option<&TtToken> { | ||
72 | match &self.cursor { | ||
73 | WalkCursor::DelimiterBegin(t) => t.as_ref(), | ||
74 | WalkCursor::Token(_, t) => t.as_ref(), | ||
75 | WalkCursor::DelimiterEnd(t) => t.as_ref(), | ||
76 | WalkCursor::Eof => None, | ||
77 | } | ||
78 | } | ||
79 | |||
80 | fn is_empty_delimiter(&self) -> bool { | ||
81 | match &self.cursor { | ||
82 | WalkCursor::DelimiterBegin(None) => true, | ||
83 | WalkCursor::DelimiterEnd(None) => true, | ||
84 | _ => false, | ||
85 | } | ||
86 | } | ||
87 | |||
88 | /// Move cursor backward by 1 step with empty checking | ||
89 | fn backward(&mut self) { | ||
90 | if self.last_steps.is_empty() { | ||
91 | return; | ||
92 | } | ||
93 | self.pos -= 1; | ||
94 | loop { | ||
95 | self.backward_unchecked(); | ||
96 | // Skip Empty delimiter | ||
97 | if self.last_steps.is_empty() || !self.is_empty_delimiter() { | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | // Move forward if it is empty delimiter | ||
103 | if self.last_steps.is_empty() { | ||
104 | while self.is_empty_delimiter() { | ||
105 | self.forward_unchecked(); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /// Move cursor backward by 1 step without empty check | ||
111 | /// | ||
112 | /// Depends on the current state of cursor: | ||
113 | /// | ||
114 | /// * Delimiter Begin => Pop the stack, goto last walking token (`walk_token`) | ||
115 | /// * Token => Goto prev token (`walk_token`) | ||
116 | /// * Delimiter End => Goto the last child token (`walk_token`) | ||
117 | /// * Eof => push the root subtree, and set it as Delimiter End | ||
118 | fn backward_unchecked(&mut self) { | ||
119 | if self.last_steps.is_empty() { | ||
120 | return; | ||
121 | } | ||
122 | |||
123 | let last_step = self.last_steps.pop().unwrap(); | ||
124 | let do_walk_token = match self.cursor { | ||
125 | WalkCursor::DelimiterBegin(_) => None, | ||
126 | WalkCursor::Token(u, _) => Some(u), | ||
127 | WalkCursor::DelimiterEnd(_) => { | ||
128 | let (top, _) = self.stack.last().unwrap(); | ||
129 | Some(top.token_trees.len()) | ||
130 | } | ||
131 | WalkCursor::Eof => None, | ||
132 | }; | ||
133 | |||
134 | self.cursor = match do_walk_token { | ||
135 | Some(u) => self.walk_token(u, last_step, true), | ||
136 | None => match self.cursor { | ||
137 | WalkCursor::Eof => { | ||
138 | self.stack.push((self.subtree, None)); | ||
139 | WalkCursor::DelimiterEnd(convert_delim( | ||
140 | self.stack.last().unwrap().0.delimiter, | ||
141 | true, | ||
142 | )) | ||
143 | } | ||
144 | _ => { | ||
145 | let (_, last_top_cursor) = self.stack.pop().unwrap(); | ||
146 | assert!(!self.stack.is_empty()); | ||
147 | |||
148 | self.walk_token(last_top_cursor.unwrap(), last_step, true) | ||
149 | } | ||
150 | }, | ||
151 | }; | ||
152 | } | ||
153 | |||
154 | /// Move cursor forward by 1 step with empty checking | ||
155 | fn forward(&mut self) { | ||
156 | if self.is_eof() { | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | self.pos += 1; | ||
161 | loop { | ||
162 | self.forward_unchecked(); | ||
163 | if !self.is_empty_delimiter() { | ||
164 | break; | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /// Move cursor forward by 1 step without empty checking | ||
170 | /// | ||
171 | /// Depends on the current state of cursor: | ||
172 | /// | ||
173 | /// * Delimiter Begin => Goto the first child token (`walk_token`) | ||
174 | /// * Token => Goto next token (`walk_token`) | ||
175 | /// * Delimiter End => Pop the stack, goto last walking token (`walk_token`) | ||
176 | /// | ||
177 | fn forward_unchecked(&mut self) { | ||
178 | if self.is_eof() { | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | let step = self.current().map(|x| x.n_tokens).unwrap_or(1); | ||
183 | self.last_steps.push(step); | ||
184 | |||
185 | let do_walk_token = match self.cursor { | ||
186 | WalkCursor::DelimiterBegin(_) => Some((0, 0)), | ||
187 | WalkCursor::Token(u, _) => Some((u, step)), | ||
188 | WalkCursor::DelimiterEnd(_) => None, | ||
189 | _ => unreachable!(), | ||
190 | }; | ||
191 | |||
192 | self.cursor = match do_walk_token { | ||
193 | Some((u, step)) => self.walk_token(u, step, false), | ||
194 | None => { | ||
195 | let (_, last_top_idx) = self.stack.pop().unwrap(); | ||
196 | match self.stack.last() { | ||
197 | Some(_) => self.walk_token(last_top_idx.unwrap(), 1, false), | ||
198 | None => WalkCursor::Eof, | ||
199 | } | ||
200 | } | ||
201 | }; | ||
202 | } | ||
203 | |||
204 | /// Traversal child token | ||
205 | /// Depends on the new position, it returns: | ||
206 | /// | ||
207 | /// * new position < 0 => DelimiterBegin | ||
208 | /// * new position > token_tree.len() => DelimiterEnd | ||
209 | /// * if new position is a subtree, depends on traversal direction: | ||
210 | /// ** backward => DelimiterEnd | ||
211 | /// ** forward => DelimiterBegin | ||
212 | /// * if new psoition is a leaf, return walk_leaf() | ||
213 | fn walk_token(&mut self, pos: usize, offset: usize, backward: bool) -> WalkCursor { | ||
214 | let (top, _) = self.stack.last().unwrap(); | ||
215 | |||
216 | if backward && pos < offset { | ||
217 | return WalkCursor::DelimiterBegin(convert_delim( | ||
218 | self.stack.last().unwrap().0.delimiter, | ||
219 | false, | ||
220 | )); | ||
221 | } | ||
222 | |||
223 | if !backward && pos + offset >= top.token_trees.len() { | ||
224 | return WalkCursor::DelimiterEnd(convert_delim( | ||
225 | self.stack.last().unwrap().0.delimiter, | ||
226 | true, | ||
227 | )); | ||
228 | } | ||
229 | |||
230 | let pos = if backward { pos - offset } else { pos + offset }; | ||
231 | |||
232 | match &top.token_trees[pos] { | ||
233 | tt::TokenTree::Subtree(subtree) => { | ||
234 | self.stack.push((subtree, Some(pos))); | ||
235 | let delim = convert_delim(self.stack.last().unwrap().0.delimiter, backward); | ||
236 | if backward { | ||
237 | WalkCursor::DelimiterEnd(delim) | ||
238 | } else { | ||
239 | WalkCursor::DelimiterBegin(delim) | ||
240 | } | ||
241 | } | ||
242 | tt::TokenTree::Leaf(leaf) => WalkCursor::Token(pos, Some(self.walk_leaf(leaf, pos))), | ||
243 | } | ||
244 | } | ||
245 | |||
246 | fn walk_leaf(&mut self, leaf: &tt::Leaf, pos: usize) -> TtToken { | ||
247 | match leaf { | ||
248 | tt::Leaf::Literal(l) => convert_literal(l), | ||
249 | tt::Leaf::Ident(ident) => convert_ident(ident), | ||
250 | tt::Leaf::Punct(punct) => { | ||
251 | let (top, _) = self.stack.last().unwrap(); | ||
252 | convert_punct(punct, top, pos) | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | pub(crate) trait Querier { | ||
259 | fn token(&self, uidx: usize) -> (SyntaxKind, SmolStr); | ||
260 | } | ||
261 | |||
262 | // A wrapper class for ref cell | ||
263 | #[derive(Debug)] | ||
264 | pub(crate) struct WalkerOwner<'a> { | ||
265 | walker: RefCell<SubTreeWalker<'a>>, | ||
266 | offset: usize, | ||
267 | } | ||
268 | |||
269 | impl<'a> WalkerOwner<'a> { | ||
270 | fn new(subtree: &'a tt::Subtree) -> Self { | ||
271 | WalkerOwner { walker: RefCell::new(SubTreeWalker::new(subtree)), offset: 0 } | ||
272 | } | ||
273 | |||
274 | fn get<'b>(&self, pos: usize) -> Option<TtToken> { | ||
275 | self.set_walker_pos(pos); | ||
276 | let walker = self.walker.borrow(); | ||
277 | walker.current().cloned() | ||
278 | } | ||
279 | |||
280 | fn start_from_nth(&mut self, pos: usize) { | ||
281 | self.offset = pos; | ||
282 | self.walker.borrow_mut().start_from_nth(pos); | ||
283 | } | ||
284 | |||
285 | fn set_walker_pos(&self, mut pos: usize) { | ||
286 | pos += self.offset; | ||
287 | let mut walker = self.walker.borrow_mut(); | ||
288 | while pos > walker.pos && !walker.is_eof() { | ||
289 | walker.forward(); | ||
290 | } | ||
291 | while pos < walker.pos { | ||
292 | walker.backward(); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | fn collect_token_trees(&mut self, n: usize) -> Vec<&tt::TokenTree> { | ||
297 | self.start_from_nth(self.offset); | ||
298 | |||
299 | let mut res = vec![]; | ||
300 | let mut walker = self.walker.borrow_mut(); | ||
301 | |||
302 | while walker.pos - self.offset < n { | ||
303 | if let WalkCursor::Token(u, tt) = &walker.cursor { | ||
304 | if walker.stack.len() == 1 { | ||
305 | // We only collect the topmost child | ||
306 | res.push(&walker.stack[0].0.token_trees[*u]); | ||
307 | if let Some(tt) = tt { | ||
308 | for i in 0..tt.n_tokens - 1 { | ||
309 | res.push(&walker.stack[0].0.token_trees[u + i]); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | |||
315 | walker.forward(); | ||
316 | } | ||
317 | |||
318 | res | ||
319 | } | ||
320 | } | ||
321 | |||
322 | impl<'a> Querier for WalkerOwner<'a> { | ||
323 | fn token(&self, uidx: usize) -> (SyntaxKind, SmolStr) { | ||
324 | let tkn = self.get(uidx).unwrap(); | ||
325 | (tkn.kind, tkn.text) | ||
326 | } | ||
327 | } | ||
328 | |||
329 | pub(crate) struct SubtreeTokenSource<'a> { | ||
330 | walker: WalkerOwner<'a>, | ||
331 | } | ||
332 | |||
333 | impl<'a> SubtreeTokenSource<'a> { | ||
334 | pub fn new(subtree: &tt::Subtree) -> SubtreeTokenSource { | ||
335 | SubtreeTokenSource { walker: WalkerOwner::new(subtree) } | ||
336 | } | ||
337 | |||
338 | pub fn start_from_nth(&mut self, n: usize) { | ||
339 | self.walker.start_from_nth(n); | ||
340 | } | ||
341 | |||
342 | pub fn querier<'b>(&'a self) -> &'b WalkerOwner<'a> | ||
343 | where | ||
344 | 'a: 'b, | ||
345 | { | ||
346 | &self.walker | ||
347 | } | ||
348 | |||
349 | pub(crate) fn bump_n(&mut self, parsed_tokens: usize) -> Vec<&tt::TokenTree> { | ||
350 | let res = self.walker.collect_token_trees(parsed_tokens); | ||
351 | res | ||
352 | } | ||
353 | } | ||
354 | |||
355 | impl<'a> TokenSource for SubtreeTokenSource<'a> { | ||
356 | fn token_kind(&self, pos: usize) -> SyntaxKind { | ||
357 | if let Some(tok) = self.walker.get(pos) { | ||
358 | tok.kind | ||
359 | } else { | ||
360 | SyntaxKind::EOF | ||
361 | } | ||
362 | } | ||
363 | fn is_token_joint_to_next(&self, pos: usize) -> bool { | ||
364 | self.walker.get(pos).unwrap().is_joint_to_next | ||
365 | } | ||
366 | fn is_keyword(&self, pos: usize, kw: &str) -> bool { | ||
367 | self.walker.get(pos).unwrap().text == *kw | ||
368 | } | ||
369 | } | ||
370 | |||
371 | struct TokenPeek<'a, I> | ||
372 | where | ||
373 | I: Iterator<Item = &'a tt::TokenTree>, | ||
374 | { | ||
375 | iter: itertools::MultiPeek<I>, | ||
376 | } | ||
377 | |||
378 | // helper function | ||
379 | fn to_punct(tt: &tt::TokenTree) -> Option<&tt::Punct> { | ||
380 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(pp)) = tt { | ||
381 | return Some(pp); | ||
382 | } | ||
383 | None | ||
384 | } | ||
385 | |||
386 | impl<'a, I> TokenPeek<'a, I> | ||
387 | where | ||
388 | I: Iterator<Item = &'a tt::TokenTree>, | ||
389 | { | ||
390 | pub fn new(iter: I) -> Self { | ||
391 | TokenPeek { iter: itertools::multipeek(iter) } | ||
392 | } | ||
393 | |||
394 | fn current_punct2(&mut self, p: &tt::Punct) -> Option<((char, char), bool)> { | ||
395 | if p.spacing != tt::Spacing::Joint { | ||
396 | return None; | ||
397 | } | ||
398 | |||
399 | self.iter.reset_peek(); | ||
400 | let p1 = to_punct(self.iter.peek()?)?; | ||
401 | Some(((p.char, p1.char), p1.spacing == tt::Spacing::Joint)) | ||
402 | } | ||
403 | |||
404 | fn current_punct3(&mut self, p: &tt::Punct) -> Option<((char, char, char), bool)> { | ||
405 | self.current_punct2(p).and_then(|((p0, p1), last_joint)| { | ||
406 | if !last_joint { | ||
407 | None | ||
408 | } else { | ||
409 | let p2 = to_punct(*self.iter.peek()?)?; | ||
410 | Some(((p0, p1, p2.char), p2.spacing == tt::Spacing::Joint)) | ||
411 | } | ||
412 | }) | ||
413 | } | ||
414 | } | ||
415 | |||
416 | fn convert_multi_char_punct<'b, I>( | ||
417 | p: &tt::Punct, | ||
418 | iter: &mut TokenPeek<'b, I>, | ||
419 | ) -> Option<(SyntaxKind, bool, &'static str, usize)> | ||
420 | where | ||
421 | I: Iterator<Item = &'b tt::TokenTree>, | ||
422 | { | ||
423 | if let Some((m, is_joint_to_next)) = iter.current_punct3(p) { | ||
424 | if let Some((kind, text)) = match m { | ||
425 | ('<', '<', '=') => Some((SHLEQ, "<<=")), | ||
426 | ('>', '>', '=') => Some((SHREQ, ">>=")), | ||
427 | ('.', '.', '.') => Some((DOTDOTDOT, "...")), | ||
428 | ('.', '.', '=') => Some((DOTDOTEQ, "..=")), | ||
429 | _ => None, | ||
430 | } { | ||
431 | return Some((kind, is_joint_to_next, text, 3)); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | if let Some((m, is_joint_to_next)) = iter.current_punct2(p) { | ||
436 | if let Some((kind, text)) = match m { | ||
437 | ('<', '<') => Some((SHL, "<<")), | ||
438 | ('>', '>') => Some((SHR, ">>")), | ||
439 | |||
440 | ('|', '|') => Some((PIPEPIPE, "||")), | ||
441 | ('&', '&') => Some((AMPAMP, "&&")), | ||
442 | ('%', '=') => Some((PERCENTEQ, "%=")), | ||
443 | ('*', '=') => Some((STAREQ, "*=")), | ||
444 | ('/', '=') => Some((SLASHEQ, "/=")), | ||
445 | ('^', '=') => Some((CARETEQ, "^=")), | ||
446 | |||
447 | ('&', '=') => Some((AMPEQ, "&=")), | ||
448 | ('|', '=') => Some((PIPEEQ, "|=")), | ||
449 | ('-', '=') => Some((MINUSEQ, "-=")), | ||
450 | ('+', '=') => Some((PLUSEQ, "+=")), | ||
451 | ('>', '=') => Some((GTEQ, ">=")), | ||
452 | ('<', '=') => Some((LTEQ, "<=")), | ||
453 | |||
454 | ('-', '>') => Some((THIN_ARROW, "->")), | ||
455 | ('!', '=') => Some((NEQ, "!=")), | ||
456 | ('=', '>') => Some((FAT_ARROW, "=>")), | ||
457 | ('=', '=') => Some((EQEQ, "==")), | ||
458 | ('.', '.') => Some((DOTDOT, "..")), | ||
459 | (':', ':') => Some((COLONCOLON, "::")), | ||
460 | |||
461 | _ => None, | ||
462 | } { | ||
463 | return Some((kind, is_joint_to_next, text, 2)); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | None | ||
468 | } | ||
469 | |||
470 | fn convert_delim(d: tt::Delimiter, closing: bool) -> Option<TtToken> { | ||
471 | let (kinds, texts) = match d { | ||
472 | tt::Delimiter::Parenthesis => ([L_PAREN, R_PAREN], "()"), | ||
473 | tt::Delimiter::Brace => ([L_CURLY, R_CURLY], "{}"), | ||
474 | tt::Delimiter::Bracket => ([L_BRACK, R_BRACK], "[]"), | ||
475 | tt::Delimiter::None => return None, | ||
476 | }; | ||
477 | |||
478 | let idx = closing as usize; | ||
479 | let kind = kinds[idx]; | ||
480 | let text = &texts[idx..texts.len() - (1 - idx)]; | ||
481 | Some(TtToken { kind, is_joint_to_next: false, text: SmolStr::new(text), n_tokens: 1 }) | ||
482 | } | ||
483 | |||
484 | fn convert_literal(l: &tt::Literal) -> TtToken { | ||
485 | TtToken { | ||
486 | kind: classify_literal(&l.text).unwrap().kind, | ||
487 | is_joint_to_next: false, | ||
488 | text: l.text.clone(), | ||
489 | n_tokens: 1, | ||
490 | } | ||
491 | } | ||
492 | |||
493 | fn convert_ident(ident: &tt::Ident) -> TtToken { | ||
494 | let kind = SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT); | ||
495 | TtToken { kind, is_joint_to_next: false, text: ident.text.clone(), n_tokens: 1 } | ||
496 | } | ||
497 | |||
498 | fn convert_punct(p: &tt::Punct, parent: &tt::Subtree, next: usize) -> TtToken { | ||
499 | let iter = parent.token_trees[next + 1..].iter(); | ||
500 | let mut peek = TokenPeek::new(iter); | ||
501 | |||
502 | if let Some((kind, is_joint_to_next, text, size)) = convert_multi_char_punct(p, &mut peek) { | ||
503 | TtToken { kind, is_joint_to_next, text: text.into(), n_tokens: size } | ||
504 | } else { | ||
505 | let kind = match p.char { | ||
506 | // lexer may produce combpund tokens for these ones | ||
507 | '.' => DOT, | ||
508 | ':' => COLON, | ||
509 | '=' => EQ, | ||
510 | '!' => EXCL, | ||
511 | '-' => MINUS, | ||
512 | c => SyntaxKind::from_char(c).unwrap(), | ||
513 | }; | ||
514 | let text = { | ||
515 | let mut buf = [0u8; 4]; | ||
516 | let s: &str = p.char.encode_utf8(&mut buf); | ||
517 | SmolStr::new(s) | ||
518 | }; | ||
519 | TtToken { kind, is_joint_to_next: p.spacing == tt::Spacing::Joint, text, n_tokens: 1 } | ||
520 | } | ||
521 | } | ||
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 139a0fd33..19c17bd55 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | use ra_parser::{TokenSource, TreeSink, ParseError}; | 1 | use ra_parser::{TreeSink, ParseError}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, SyntaxNode, TextRange, SyntaxKind, SmolStr, SyntaxTreeBuilder, TreeArc, SyntaxElement, | 3 | AstNode, SyntaxNode, TextRange, SyntaxKind, SmolStr, SyntaxTreeBuilder, TreeArc, SyntaxElement, |
4 | ast, SyntaxKind::*, TextUnit, classify_literal | 4 | ast, SyntaxKind::*, TextUnit |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::subtree_source::{SubtreeTokenSource, Querier}; | ||
8 | |||
7 | /// Maps `tt::TokenId` to the relative range of the original token. | 9 | /// Maps `tt::TokenId` to the relative range of the original token. |
8 | #[derive(Default)] | 10 | #[derive(Default)] |
9 | pub struct TokenMap { | 11 | pub struct TokenMap { |
@@ -22,8 +24,8 @@ pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap) | |||
22 | 24 | ||
23 | /// Parses the token tree (result of macro expansion) as a sequence of items | 25 | /// Parses the token tree (result of macro expansion) as a sequence of items |
24 | pub fn token_tree_to_ast_item_list(tt: &tt::Subtree) -> TreeArc<ast::SourceFile> { | 26 | pub fn token_tree_to_ast_item_list(tt: &tt::Subtree) -> TreeArc<ast::SourceFile> { |
25 | let token_source = TtTokenSource::new(tt); | 27 | let token_source = SubtreeTokenSource::new(tt); |
26 | let mut tree_sink = TtTreeSink::new(&token_source.tokens); | 28 | let mut tree_sink = TtTreeSink::new(token_source.querier()); |
27 | ra_parser::parse(&token_source, &mut tree_sink); | 29 | ra_parser::parse(&token_source, &mut tree_sink); |
28 | let syntax = tree_sink.inner.finish(); | 30 | let syntax = tree_sink.inner.finish(); |
29 | ast::SourceFile::cast(&syntax).unwrap().to_owned() | 31 | ast::SourceFile::cast(&syntax).unwrap().to_owned() |
@@ -103,229 +105,19 @@ fn convert_tt( | |||
103 | Some(res) | 105 | Some(res) |
104 | } | 106 | } |
105 | 107 | ||
106 | #[derive(Debug)] | 108 | struct TtTreeSink<'a, Q: Querier> { |
107 | struct TtTokenSource { | ||
108 | tokens: Vec<TtToken>, | ||
109 | } | ||
110 | |||
111 | #[derive(Debug)] | ||
112 | struct TtToken { | ||
113 | kind: SyntaxKind, | ||
114 | is_joint_to_next: bool, | ||
115 | text: SmolStr, | ||
116 | } | ||
117 | |||
118 | // Some helper functions | ||
119 | fn to_punct(tt: &tt::TokenTree) -> Option<&tt::Punct> { | ||
120 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(pp)) = tt { | ||
121 | return Some(pp); | ||
122 | } | ||
123 | None | ||
124 | } | ||
125 | |||
126 | struct TokenPeek<'a, I> | ||
127 | where | ||
128 | I: Iterator<Item = &'a tt::TokenTree>, | ||
129 | { | ||
130 | iter: itertools::MultiPeek<I>, | ||
131 | } | ||
132 | |||
133 | impl<'a, I> TokenPeek<'a, I> | ||
134 | where | ||
135 | I: Iterator<Item = &'a tt::TokenTree>, | ||
136 | { | ||
137 | fn next(&mut self) -> Option<&tt::TokenTree> { | ||
138 | self.iter.next() | ||
139 | } | ||
140 | |||
141 | fn current_punct2(&mut self, p: &tt::Punct) -> Option<((char, char), bool)> { | ||
142 | if p.spacing != tt::Spacing::Joint { | ||
143 | return None; | ||
144 | } | ||
145 | |||
146 | self.iter.reset_peek(); | ||
147 | let p1 = to_punct(self.iter.peek()?)?; | ||
148 | Some(((p.char, p1.char), p1.spacing == tt::Spacing::Joint)) | ||
149 | } | ||
150 | |||
151 | fn current_punct3(&mut self, p: &tt::Punct) -> Option<((char, char, char), bool)> { | ||
152 | self.current_punct2(p).and_then(|((p0, p1), last_joint)| { | ||
153 | if !last_joint { | ||
154 | None | ||
155 | } else { | ||
156 | let p2 = to_punct(*self.iter.peek()?)?; | ||
157 | Some(((p0, p1, p2.char), p2.spacing == tt::Spacing::Joint)) | ||
158 | } | ||
159 | }) | ||
160 | } | ||
161 | } | ||
162 | |||
163 | impl TtTokenSource { | ||
164 | fn new(tt: &tt::Subtree) -> TtTokenSource { | ||
165 | let mut res = TtTokenSource { tokens: Vec::new() }; | ||
166 | res.convert_subtree(tt); | ||
167 | res | ||
168 | } | ||
169 | fn convert_subtree(&mut self, sub: &tt::Subtree) { | ||
170 | self.push_delim(sub.delimiter, false); | ||
171 | let mut peek = TokenPeek { iter: itertools::multipeek(sub.token_trees.iter()) }; | ||
172 | while let Some(tt) = peek.iter.next() { | ||
173 | self.convert_tt(tt, &mut peek); | ||
174 | } | ||
175 | self.push_delim(sub.delimiter, true) | ||
176 | } | ||
177 | |||
178 | fn convert_tt<'a, I>(&mut self, tt: &tt::TokenTree, iter: &mut TokenPeek<'a, I>) | ||
179 | where | ||
180 | I: Iterator<Item = &'a tt::TokenTree>, | ||
181 | { | ||
182 | match tt { | ||
183 | tt::TokenTree::Leaf(token) => self.convert_token(token, iter), | ||
184 | tt::TokenTree::Subtree(sub) => self.convert_subtree(sub), | ||
185 | } | ||
186 | } | ||
187 | |||
188 | fn convert_token<'a, I>(&mut self, token: &tt::Leaf, iter: &mut TokenPeek<'a, I>) | ||
189 | where | ||
190 | I: Iterator<Item = &'a tt::TokenTree>, | ||
191 | { | ||
192 | let tok = match token { | ||
193 | tt::Leaf::Literal(l) => TtToken { | ||
194 | kind: classify_literal(&l.text).unwrap().kind, | ||
195 | is_joint_to_next: false, | ||
196 | text: l.text.clone(), | ||
197 | }, | ||
198 | tt::Leaf::Punct(p) => { | ||
199 | if let Some(tt) = Self::convert_multi_char_punct(p, iter) { | ||
200 | tt | ||
201 | } else { | ||
202 | let kind = match p.char { | ||
203 | // lexer may produce combpund tokens for these ones | ||
204 | '.' => DOT, | ||
205 | ':' => COLON, | ||
206 | '=' => EQ, | ||
207 | '!' => EXCL, | ||
208 | '-' => MINUS, | ||
209 | c => SyntaxKind::from_char(c).unwrap(), | ||
210 | }; | ||
211 | let text = { | ||
212 | let mut buf = [0u8; 4]; | ||
213 | let s: &str = p.char.encode_utf8(&mut buf); | ||
214 | SmolStr::new(s) | ||
215 | }; | ||
216 | TtToken { kind, is_joint_to_next: p.spacing == tt::Spacing::Joint, text } | ||
217 | } | ||
218 | } | ||
219 | tt::Leaf::Ident(ident) => { | ||
220 | let kind = SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT); | ||
221 | TtToken { kind, is_joint_to_next: false, text: ident.text.clone() } | ||
222 | } | ||
223 | }; | ||
224 | self.tokens.push(tok) | ||
225 | } | ||
226 | |||
227 | fn convert_multi_char_punct<'a, I>( | ||
228 | p: &tt::Punct, | ||
229 | iter: &mut TokenPeek<'a, I>, | ||
230 | ) -> Option<TtToken> | ||
231 | where | ||
232 | I: Iterator<Item = &'a tt::TokenTree>, | ||
233 | { | ||
234 | if let Some((m, is_joint_to_next)) = iter.current_punct3(p) { | ||
235 | if let Some((kind, text)) = match m { | ||
236 | ('<', '<', '=') => Some((SHLEQ, "<<=")), | ||
237 | ('>', '>', '=') => Some((SHREQ, ">>=")), | ||
238 | ('.', '.', '.') => Some((DOTDOTDOT, "...")), | ||
239 | ('.', '.', '=') => Some((DOTDOTEQ, "..=")), | ||
240 | _ => None, | ||
241 | } { | ||
242 | iter.next(); | ||
243 | iter.next(); | ||
244 | return Some(TtToken { kind, is_joint_to_next, text: text.into() }); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | if let Some((m, is_joint_to_next)) = iter.current_punct2(p) { | ||
249 | if let Some((kind, text)) = match m { | ||
250 | ('<', '<') => Some((SHL, "<<")), | ||
251 | ('>', '>') => Some((SHR, ">>")), | ||
252 | |||
253 | ('|', '|') => Some((PIPEPIPE, "||")), | ||
254 | ('&', '&') => Some((AMPAMP, "&&")), | ||
255 | ('%', '=') => Some((PERCENTEQ, "%=")), | ||
256 | ('*', '=') => Some((STAREQ, "*=")), | ||
257 | ('/', '=') => Some((SLASHEQ, "/=")), | ||
258 | ('^', '=') => Some((CARETEQ, "^=")), | ||
259 | |||
260 | ('&', '=') => Some((AMPEQ, "&=")), | ||
261 | ('|', '=') => Some((PIPEEQ, "|=")), | ||
262 | ('-', '=') => Some((MINUSEQ, "-=")), | ||
263 | ('+', '=') => Some((PLUSEQ, "+=")), | ||
264 | ('>', '=') => Some((GTEQ, ">=")), | ||
265 | ('<', '=') => Some((LTEQ, "<=")), | ||
266 | |||
267 | ('-', '>') => Some((THIN_ARROW, "->")), | ||
268 | ('!', '=') => Some((NEQ, "!=")), | ||
269 | ('=', '>') => Some((FAT_ARROW, "=>")), | ||
270 | ('=', '=') => Some((EQEQ, "==")), | ||
271 | ('.', '.') => Some((DOTDOT, "..")), | ||
272 | (':', ':') => Some((COLONCOLON, "::")), | ||
273 | |||
274 | _ => None, | ||
275 | } { | ||
276 | iter.next(); | ||
277 | return Some(TtToken { kind, is_joint_to_next, text: text.into() }); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | None | ||
282 | } | ||
283 | |||
284 | fn push_delim(&mut self, d: tt::Delimiter, closing: bool) { | ||
285 | let (kinds, texts) = match d { | ||
286 | tt::Delimiter::Parenthesis => ([L_PAREN, R_PAREN], "()"), | ||
287 | tt::Delimiter::Brace => ([L_CURLY, R_CURLY], "{}"), | ||
288 | tt::Delimiter::Bracket => ([L_BRACK, R_BRACK], "[]"), | ||
289 | tt::Delimiter::None => return, | ||
290 | }; | ||
291 | let idx = closing as usize; | ||
292 | let kind = kinds[idx]; | ||
293 | let text = &texts[idx..texts.len() - (1 - idx)]; | ||
294 | let tok = TtToken { kind, is_joint_to_next: false, text: SmolStr::new(text) }; | ||
295 | self.tokens.push(tok) | ||
296 | } | ||
297 | } | ||
298 | |||
299 | impl TokenSource for TtTokenSource { | ||
300 | fn token_kind(&self, pos: usize) -> SyntaxKind { | ||
301 | if let Some(tok) = self.tokens.get(pos) { | ||
302 | tok.kind | ||
303 | } else { | ||
304 | SyntaxKind::EOF | ||
305 | } | ||
306 | } | ||
307 | fn is_token_joint_to_next(&self, pos: usize) -> bool { | ||
308 | self.tokens[pos].is_joint_to_next | ||
309 | } | ||
310 | fn is_keyword(&self, pos: usize, kw: &str) -> bool { | ||
311 | self.tokens[pos].text == *kw | ||
312 | } | ||
313 | } | ||
314 | |||
315 | #[derive(Default)] | ||
316 | struct TtTreeSink<'a> { | ||
317 | buf: String, | 109 | buf: String, |
318 | tokens: &'a [TtToken], | 110 | src_querier: &'a Q, |
319 | text_pos: TextUnit, | 111 | text_pos: TextUnit, |
320 | token_pos: usize, | 112 | token_pos: usize, |
321 | inner: SyntaxTreeBuilder, | 113 | inner: SyntaxTreeBuilder, |
322 | } | 114 | } |
323 | 115 | ||
324 | impl<'a> TtTreeSink<'a> { | 116 | impl<'a, Q: Querier> TtTreeSink<'a, Q> { |
325 | fn new(tokens: &'a [TtToken]) -> TtTreeSink { | 117 | fn new(src_querier: &'a Q) -> Self { |
326 | TtTreeSink { | 118 | TtTreeSink { |
327 | buf: String::new(), | 119 | buf: String::new(), |
328 | tokens, | 120 | src_querier, |
329 | text_pos: 0.into(), | 121 | text_pos: 0.into(), |
330 | token_pos: 0, | 122 | token_pos: 0, |
331 | inner: SyntaxTreeBuilder::default(), | 123 | inner: SyntaxTreeBuilder::default(), |
@@ -333,10 +125,10 @@ impl<'a> TtTreeSink<'a> { | |||
333 | } | 125 | } |
334 | } | 126 | } |
335 | 127 | ||
336 | impl<'a> TreeSink for TtTreeSink<'a> { | 128 | impl<'a, Q: Querier> TreeSink for TtTreeSink<'a, Q> { |
337 | fn token(&mut self, kind: SyntaxKind, n_tokens: u8) { | 129 | fn token(&mut self, kind: SyntaxKind, n_tokens: u8) { |
338 | for _ in 0..n_tokens { | 130 | for _ in 0..n_tokens { |
339 | self.buf += self.tokens[self.token_pos].text.as_str(); | 131 | self.buf += &self.src_querier.token(self.token_pos).1; |
340 | self.token_pos += 1; | 132 | self.token_pos += 1; |
341 | } | 133 | } |
342 | self.text_pos += TextUnit::of_str(&self.buf); | 134 | self.text_pos += TextUnit::of_str(&self.buf); |
@@ -380,21 +172,23 @@ mod tests { | |||
380 | "#, | 172 | "#, |
381 | ); | 173 | ); |
382 | let expansion = expand(&rules, "literals!(foo)"); | 174 | let expansion = expand(&rules, "literals!(foo)"); |
383 | let tt_src = TtTokenSource::new(&expansion); | 175 | let tt_src = SubtreeTokenSource::new(&expansion); |
176 | |||
177 | let query = tt_src.querier(); | ||
384 | 178 | ||
385 | // [{] | 179 | // [{] |
386 | // [let] [a] [=] ['c'] [;] | 180 | // [let] [a] [=] ['c'] [;] |
387 | assert_eq!(tt_src.tokens[1 + 3].text, "'c'"); | 181 | assert_eq!(query.token(1 + 3).1, "'c'"); |
388 | assert_eq!(tt_src.tokens[1 + 3].kind, CHAR); | 182 | assert_eq!(query.token(1 + 3).0, CHAR); |
389 | // [let] [c] [=] [1000] [;] | 183 | // [let] [c] [=] [1000] [;] |
390 | assert_eq!(tt_src.tokens[1 + 5 + 3].text, "1000"); | 184 | assert_eq!(query.token(1 + 5 + 3).1, "1000"); |
391 | assert_eq!(tt_src.tokens[1 + 5 + 3].kind, INT_NUMBER); | 185 | assert_eq!(query.token(1 + 5 + 3).0, INT_NUMBER); |
392 | // [let] [f] [=] [12E+99_f64] [;] | 186 | // [let] [f] [=] [12E+99_f64] [;] |
393 | assert_eq!(tt_src.tokens[1 + 10 + 3].text, "12E+99_f64"); | 187 | assert_eq!(query.token(1 + 10 + 3).1, "12E+99_f64"); |
394 | assert_eq!(tt_src.tokens[1 + 10 + 3].kind, FLOAT_NUMBER); | 188 | assert_eq!(query.token(1 + 10 + 3).0, FLOAT_NUMBER); |
395 | 189 | ||
396 | // [let] [s] [=] ["rust1"] [;] | 190 | // [let] [s] [=] ["rust1"] [;] |
397 | assert_eq!(tt_src.tokens[1 + 15 + 3].text, "\"rust1\""); | 191 | assert_eq!(query.token(1 + 15 + 3).1, "\"rust1\""); |
398 | assert_eq!(tt_src.tokens[1 + 15 + 3].kind, STRING); | 192 | assert_eq!(query.token(1 + 15 + 3).0, STRING); |
399 | } | 193 | } |
400 | } | 194 | } |
diff --git a/crates/ra_mbe/src/tt_cursor.rs b/crates/ra_mbe/src/tt_cursor.rs index 3128cb9ae..d29faa77c 100644 --- a/crates/ra_mbe/src/tt_cursor.rs +++ b/crates/ra_mbe/src/tt_cursor.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | use crate::ParseError; | 1 | use crate::ParseError; |
2 | use crate::subtree_parser::Parser; | ||
2 | 3 | ||
3 | #[derive(Clone)] | 4 | #[derive(Clone)] |
4 | pub(crate) struct TtCursor<'a> { | 5 | pub(crate) struct TtCursor<'a> { |
@@ -78,6 +79,11 @@ impl<'a> TtCursor<'a> { | |||
78 | }) | 79 | }) |
79 | } | 80 | } |
80 | 81 | ||
82 | pub(crate) fn eat_path(&mut self) -> Option<tt::TokenTree> { | ||
83 | let parser = Parser::new(&mut self.pos, self.subtree); | ||
84 | parser.parse_path() | ||
85 | } | ||
86 | |||
81 | pub(crate) fn expect_char(&mut self, char: char) -> Result<(), ParseError> { | 87 | pub(crate) fn expect_char(&mut self, char: char) -> Result<(), ParseError> { |
82 | if self.at_char(char) { | 88 | if self.at_char(char) { |
83 | self.bump(); | 89 | self.bump(); |