]>
Commit | Line | Data |
---|---|---|
3b2f2976 | 1 | // Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
1a4d82fc | 10 | |
3b2f2976 XL |
11 | //! This is an NFA-based parser, which calls out to the main rust parser for named nonterminals |
12 | //! (which it commits to fully when it hits one in a grammar). There's a set of current NFA threads | |
13 | //! and a set of next ones. Instead of NTs, we have a special case for Kleene star. The big-O, in | |
14 | //! pathological cases, is worse than traditional use of NFA or Earley parsing, but it's an easier | |
15 | //! fit for Macro-by-Example-style rules. | |
16 | //! | |
17 | //! (In order to prevent the pathological case, we'd need to lazily construct the resulting | |
18 | //! `NamedMatch`es at the very end. It'd be a pain, and require more memory to keep around old | |
19 | //! items, but it would also save overhead) | |
20 | //! | |
94b46f34 | 21 | //! We don't say this parser uses the Earley algorithm, because it's unnecessarily inaccurate. |
3b2f2976 XL |
22 | //! The macro parser restricts itself to the features of finite state automata. Earley parsers |
23 | //! can be described as an extension of NFAs with completion rules, prediction rules, and recursion. | |
1a4d82fc JJ |
24 | //! |
25 | //! Quick intro to how the parser works: | |
26 | //! | |
27 | //! A 'position' is a dot in the middle of a matcher, usually represented as a | |
28 | //! dot. For example `· a $( a )* a b` is a position, as is `a $( · a )* a b`. | |
29 | //! | |
30 | //! The parser walks through the input a character at a time, maintaining a list | |
3b2f2976 | 31 | //! of threads consistent with the current position in the input string: `cur_items`. |
1a4d82fc | 32 | //! |
3b2f2976 XL |
33 | //! As it processes them, it fills up `eof_items` with threads that would be valid if |
34 | //! the macro invocation is now over, `bb_items` with threads that are waiting on | |
35 | //! a Rust nonterminal like `$e:expr`, and `next_items` with threads that are waiting | |
b039eaaf | 36 | //! on a particular token. Most of the logic concerns moving the · through the |
3b2f2976 XL |
37 | //! repetitions indicated by Kleene stars. The rules for moving the · without |
38 | //! consuming any input are called epsilon transitions. It only advances or calls | |
39 | //! out to the real Rust parser when no `cur_items` threads remain. | |
1a4d82fc | 40 | //! |
7cac9316 | 41 | //! Example: |
1a4d82fc | 42 | //! |
7cac9316 XL |
43 | //! ```text, ignore |
44 | //! Start parsing a a a a b against [· a $( a )* a b]. | |
45 | //! | |
46 | //! Remaining input: a a a a b | |
3b2f2976 | 47 | //! next: [· a $( a )* a b] |
1a4d82fc | 48 | //! |
7cac9316 | 49 | //! - - - Advance over an a. - - - |
1a4d82fc | 50 | //! |
7cac9316 | 51 | //! Remaining input: a a a b |
1a4d82fc JJ |
52 | //! cur: [a · $( a )* a b] |
53 | //! Descend/Skip (first item). | |
54 | //! next: [a $( · a )* a b] [a $( a )* · a b]. | |
55 | //! | |
7cac9316 | 56 | //! - - - Advance over an a. - - - |
1a4d82fc | 57 | //! |
7cac9316 | 58 | //! Remaining input: a a b |
3b2f2976 XL |
59 | //! cur: [a $( a · )* a b] [a $( a )* a · b] |
60 | //! Follow epsilon transition: Finish/Repeat (first item) | |
1a4d82fc JJ |
61 | //! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b] |
62 | //! | |
7cac9316 | 63 | //! - - - Advance over an a. - - - (this looks exactly like the last step) |
1a4d82fc | 64 | //! |
7cac9316 | 65 | //! Remaining input: a b |
3b2f2976 XL |
66 | //! cur: [a $( a · )* a b] [a $( a )* a · b] |
67 | //! Follow epsilon transition: Finish/Repeat (first item) | |
1a4d82fc JJ |
68 | //! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b] |
69 | //! | |
7cac9316 | 70 | //! - - - Advance over an a. - - - (this looks exactly like the last step) |
1a4d82fc | 71 | //! |
7cac9316 | 72 | //! Remaining input: b |
3b2f2976 XL |
73 | //! cur: [a $( a · )* a b] [a $( a )* a · b] |
74 | //! Follow epsilon transition: Finish/Repeat (first item) | |
75 | //! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b] | |
1a4d82fc | 76 | //! |
7cac9316 | 77 | //! - - - Advance over a b. - - - |
1a4d82fc | 78 | //! |
7cac9316 | 79 | //! Remaining input: '' |
1a4d82fc | 80 | //! eof: [a $( a )* a b ·] |
7cac9316 | 81 | //! ``` |
1a4d82fc JJ |
82 | |
83 | pub use self::NamedMatch::*; | |
84 | pub use self::ParseResult::*; | |
94b46f34 | 85 | use self::TokenTreeOrTokenTreeSlice::*; |
970d7e83 | 86 | |
5bcae85e | 87 | use ast::Ident; |
cc61c64b | 88 | use syntax_pos::{self, BytePos, Span}; |
9cc50fc6 | 89 | use errors::FatalError; |
8bb4bdeb | 90 | use ext::tt::quoted::{self, TokenTree}; |
476ff2be | 91 | use parse::{Directory, ParseSess}; |
2c00a5a8 XL |
92 | use parse::parser::{Parser, PathStyle}; |
93 | use parse::token::{self, DocComment, Nonterminal, Token}; | |
1a4d82fc | 94 | use print::pprust; |
8bb4bdeb XL |
95 | use symbol::keywords; |
96 | use tokenstream::TokenStream; | |
c30ab7b3 | 97 | use util::small_vector::SmallVector; |
223e47cc | 98 | |
1a4d82fc | 99 | use std::mem; |
94b46f34 | 100 | use std::ops::{Deref, DerefMut}; |
1a4d82fc JJ |
101 | use std::rc::Rc; |
102 | use std::collections::HashMap; | |
2c00a5a8 | 103 | use std::collections::hash_map::Entry::{Occupied, Vacant}; |
223e47cc | 104 | |
2c00a5a8 | 105 | // To avoid costly uniqueness checks, we require that `MatchSeq` always has a nonempty body. |
223e47cc | 106 | |
2c00a5a8 XL |
107 | /// Either a sequence of token trees or a single one. This is used as the representation of the |
108 | /// sequence of tokens that make up a matcher. | |
1a4d82fc | 109 | #[derive(Clone)] |
94b46f34 | 110 | enum TokenTreeOrTokenTreeSlice<'a> { |
8bb4bdeb | 111 | Tt(TokenTree), |
94b46f34 | 112 | TtSeq(&'a [TokenTree]), |
1a4d82fc | 113 | } |
223e47cc | 114 | |
94b46f34 | 115 | impl<'a> TokenTreeOrTokenTreeSlice<'a> { |
2c00a5a8 XL |
116 | /// Returns the number of constituent top-level token trees of `self` (top-level in that it |
117 | /// will not recursively descend into subtrees). | |
85aaf69f | 118 | fn len(&self) -> usize { |
92a42be0 SL |
119 | match *self { |
120 | TtSeq(ref v) => v.len(), | |
121 | Tt(ref tt) => tt.len(), | |
1a4d82fc JJ |
122 | } |
123 | } | |
223e47cc | 124 | |
2c00a5a8 | 125 | /// The the `index`-th token tree of `self`. |
85aaf69f | 126 | fn get_tt(&self, index: usize) -> TokenTree { |
92a42be0 SL |
127 | match *self { |
128 | TtSeq(ref v) => v[index].clone(), | |
129 | Tt(ref tt) => tt.get_tt(index), | |
1a4d82fc JJ |
130 | } |
131 | } | |
223e47cc LB |
132 | } |
133 | ||
2c00a5a8 XL |
134 | /// An unzipping of `TokenTree`s... see the `stack` field of `MatcherPos`. |
135 | /// | |
136 | /// This is used by `inner_parse_loop` to keep track of delimited submatchers that we have | |
137 | /// descended into. | |
1a4d82fc | 138 | #[derive(Clone)] |
94b46f34 | 139 | struct MatcherTtFrame<'a> { |
2c00a5a8 | 140 | /// The "parent" matcher that we are descending into. |
94b46f34 | 141 | elts: TokenTreeOrTokenTreeSlice<'a>, |
2c00a5a8 | 142 | /// The position of the "dot" in `elts` at the time we descended. |
85aaf69f | 143 | idx: usize, |
223e47cc LB |
144 | } |
145 | ||
2c00a5a8 XL |
146 | /// Represents a single "position" (aka "matcher position", aka "item"), as described in the module |
147 | /// documentation. | |
1a4d82fc | 148 | #[derive(Clone)] |
94b46f34 | 149 | struct MatcherPos<'a> { |
2c00a5a8 | 150 | /// The token or sequence of tokens that make up the matcher |
94b46f34 | 151 | top_elts: TokenTreeOrTokenTreeSlice<'a>, |
2c00a5a8 | 152 | /// The position of the "dot" in this matcher |
85aaf69f | 153 | idx: usize, |
2c00a5a8 XL |
154 | /// The beginning position in the source that the beginning of this matcher corresponds to. In |
155 | /// other words, the token in the source at `sp_lo` is matched against the first token of the | |
156 | /// matcher. | |
157 | sp_lo: BytePos, | |
158 | ||
159 | /// For each named metavar in the matcher, we keep track of token trees matched against the | |
160 | /// metavar by the black box parser. In particular, there may be more than one match per | |
161 | /// metavar if we are in a repetition (each repetition matches each of the variables). | |
162 | /// Moreover, matchers and repetitions can be nested; the `matches` field is shared (hence the | |
163 | /// `Rc`) among all "nested" matchers. `match_lo`, `match_cur`, and `match_hi` keep track of | |
164 | /// the current position of the `self` matcher position in the shared `matches` list. | |
165 | /// | |
166 | /// Also, note that while we are descending into a sequence, matchers are given their own | |
167 | /// `matches` vector. Only once we reach the end of a full repetition of the sequence do we add | |
168 | /// all bound matches from the submatcher into the shared top-level `matches` vector. If `sep` | |
169 | /// and `up` are `Some`, then `matches` is _not_ the shared top-level list. Instead, if one | |
170 | /// wants the shared `matches`, one should use `up.matches`. | |
041b39d2 | 171 | matches: Vec<Rc<Vec<NamedMatch>>>, |
2c00a5a8 XL |
172 | /// The position in `matches` corresponding to the first metavar in this matcher's sequence of |
173 | /// token trees. In other words, the first metavar in the first token of `top_elts` corresponds | |
174 | /// to `matches[match_lo]`. | |
85aaf69f | 175 | match_lo: usize, |
2c00a5a8 XL |
176 | /// The position in `matches` corresponding to the metavar we are currently trying to match |
177 | /// against the source token stream. `match_lo <= match_cur <= match_hi`. | |
85aaf69f | 178 | match_cur: usize, |
2c00a5a8 XL |
179 | /// Similar to `match_lo` except `match_hi` is the position in `matches` of the _last_ metavar |
180 | /// in this matcher. | |
85aaf69f | 181 | match_hi: usize, |
2c00a5a8 XL |
182 | |
183 | // Specifically used if we are matching a repetition. If we aren't both should be `None`. | |
184 | /// The KleeneOp of this sequence if we are in a repetition. | |
185 | seq_op: Option<quoted::KleeneOp>, | |
186 | /// The separator if we are in a repetition | |
187 | sep: Option<Token>, | |
188 | /// The "parent" matcher position if we are in a repetition. That is, the matcher position just | |
189 | /// before we enter the sequence. | |
94b46f34 | 190 | up: Option<MatcherPosHandle<'a>>, |
2c00a5a8 XL |
191 | |
192 | // Specifically used to "unzip" token trees. By "unzip", we mean to unwrap the delimiters from | |
193 | // a delimited token tree (e.g. something wrapped in `(` `)`) or to get the contents of a doc | |
194 | // comment... | |
195 | /// When matching against matchers with nested delimited submatchers (e.g. `pat ( pat ( .. ) | |
196 | /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does | |
197 | /// that where the bottom of the stack is the outermost matcher. | |
198 | // Also, throughout the comments, this "descent" is often referred to as "unzipping"... | |
94b46f34 | 199 | stack: Vec<MatcherTtFrame<'a>>, |
223e47cc LB |
200 | } |
201 | ||
94b46f34 | 202 | impl<'a> MatcherPos<'a> { |
2c00a5a8 | 203 | /// Add `m` as a named match for the `idx`-th metavar. |
041b39d2 XL |
204 | fn push_match(&mut self, idx: usize, m: NamedMatch) { |
205 | let matches = Rc::make_mut(&mut self.matches[idx]); | |
206 | matches.push(m); | |
207 | } | |
208 | } | |
209 | ||
94b46f34 XL |
210 | // Lots of MatcherPos instances are created at runtime. Allocating them on the |
211 | // heap is slow. Furthermore, using SmallVec<MatcherPos> to allocate them all | |
212 | // on the stack is also slow, because MatcherPos is quite a large type and | |
213 | // instances get moved around a lot between vectors, which requires lots of | |
214 | // slow memcpy calls. | |
215 | // | |
216 | // Therefore, the initial MatcherPos is always allocated on the stack, | |
217 | // subsequent ones (of which there aren't that many) are allocated on the heap, | |
218 | // and this type is used to encapsulate both cases. | |
219 | enum MatcherPosHandle<'a> { | |
220 | Ref(&'a mut MatcherPos<'a>), | |
221 | Box(Box<MatcherPos<'a>>), | |
222 | } | |
223 | ||
224 | impl<'a> Clone for MatcherPosHandle<'a> { | |
225 | // This always produces a new Box. | |
226 | fn clone(&self) -> Self { | |
227 | MatcherPosHandle::Box(match *self { | |
228 | MatcherPosHandle::Ref(ref r) => Box::new((**r).clone()), | |
229 | MatcherPosHandle::Box(ref b) => b.clone(), | |
230 | }) | |
231 | } | |
232 | } | |
233 | ||
234 | impl<'a> Deref for MatcherPosHandle<'a> { | |
235 | type Target = MatcherPos<'a>; | |
236 | fn deref(&self) -> &Self::Target { | |
237 | match *self { | |
238 | MatcherPosHandle::Ref(ref r) => r, | |
239 | MatcherPosHandle::Box(ref b) => b, | |
240 | } | |
241 | } | |
242 | } | |
243 | ||
244 | impl<'a> DerefMut for MatcherPosHandle<'a> { | |
245 | fn deref_mut(&mut self) -> &mut MatcherPos<'a> { | |
246 | match *self { | |
247 | MatcherPosHandle::Ref(ref mut r) => r, | |
248 | MatcherPosHandle::Box(ref mut b) => b, | |
249 | } | |
250 | } | |
251 | } | |
252 | ||
2c00a5a8 XL |
253 | /// Represents the possible results of an attempted parse. |
254 | pub enum ParseResult<T> { | |
255 | /// Parsed successfully. | |
256 | Success(T), | |
257 | /// Arm failed to match. If the second parameter is `token::Eof`, it indicates an unexpected | |
258 | /// end of macro invocation. Otherwise, it indicates that no rules expected the given token. | |
259 | Failure(syntax_pos::Span, Token), | |
260 | /// Fatal error (malformed macro?). Abort compilation. | |
261 | Error(syntax_pos::Span, String), | |
262 | } | |
263 | ||
264 | /// A `ParseResult` where the `Success` variant contains a mapping of `Ident`s to `NamedMatch`es. | |
265 | /// This represents the mapping of metavars to the token trees they bind to. | |
476ff2be SL |
266 | pub type NamedParseResult = ParseResult<HashMap<Ident, Rc<NamedMatch>>>; |
267 | ||
2c00a5a8 | 268 | /// Count how many metavars are named in the given matcher `ms`. |
85aaf69f | 269 | pub fn count_names(ms: &[TokenTree]) -> usize { |
1a4d82fc | 270 | ms.iter().fold(0, |count, elt| { |
92a42be0 | 271 | count + match *elt { |
041b39d2 XL |
272 | TokenTree::Sequence(_, ref seq) => seq.num_captures, |
273 | TokenTree::Delimited(_, ref delim) => count_names(&delim.tts), | |
274 | TokenTree::MetaVar(..) => 0, | |
275 | TokenTree::MetaVarDecl(..) => 1, | |
9e0c209e | 276 | TokenTree::Token(..) => 0, |
1a4d82fc JJ |
277 | } |
278 | }) | |
223e47cc LB |
279 | } |
280 | ||
2c00a5a8 XL |
281 | /// Initialize `len` empty shared `Vec`s to be used to store matches of metavars. |
282 | fn create_matches(len: usize) -> Vec<Rc<Vec<NamedMatch>>> { | |
283 | (0..len).into_iter().map(|_| Rc::new(Vec::new())).collect() | |
284 | } | |
285 | ||
286 | /// Generate the top-level matcher position in which the "dot" is before the first token of the | |
287 | /// matcher `ms` and we are going to start matching at position `lo` in the source. | |
94b46f34 XL |
288 | fn initial_matcher_pos(ms: &[TokenTree], lo: BytePos) -> MatcherPos { |
289 | let match_idx_hi = count_names(ms); | |
476ff2be | 290 | let matches = create_matches(match_idx_hi); |
94b46f34 | 291 | MatcherPos { |
2c00a5a8 XL |
292 | // Start with the top level matcher given to us |
293 | top_elts: TtSeq(ms), // "elts" is an abbr. for "elements" | |
294 | // The "dot" is before the first token of the matcher | |
85aaf69f | 295 | idx: 0, |
2c00a5a8 XL |
296 | // We start matching with byte `lo` in the source code |
297 | sp_lo: lo, | |
298 | ||
299 | // Initialize `matches` to a bunch of empty `Vec`s -- one for each metavar in `top_elts`. | |
300 | // `match_lo` for `top_elts` is 0 and `match_hi` is `matches.len()`. `match_cur` is 0 since | |
301 | // we haven't actually matched anything yet. | |
3b2f2976 | 302 | matches, |
85aaf69f SL |
303 | match_lo: 0, |
304 | match_cur: 0, | |
223e47cc | 305 | match_hi: match_idx_hi, |
2c00a5a8 XL |
306 | |
307 | // Haven't descended into any delimiters, so empty stack | |
308 | stack: vec![], | |
309 | ||
310 | // Haven't descended into any sequences, so both of these are `None`. | |
311 | seq_op: None, | |
312 | sep: None, | |
313 | up: None, | |
94b46f34 | 314 | } |
223e47cc LB |
315 | } |
316 | ||
7cac9316 | 317 | /// `NamedMatch` is a pattern-match result for a single `token::MATCH_NONTERMINAL`: |
1a4d82fc | 318 | /// so it is associated with a single ident in a parse, and all |
7cac9316 XL |
319 | /// `MatchedNonterminal`s in the `NamedMatch` have the same nonterminal type |
320 | /// (expr, item, etc). Each leaf in a single `NamedMatch` corresponds to a | |
321 | /// single `token::MATCH_NONTERMINAL` in the `TokenTree` that produced it. | |
1a4d82fc | 322 | /// |
7cac9316 | 323 | /// The in-memory structure of a particular `NamedMatch` represents the match |
1a4d82fc JJ |
324 | /// that occurred when a particular subset of a matcher was applied to a |
325 | /// particular token tree. | |
326 | /// | |
7cac9316 XL |
327 | /// The width of each `MatchedSeq` in the `NamedMatch`, and the identity of |
328 | /// the `MatchedNonterminal`s, will depend on the token tree it was applied | |
329 | /// to: each `MatchedSeq` corresponds to a single `TTSeq` in the originating | |
330 | /// token tree. The depth of the `NamedMatch` structure will therefore depend | |
1a4d82fc JJ |
331 | /// only on the nesting depth of `ast::TTSeq`s in the originating |
332 | /// token tree it was derived from. | |
041b39d2 | 333 | #[derive(Debug, Clone)] |
1a4d82fc | 334 | pub enum NamedMatch { |
041b39d2 | 335 | MatchedSeq(Rc<Vec<NamedMatch>>, syntax_pos::Span), |
2c00a5a8 | 336 | MatchedNonterminal(Rc<Nonterminal>), |
223e47cc LB |
337 | } |
338 | ||
2c00a5a8 XL |
339 | /// Takes a sequence of token trees `ms` representing a matcher which successfully matched input |
340 | /// and an iterator of items that matched input and produces a `NamedParseResult`. | |
341 | fn nameize<I: Iterator<Item = NamedMatch>>( | |
342 | sess: &ParseSess, | |
343 | ms: &[TokenTree], | |
344 | mut res: I, | |
345 | ) -> NamedParseResult { | |
346 | // Recursively descend into each type of matcher (e.g. sequences, delimited, metavars) and make | |
347 | // sure that each metavar has _exactly one_ binding. If a metavar does not have exactly one | |
348 | // binding, then there is an error. If it does, then we insert the binding into the | |
349 | // `NamedParseResult`. | |
350 | fn n_rec<I: Iterator<Item = NamedMatch>>( | |
351 | sess: &ParseSess, | |
352 | m: &TokenTree, | |
353 | res: &mut I, | |
354 | ret_val: &mut HashMap<Ident, Rc<NamedMatch>>, | |
355 | ) -> Result<(), (syntax_pos::Span, String)> { | |
92a42be0 | 356 | match *m { |
2c00a5a8 XL |
357 | TokenTree::Sequence(_, ref seq) => for next_m in &seq.tts { |
358 | n_rec(sess, next_m, res.by_ref(), ret_val)? | |
359 | }, | |
360 | TokenTree::Delimited(_, ref delim) => for next_m in &delim.tts { | |
361 | n_rec(sess, next_m, res.by_ref(), ret_val)?; | |
362 | }, | |
8bb4bdeb XL |
363 | TokenTree::MetaVarDecl(span, _, id) if id.name == keywords::Invalid.name() => { |
364 | if sess.missing_fragment_specifiers.borrow_mut().remove(&span) { | |
365 | return Err((span, "missing fragment specifier".to_string())); | |
366 | } | |
367 | } | |
368 | TokenTree::MetaVarDecl(sp, bind_name, _) => { | |
5bcae85e | 369 | match ret_val.entry(bind_name) { |
1a4d82fc | 370 | Vacant(spot) => { |
041b39d2 XL |
371 | // FIXME(simulacrum): Don't construct Rc here |
372 | spot.insert(Rc::new(res.next().unwrap())); | |
1a4d82fc JJ |
373 | } |
374 | Occupied(..) => { | |
92a42be0 | 375 | return Err((sp, format!("duplicated bind name: {}", bind_name))) |
1a4d82fc JJ |
376 | } |
377 | } | |
223e47cc | 378 | } |
041b39d2 | 379 | TokenTree::MetaVar(..) | TokenTree::Token(..) => (), |
223e47cc | 380 | } |
92a42be0 SL |
381 | |
382 | Ok(()) | |
223e47cc | 383 | } |
92a42be0 | 384 | |
970d7e83 | 385 | let mut ret_val = HashMap::new(); |
92a42be0 | 386 | for m in ms { |
8bb4bdeb | 387 | match n_rec(sess, m, res.by_ref(), &mut ret_val) { |
2c00a5a8 | 388 | Ok(_) => {} |
92a42be0 SL |
389 | Err((sp, msg)) => return Error(sp, msg), |
390 | } | |
391 | } | |
392 | ||
393 | Success(ret_val) | |
223e47cc LB |
394 | } |
395 | ||
2c00a5a8 XL |
396 | /// Generate an appropriate parsing failure message. For EOF, this is "unexpected end...". For |
397 | /// other tokens, this is "unexpected token...". | |
c30ab7b3 SL |
398 | pub fn parse_failure_msg(tok: Token) -> String { |
399 | match tok { | |
400 | token::Eof => "unexpected end of macro invocation".to_string(), | |
2c00a5a8 XL |
401 | _ => format!( |
402 | "no rules expected the token `{}`", | |
403 | pprust::token_to_string(&tok) | |
404 | ), | |
c30ab7b3 SL |
405 | } |
406 | } | |
407 | ||
476ff2be | 408 | /// Perform a token equality check, ignoring syntax context (that is, an unhygienic comparison) |
2c00a5a8 | 409 | fn token_name_eq(t1: &Token, t2: &Token) -> bool { |
0531ce1d XL |
410 | if let (Some((id1, is_raw1)), Some((id2, is_raw2))) = (t1.ident(), t2.ident()) { |
411 | id1.name == id2.name && is_raw1 == is_raw2 | |
83c7162d | 412 | } else if let (Some(id1), Some(id2)) = (t1.lifetime(), t2.lifetime()) { |
cc61c64b XL |
413 | id1.name == id2.name |
414 | } else { | |
415 | *t1 == *t2 | |
1a4d82fc JJ |
416 | } |
417 | } | |
418 | ||
2c00a5a8 XL |
419 | /// Process the matcher positions of `cur_items` until it is empty. In the process, this will |
420 | /// produce more items in `next_items`, `eof_items`, and `bb_items`. | |
421 | /// | |
422 | /// For more info about the how this happens, see the module-level doc comments and the inline | |
423 | /// comments of this function. | |
424 | /// | |
425 | /// # Parameters | |
426 | /// | |
427 | /// - `sess`: the parsing session into which errors are emitted. | |
428 | /// - `cur_items`: the set of current items to be processed. This should be empty by the end of a | |
429 | /// successful execution of this function. | |
430 | /// - `next_items`: the set of newly generated items. These are used to replenish `cur_items` in | |
431 | /// the function `parse`. | |
432 | /// - `eof_items`: the set of items that would be valid if this was the EOF. | |
433 | /// - `bb_items`: the set of items that are waiting for the black-box parser. | |
434 | /// - `token`: the current token of the parser. | |
435 | /// - `span`: the `Span` in the source code corresponding to the token trees we are trying to match | |
436 | /// against the matcher positions in `cur_items`. | |
437 | /// | |
438 | /// # Returns | |
439 | /// | |
440 | /// A `ParseResult`. Note that matches are kept track of through the items generated. | |
94b46f34 | 441 | fn inner_parse_loop<'a>( |
2c00a5a8 | 442 | sess: &ParseSess, |
94b46f34 XL |
443 | cur_items: &mut SmallVector<MatcherPosHandle<'a>>, |
444 | next_items: &mut Vec<MatcherPosHandle<'a>>, | |
445 | eof_items: &mut SmallVector<MatcherPosHandle<'a>>, | |
446 | bb_items: &mut SmallVector<MatcherPosHandle<'a>>, | |
2c00a5a8 XL |
447 | token: &Token, |
448 | span: syntax_pos::Span, | |
449 | ) -> ParseResult<()> { | |
450 | // Pop items from `cur_items` until it is empty. | |
3b2f2976 | 451 | while let Some(mut item) = cur_items.pop() { |
2c00a5a8 XL |
452 | // When unzipped trees end, remove them. This corresponds to backtracking out of a |
453 | // delimited submatcher into which we already descended. In backtracking out again, we need | |
454 | // to advance the "dot" past the delimiters in the outer matcher. | |
3b2f2976 XL |
455 | while item.idx >= item.top_elts.len() { |
456 | match item.stack.pop() { | |
476ff2be | 457 | Some(MatcherTtFrame { elts, idx }) => { |
3b2f2976 XL |
458 | item.top_elts = elts; |
459 | item.idx = idx + 1; | |
1a4d82fc | 460 | } |
2c00a5a8 | 461 | None => break, |
1a4d82fc | 462 | } |
476ff2be | 463 | } |
223e47cc | 464 | |
2c00a5a8 XL |
465 | // Get the current position of the "dot" (`idx`) in `item` and the number of token trees in |
466 | // the matcher (`len`). | |
3b2f2976 XL |
467 | let idx = item.idx; |
468 | let len = item.top_elts.len(); | |
476ff2be | 469 | |
2c00a5a8 | 470 | // If `idx >= len`, then we are at or past the end of the matcher of `item`. |
476ff2be | 471 | if idx >= len { |
2c00a5a8 XL |
472 | // We are repeating iff there is a parent. If the matcher is inside of a repetition, |
473 | // then we could be at the end of a sequence or at the beginning of the next | |
474 | // repetition. | |
3b2f2976 | 475 | if item.up.is_some() { |
2c00a5a8 XL |
476 | // At this point, regardless of whether there is a separator, we should add all |
477 | // matches from the complete repetition of the sequence to the shared, top-level | |
478 | // `matches` list (actually, `up.matches`, which could itself not be the top-level, | |
479 | // but anyway...). Moreover, we add another item to `cur_items` in which the "dot" | |
480 | // is at the end of the `up` matcher. This ensures that the "dot" in the `up` | |
481 | // matcher is also advanced sufficiently. | |
482 | // | |
483 | // NOTE: removing the condition `idx == len` allows trailing separators. | |
476ff2be | 484 | if idx == len { |
2c00a5a8 | 485 | // Get the `up` matcher |
3b2f2976 | 486 | let mut new_pos = item.up.clone().unwrap(); |
476ff2be | 487 | |
2c00a5a8 | 488 | // Add matches from this repetition to the `matches` of `up` |
3b2f2976 XL |
489 | for idx in item.match_lo..item.match_hi { |
490 | let sub = item.matches[idx].clone(); | |
ea8adc8c XL |
491 | let span = span.with_lo(item.sp_lo); |
492 | new_pos.push_match(idx, MatchedSeq(sub, span)); | |
223e47cc LB |
493 | } |
494 | ||
2c00a5a8 | 495 | // Move the "dot" past the repetition in `up` |
3b2f2976 | 496 | new_pos.match_cur = item.match_hi; |
476ff2be | 497 | new_pos.idx += 1; |
3b2f2976 | 498 | cur_items.push(new_pos); |
223e47cc | 499 | } |
223e47cc | 500 | |
2c00a5a8 | 501 | // Check if we need a separator. |
3b2f2976 | 502 | if idx == len && item.sep.is_some() { |
2c00a5a8 XL |
503 | // We have a separator, and it is the current token. We can advance past the |
504 | // separator token. | |
505 | if item.sep | |
506 | .as_ref() | |
507 | .map(|sep| token_name_eq(token, sep)) | |
508 | .unwrap_or(false) | |
509 | { | |
3b2f2976 XL |
510 | item.idx += 1; |
511 | next_items.push(item); | |
223e47cc | 512 | } |
2c00a5a8 XL |
513 | } |
514 | // We don't need a separator. Move the "dot" back to the beginning of the matcher | |
515 | // and try to match again UNLESS we are only allowed to have _one_ repetition. | |
516 | else if item.seq_op != Some(quoted::KleeneOp::ZeroOrOne) { | |
3b2f2976 XL |
517 | item.match_cur = item.match_lo; |
518 | item.idx = 0; | |
519 | cur_items.push(item); | |
476ff2be | 520 | } |
2c00a5a8 XL |
521 | } |
522 | // If we are not in a repetition, then being at the end of a matcher means that we have | |
523 | // reached the potential end of the input. | |
524 | else { | |
3b2f2976 | 525 | eof_items.push(item); |
476ff2be | 526 | } |
2c00a5a8 XL |
527 | } |
528 | // We are in the middle of a matcher. | |
529 | else { | |
530 | // Look at what token in the matcher we are trying to match the current token (`token`) | |
531 | // against. Depending on that, we may generate new items. | |
3b2f2976 | 532 | match item.top_elts.get_tt(idx) { |
2c00a5a8 | 533 | // Need to descend into a sequence |
476ff2be | 534 | TokenTree::Sequence(sp, seq) => { |
2c00a5a8 XL |
535 | // Examine the case where there are 0 matches of this sequence |
536 | if seq.op == quoted::KleeneOp::ZeroOrMore | |
537 | || seq.op == quoted::KleeneOp::ZeroOrOne | |
538 | { | |
3b2f2976 XL |
539 | let mut new_item = item.clone(); |
540 | new_item.match_cur += seq.num_captures; | |
541 | new_item.idx += 1; | |
542 | for idx in item.match_cur..item.match_cur + seq.num_captures { | |
543 | new_item.push_match(idx, MatchedSeq(Rc::new(vec![]), sp)); | |
1a4d82fc | 544 | } |
3b2f2976 | 545 | cur_items.push(new_item); |
1a4d82fc | 546 | } |
476ff2be | 547 | |
3b2f2976 | 548 | let matches = create_matches(item.matches.len()); |
94b46f34 | 549 | cur_items.push(MatcherPosHandle::Box(Box::new(MatcherPos { |
476ff2be SL |
550 | stack: vec![], |
551 | sep: seq.separator.clone(), | |
2c00a5a8 | 552 | seq_op: Some(seq.op), |
476ff2be | 553 | idx: 0, |
3b2f2976 XL |
554 | matches, |
555 | match_lo: item.match_cur, | |
556 | match_cur: item.match_cur, | |
557 | match_hi: item.match_cur + seq.num_captures, | |
558 | up: Some(item), | |
ea8adc8c | 559 | sp_lo: sp.lo(), |
476ff2be | 560 | top_elts: Tt(TokenTree::Sequence(sp, seq)), |
94b46f34 | 561 | }))); |
476ff2be | 562 | } |
2c00a5a8 XL |
563 | |
564 | // We need to match a metavar (but the identifier is invalid)... this is an error | |
8bb4bdeb XL |
565 | TokenTree::MetaVarDecl(span, _, id) if id.name == keywords::Invalid.name() => { |
566 | if sess.missing_fragment_specifiers.borrow_mut().remove(&span) { | |
567 | return Error(span, "missing fragment specifier".to_string()); | |
568 | } | |
569 | } | |
2c00a5a8 XL |
570 | |
571 | // We need to match a metavar with a valid ident... call out to the black-box | |
572 | // parser by adding an item to `bb_items`. | |
041b39d2 | 573 | TokenTree::MetaVarDecl(_, _, id) => { |
476ff2be SL |
574 | // Built-in nonterminals never start with these tokens, |
575 | // so we can eliminate them from consideration. | |
94b46f34 | 576 | if may_begin_with(&*id.as_str(), token) { |
3b2f2976 | 577 | bb_items.push(item); |
1a4d82fc | 578 | } |
476ff2be | 579 | } |
2c00a5a8 XL |
580 | |
581 | // We need to descend into a delimited submatcher or a doc comment. To do this, we | |
582 | // push the current matcher onto a stack and push a new item containing the | |
583 | // submatcher onto `cur_items`. | |
584 | // | |
585 | // At the beginning of the loop, if we reach the end of the delimited submatcher, | |
586 | // we pop the stack to backtrack out of the descent. | |
476ff2be | 587 | seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..)) => { |
3b2f2976 XL |
588 | let lower_elts = mem::replace(&mut item.top_elts, Tt(seq)); |
589 | let idx = item.idx; | |
590 | item.stack.push(MatcherTtFrame { | |
476ff2be | 591 | elts: lower_elts, |
3b2f2976 | 592 | idx, |
476ff2be | 593 | }); |
3b2f2976 XL |
594 | item.idx = 0; |
595 | cur_items.push(item); | |
476ff2be | 596 | } |
2c00a5a8 XL |
597 | |
598 | // We just matched a normal token. We can just advance the parser. | |
041b39d2 | 599 | TokenTree::Token(_, ref t) if token_name_eq(t, token) => { |
3b2f2976 XL |
600 | item.idx += 1; |
601 | next_items.push(item); | |
223e47cc | 602 | } |
2c00a5a8 XL |
603 | |
604 | // There was another token that was not `token`... This means we can't add any | |
605 | // rules. NOTE that this is not necessarily an error unless _all_ items in | |
606 | // `cur_items` end up doing this. There may still be some other matchers that do | |
607 | // end up working out. | |
041b39d2 | 608 | TokenTree::Token(..) | TokenTree::MetaVar(..) => {} |
223e47cc LB |
609 | } |
610 | } | |
476ff2be SL |
611 | } |
612 | ||
2c00a5a8 | 613 | // Yay a successful parse (so far)! |
476ff2be SL |
614 | Success(()) |
615 | } | |
616 | ||
2c00a5a8 XL |
617 | /// Use the given sequence of token trees (`ms`) as a matcher. Match the given token stream `tts` |
618 | /// against it and return the match. | |
619 | /// | |
620 | /// # Parameters | |
621 | /// | |
622 | /// - `sess`: The session into which errors are emitted | |
623 | /// - `tts`: The tokenstream we are matching against the pattern `ms` | |
624 | /// - `ms`: A sequence of token trees representing a pattern against which we are matching | |
625 | /// - `directory`: Information about the file locations (needed for the black-box parser) | |
626 | /// - `recurse_into_modules`: Whether or not to recurse into modules (needed for the black-box | |
627 | /// parser) | |
628 | pub fn parse( | |
629 | sess: &ParseSess, | |
630 | tts: TokenStream, | |
631 | ms: &[TokenTree], | |
632 | directory: Option<Directory>, | |
633 | recurse_into_modules: bool, | |
634 | ) -> NamedParseResult { | |
635 | // Create a parser that can be used for the "black box" parts. | |
7cac9316 | 636 | let mut parser = Parser::new(sess, tts, directory, recurse_into_modules, true); |
2c00a5a8 XL |
637 | |
638 | // A queue of possible matcher positions. We initialize it with the matcher position in which | |
639 | // the "dot" is before the first token of the first token tree in `ms`. `inner_parse_loop` then | |
640 | // processes all of these possible matcher positions and produces posible next positions into | |
641 | // `next_items`. After some post-processing, the contents of `next_items` replenish `cur_items` | |
642 | // and we start over again. | |
94b46f34 XL |
643 | // |
644 | // This MatcherPos instance is allocated on the stack. All others -- and | |
645 | // there are frequently *no* others! -- are allocated on the heap. | |
646 | let mut initial = initial_matcher_pos(ms, parser.span.lo()); | |
647 | let mut cur_items = SmallVector::one(MatcherPosHandle::Ref(&mut initial)); | |
2c00a5a8 | 648 | let mut next_items = Vec::new(); |
476ff2be SL |
649 | |
650 | loop { | |
2c00a5a8 XL |
651 | // Matcher positions black-box parsed by parser.rs (`parser`) |
652 | let mut bb_items = SmallVector::new(); | |
653 | ||
654 | // Matcher positions that would be valid if the macro invocation was over now | |
3b2f2976 XL |
655 | let mut eof_items = SmallVector::new(); |
656 | assert!(next_items.is_empty()); | |
476ff2be | 657 | |
2c00a5a8 XL |
658 | // Process `cur_items` until either we have finished the input or we need to get some |
659 | // parsing from the black-box parser done. The result is that `next_items` will contain a | |
660 | // bunch of possible next matcher positions in `next_items`. | |
661 | match inner_parse_loop( | |
662 | sess, | |
663 | &mut cur_items, | |
664 | &mut next_items, | |
665 | &mut eof_items, | |
666 | &mut bb_items, | |
667 | &parser.token, | |
668 | parser.span, | |
669 | ) { | |
670 | Success(_) => {} | |
476ff2be SL |
671 | Failure(sp, tok) => return Failure(sp, tok), |
672 | Error(sp, msg) => return Error(sp, msg), | |
673 | } | |
674 | ||
3b2f2976 XL |
675 | // inner parse loop handled all cur_items, so it's empty |
676 | assert!(cur_items.is_empty()); | |
223e47cc | 677 | |
2c00a5a8 XL |
678 | // We need to do some post processing after the `inner_parser_loop`. |
679 | // | |
680 | // Error messages here could be improved with links to original rules. | |
681 | ||
682 | // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise, | |
683 | // either the parse is ambiguous (which should never happen) or their is a syntax error. | |
476ff2be | 684 | if token_name_eq(&parser.token, &token::Eof) { |
3b2f2976 | 685 | if eof_items.len() == 1 { |
2c00a5a8 XL |
686 | let matches = eof_items[0] |
687 | .matches | |
688 | .iter_mut() | |
689 | .map(|dv| Rc::make_mut(dv).pop().unwrap()); | |
8bb4bdeb | 690 | return nameize(sess, ms, matches); |
3b2f2976 | 691 | } else if eof_items.len() > 1 { |
2c00a5a8 XL |
692 | return Error( |
693 | parser.span, | |
694 | "ambiguity: multiple successful parses".to_string(), | |
695 | ); | |
223e47cc | 696 | } else { |
476ff2be | 697 | return Failure(parser.span, token::Eof); |
223e47cc | 698 | } |
2c00a5a8 XL |
699 | } |
700 | // Another possibility is that we need to call out to parse some rust nonterminal | |
701 | // (black-box) parser. However, if there is not EXACTLY ONE of these, something is wrong. | |
702 | else if (!bb_items.is_empty() && !next_items.is_empty()) || bb_items.len() > 1 { | |
703 | let nts = bb_items | |
704 | .iter() | |
705 | .map(|item| match item.top_elts.get_tt(item.idx) { | |
706 | TokenTree::MetaVarDecl(_, bind, name) => format!("{} ('{}')", name, bind), | |
707 | _ => panic!(), | |
708 | }) | |
709 | .collect::<Vec<String>>() | |
710 | .join(" or "); | |
711 | ||
712 | return Error( | |
713 | parser.span, | |
714 | format!( | |
715 | "local ambiguity: multiple parsing options: {}", | |
716 | match next_items.len() { | |
717 | 0 => format!("built-in NTs {}.", nts), | |
718 | 1 => format!("built-in NTs {} or 1 other option.", nts), | |
719 | n => format!("built-in NTs {} or {} other options.", nts, n), | |
720 | } | |
721 | ), | |
722 | ); | |
723 | } | |
724 | // If there are no posible next positions AND we aren't waiting for the black-box parser, | |
725 | // then their is a syntax error. | |
726 | else if bb_items.is_empty() && next_items.is_empty() { | |
476ff2be | 727 | return Failure(parser.span, parser.token); |
2c00a5a8 XL |
728 | } |
729 | // Dump all possible `next_items` into `cur_items` for the next iteration. | |
730 | else if !next_items.is_empty() { | |
731 | // Now process the next token | |
3b2f2976 | 732 | cur_items.extend(next_items.drain(..)); |
476ff2be | 733 | parser.bump(); |
2c00a5a8 XL |
734 | } |
735 | // Finally, we have the case where we need to call the black-box parser to get some | |
736 | // nonterminal. | |
737 | else { | |
738 | assert_eq!(bb_items.len(), 1); | |
739 | ||
3b2f2976 XL |
740 | let mut item = bb_items.pop().unwrap(); |
741 | if let TokenTree::MetaVarDecl(span, _, ident) = item.top_elts.get_tt(item.idx) { | |
742 | let match_cur = item.match_cur; | |
2c00a5a8 XL |
743 | item.push_match( |
744 | match_cur, | |
94b46f34 | 745 | MatchedNonterminal(Rc::new(parse_nt(&mut parser, span, &ident.as_str()))), |
2c00a5a8 | 746 | ); |
3b2f2976 XL |
747 | item.idx += 1; |
748 | item.match_cur += 1; | |
476ff2be SL |
749 | } else { |
750 | unreachable!() | |
223e47cc | 751 | } |
3b2f2976 | 752 | cur_items.push(item); |
223e47cc LB |
753 | } |
754 | ||
3b2f2976 | 755 | assert!(!cur_items.is_empty()); |
223e47cc LB |
756 | } |
757 | } | |
758 | ||
0531ce1d XL |
759 | /// The token is an identifier, but not `_`. |
760 | /// We prohibit passing `_` to macros expecting `ident` for now. | |
761 | fn get_macro_ident(token: &Token) -> Option<(Ident, bool)> { | |
762 | match *token { | |
763 | token::Ident(ident, is_raw) if ident.name != keywords::Underscore.name() => | |
764 | Some((ident, is_raw)), | |
765 | _ => None, | |
766 | } | |
767 | } | |
768 | ||
041b39d2 XL |
769 | /// Checks whether a non-terminal may begin with a particular token. |
770 | /// | |
771 | /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that | |
772 | /// token. Be conservative (return true) if not sure. | |
773 | fn may_begin_with(name: &str, token: &Token) -> bool { | |
774 | /// Checks whether the non-terminal may contain a single (non-keyword) identifier. | |
775 | fn may_be_ident(nt: &token::Nonterminal) -> bool { | |
776 | match *nt { | |
777 | token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) => false, | |
778 | _ => true, | |
779 | } | |
780 | } | |
781 | ||
782 | match name { | |
783 | "expr" => token.can_begin_expr(), | |
784 | "ty" => token.can_begin_type(), | |
0531ce1d | 785 | "ident" => get_macro_ident(token).is_some(), |
94b46f34 | 786 | "literal" => token.can_begin_literal_or_bool(), |
2c00a5a8 XL |
787 | "vis" => match *token { |
788 | // The follow-set of :vis + "priv" keyword + interpolated | |
0531ce1d | 789 | Token::Comma | Token::Ident(..) | Token::Interpolated(_) => true, |
041b39d2 XL |
790 | _ => token.can_begin_type(), |
791 | }, | |
792 | "block" => match *token { | |
793 | Token::OpenDelim(token::Brace) => true, | |
794 | Token::Interpolated(ref nt) => match nt.0 { | |
2c00a5a8 XL |
795 | token::NtItem(_) |
796 | | token::NtPat(_) | |
797 | | token::NtTy(_) | |
0531ce1d | 798 | | token::NtIdent(..) |
2c00a5a8 XL |
799 | | token::NtMeta(_) |
800 | | token::NtPath(_) | |
801 | | token::NtVis(_) => false, // none of these may start with '{'. | |
041b39d2 XL |
802 | _ => true, |
803 | }, | |
804 | _ => false, | |
805 | }, | |
806 | "path" | "meta" => match *token { | |
0531ce1d | 807 | Token::ModSep | Token::Ident(..) => true, |
041b39d2 XL |
808 | Token::Interpolated(ref nt) => match nt.0 { |
809 | token::NtPath(_) | token::NtMeta(_) => true, | |
810 | _ => may_be_ident(&nt.0), | |
811 | }, | |
812 | _ => false, | |
813 | }, | |
814 | "pat" => match *token { | |
0531ce1d | 815 | Token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) |
041b39d2 XL |
816 | Token::OpenDelim(token::Paren) | // tuple pattern |
817 | Token::OpenDelim(token::Bracket) | // slice pattern | |
818 | Token::BinOp(token::And) | // reference | |
819 | Token::BinOp(token::Minus) | // negative literal | |
820 | Token::AndAnd | // double reference | |
821 | Token::Literal(..) | // literal | |
822 | Token::DotDot | // range pattern (future compat) | |
823 | Token::DotDotDot | // range pattern (future compat) | |
824 | Token::ModSep | // path | |
825 | Token::Lt | // path (UFCS constant) | |
0531ce1d | 826 | Token::BinOp(token::Shl) => true, // path (double UFCS) |
041b39d2 XL |
827 | Token::Interpolated(ref nt) => may_be_ident(&nt.0), |
828 | _ => false, | |
829 | }, | |
94b46f34 XL |
830 | "lifetime" => match *token { |
831 | Token::Lifetime(_) => true, | |
832 | Token::Interpolated(ref nt) => match nt.0 { | |
833 | token::NtLifetime(_) | token::NtTT(_) => true, | |
834 | _ => false, | |
835 | }, | |
836 | _ => false, | |
837 | }, | |
041b39d2 XL |
838 | _ => match *token { |
839 | token::CloseDelim(_) => false, | |
840 | _ => true, | |
841 | }, | |
842 | } | |
843 | } | |
844 | ||
2c00a5a8 XL |
845 | /// A call to the "black-box" parser to parse some rust nonterminal. |
846 | /// | |
847 | /// # Parameters | |
848 | /// | |
849 | /// - `p`: the "black-box" parser to use | |
850 | /// - `sp`: the `Span` we want to parse | |
851 | /// - `name`: the name of the metavar _matcher_ we want to match (e.g. `tt`, `ident`, `block`, | |
852 | /// etc...) | |
853 | /// | |
854 | /// # Returns | |
855 | /// | |
856 | /// The parsed nonterminal. | |
476ff2be | 857 | fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { |
7cac9316 XL |
858 | if name == "tt" { |
859 | return token::NtTT(p.parse_token_tree()); | |
1a4d82fc JJ |
860 | } |
861 | // check at the beginning and the parser checks after each bump | |
cc61c64b | 862 | p.process_potential_macro_variable(); |
1a4d82fc | 863 | match name { |
92a42be0 | 864 | "item" => match panictry!(p.parse_item()) { |
e9174d1e | 865 | Some(i) => token::NtItem(i), |
9cc50fc6 SL |
866 | None => { |
867 | p.fatal("expected an item keyword").emit(); | |
2c00a5a8 | 868 | FatalError.raise(); |
9cc50fc6 | 869 | } |
e9174d1e SL |
870 | }, |
871 | "block" => token::NtBlock(panictry!(p.parse_block())), | |
92a42be0 | 872 | "stmt" => match panictry!(p.parse_stmt()) { |
c30ab7b3 | 873 | Some(s) => token::NtStmt(s), |
9cc50fc6 SL |
874 | None => { |
875 | p.fatal("expected a statement").emit(); | |
2c00a5a8 | 876 | FatalError.raise(); |
9cc50fc6 | 877 | } |
e9174d1e | 878 | }, |
92a42be0 SL |
879 | "pat" => token::NtPat(panictry!(p.parse_pat())), |
880 | "expr" => token::NtExpr(panictry!(p.parse_expr())), | |
94b46f34 | 881 | "literal" => token::NtLiteral(panictry!(p.parse_literal_maybe_minus())), |
cc61c64b | 882 | "ty" => token::NtTy(panictry!(p.parse_ty())), |
e9174d1e | 883 | // this could be handled like a token, since it is one |
0531ce1d | 884 | "ident" => if let Some((ident, is_raw)) = get_macro_ident(&p.token) { |
83c7162d | 885 | let span = p.span; |
0531ce1d | 886 | p.bump(); |
83c7162d | 887 | token::NtIdent(Ident::new(ident.name, span), is_raw) |
0531ce1d XL |
888 | } else { |
889 | let token_str = pprust::token_to_string(&p.token); | |
890 | p.fatal(&format!("expected ident, found {}", &token_str)).emit(); | |
891 | FatalError.raise() | |
892 | } | |
3b2f2976 | 893 | "path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))), |
92a42be0 | 894 | "meta" => token::NtMeta(panictry!(p.parse_meta_item())), |
cc61c64b | 895 | "vis" => token::NtVis(panictry!(p.parse_visibility(true))), |
83c7162d XL |
896 | "lifetime" => if p.check_lifetime() { |
897 | token::NtLifetime(p.expect_lifetime().ident) | |
898 | } else { | |
899 | let token_str = pprust::token_to_string(&p.token); | |
900 | p.fatal(&format!("expected a lifetime, found `{}`", &token_str)).emit(); | |
901 | FatalError.raise(); | |
902 | } | |
3157f602 XL |
903 | // this is not supposed to happen, since it has been checked |
904 | // when compiling the macro. | |
2c00a5a8 | 905 | _ => p.span_bug(sp, "invalid fragment specifier"), |
223e47cc LB |
906 | } |
907 | } |