@@ -13,7 +13,6 @@ use syntax_pos::DUMMY_SP;
13
13
use rustc_data_structures:: fx:: FxHashMap ;
14
14
use rustc_data_structures:: sync:: Lrc ;
15
15
use std:: mem;
16
- use std:: ops:: Add ;
17
16
use std:: rc:: Rc ;
18
17
19
18
/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
@@ -23,6 +22,7 @@ enum Frame {
23
22
}
24
23
25
24
impl Frame {
25
+ /// Construct a new frame around the delimited set of tokens.
26
26
fn new ( tts : Vec < quoted:: TokenTree > ) -> Frame {
27
27
let forest = Lrc :: new ( quoted:: Delimited { delim : token:: NoDelim , tts : tts } ) ;
28
28
Frame :: Delimited { forest : forest, idx : 0 , span : DelimSpan :: dummy ( ) }
@@ -46,30 +46,72 @@ impl Iterator for Frame {
46
46
}
47
47
}
48
48
49
- /// This can do Macro-By-Example transcription. On the other hand, if `src` contains no
50
- /// `TokenTree::{Sequence, MetaVar, MetaVarDecl}`s, `interp` can (and should) be `None`.
49
+ /// This can do Macro-By-Example transcription.
50
+ /// - `interp` is a map of meta-variables to the tokens (non-terminals) they matched in the
51
+ /// invocation. We are assuming we already know there is a match.
52
+ /// - `src` is the RHS of the MBE, that is, the "example" we are filling in.
53
+ ///
54
+ /// For example,
55
+ ///
56
+ /// ```rust
57
+ /// macro_rules! foo {
58
+ /// ($id:ident) => { println!("{}", stringify!($id)); }
59
+ /// }
60
+ ///
61
+ /// foo!(bar);
62
+ /// ```
63
+ ///
64
+ /// `interp` would contain `$id => bar` and `src` would contain `println!("{}", stringify!($id));`.
65
+ ///
66
+ /// `transcribe` would return a `TokenStream` containing `println!("{}", stringify!(bar));`.
67
+ ///
68
+ /// Along the way, we do some additional error checking.
51
69
pub fn transcribe (
52
70
cx : & ExtCtxt < ' _ > ,
53
- interp : Option < FxHashMap < Ident , Rc < NamedMatch > > > ,
71
+ interp : & FxHashMap < Ident , Rc < NamedMatch > > ,
54
72
src : Vec < quoted:: TokenTree > ,
55
73
) -> TokenStream {
74
+ assert ! ( src. len( ) > 0 ) ;
75
+
76
+ // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
77
+ // we have yet to expand/are still expanding. We start the stack off with the whole RHS.
56
78
let mut stack: SmallVec < [ Frame ; 1 ] > = smallvec ! [ Frame :: new( src) ] ;
57
- let interpolations = interp. unwrap_or_else ( FxHashMap :: default) ; /* just a convenience */
79
+
80
+ // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
81
+ // `repeats` keeps track of where we are in matching at each level, with the last element being
82
+ // the most deeply nested sequence. This is used as a stack.
58
83
let mut repeats = Vec :: new ( ) ;
84
+
85
+ // `result` contains resulting token stream from the TokenTree we just finished processing. At
86
+ // the end, this will contain the full result of transcription, but at arbitrary points during
87
+ // `transcribe`, `result` will contain subsets of the final result.
88
+ //
89
+ // Specifically, as we descend into each TokenTree, we will push the existing results onto the
90
+ // `result_stack` and clear `results`. We will then produce the results of transcribing the
91
+ // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
92
+ // `result_stack` and append `results` too it to produce the new `results` up to that point.
93
+ //
94
+ // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
95
+ // again, and we are done transcribing.
59
96
let mut result: Vec < TreeAndJoint > = Vec :: new ( ) ;
60
97
let mut result_stack = Vec :: new ( ) ;
61
98
62
99
loop {
100
+ // Look at the last frame on the stack.
63
101
let tree = if let Some ( tree) = stack. last_mut ( ) . unwrap ( ) . next ( ) {
102
+ // If it still has a TokenTree we have not looked at yet, use that tree.
64
103
tree
65
- } else {
104
+ }
105
+ // The else-case never produces a value for `tree` (it `continue`s or `return`s).
106
+ else {
107
+ // Otherwise, if we have just reached the end of a sequence and we can keep repeating,
108
+ // go back to the beginning of the sequence.
66
109
if let Frame :: Sequence { ref mut idx, ref sep, .. } = * stack. last_mut ( ) . unwrap ( ) {
67
110
let ( ref mut repeat_idx, repeat_len) = * repeats. last_mut ( ) . unwrap ( ) ;
68
111
* repeat_idx += 1 ;
69
112
if * repeat_idx < repeat_len {
70
113
* idx = 0 ;
71
114
if let Some ( sep) = sep. clone ( ) {
72
- // repeat same span, I guess
73
115
let prev_span = match result. last ( ) {
74
116
Some ( ( tt, _) ) => tt. span ( ) ,
75
117
None => DUMMY_SP ,
@@ -80,14 +122,25 @@ pub fn transcribe(
80
122
}
81
123
}
82
124
125
+ // We are done with the top of the stack. Pop it. Depending on what it was, we do
126
+ // different things. Note that the outermost item must be the delimited, wrapped RHS
127
+ // that was passed in originally to `transcribe`.
83
128
match stack. pop ( ) . unwrap ( ) {
129
+ // Done with a sequence. Pop from repeats.
84
130
Frame :: Sequence { .. } => {
85
131
repeats. pop ( ) ;
86
132
}
133
+
134
+ // We are done processing a Delimited. If this is the top-level delimited, we are
135
+ // done. Otherwise, we unwind the result_stack to append what we have produced to
136
+ // any previous results.
87
137
Frame :: Delimited { forest, span, .. } => {
88
138
if result_stack. is_empty ( ) {
139
+ // No results left to compute! We are back at the top-level.
89
140
return TokenStream :: new ( result) ;
90
141
}
142
+
143
+ // Step back into the parent Delimited.
91
144
let tree =
92
145
TokenTree :: Delimited ( span, forest. delim , TokenStream :: new ( result) . into ( ) ) ;
93
146
result = result_stack. pop ( ) . unwrap ( ) ;
@@ -97,35 +150,54 @@ pub fn transcribe(
97
150
continue ;
98
151
} ;
99
152
153
+ // At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
154
+ // `tree` contains the next `TokenTree` to be processed.
100
155
match tree {
156
+ // We are descending into a sequence. We first make sure that the matchers in the RHS
157
+ // and the matches in `interp` have the same shape. Otherwise, either the caller or the
158
+ // macro writer has made a mistake.
101
159
seq @ quoted:: TokenTree :: Sequence ( ..) => {
102
160
match lockstep_iter_size ( & seq, interp, & repeats) {
103
161
LockstepIterSize :: Unconstrained => {
104
162
cx. span_fatal (
105
163
seq. span ( ) , /* blame macro writer */
106
- "attempted to repeat an expression \
107
- containing no syntax \
108
- variables matched as repeating at this depth",
164
+ "attempted to repeat an expression containing no syntax variables \
165
+ matched as repeating at this depth",
109
166
) ;
110
167
}
168
+
111
169
LockstepIterSize :: Contradiction ( ref msg) => {
170
+ // FIXME: this should be impossible. I (mark-i-m) believe it would
171
+ // represent a bug in the macro_parser.
112
172
// FIXME #2887 blame macro invoker instead
113
173
cx. span_fatal ( seq. span ( ) , & msg[ ..] ) ;
114
174
}
175
+
115
176
LockstepIterSize :: Constraint ( len, _) => {
177
+ // We do this to avoid an extra clone above. We know that this is a
178
+ // sequence already.
116
179
let ( sp, seq) = if let quoted:: TokenTree :: Sequence ( sp, seq) = seq {
117
180
( sp, seq)
118
181
} else {
119
182
unreachable ! ( )
120
183
} ;
121
184
185
+ // Is the repetition empty?
122
186
if len == 0 {
123
187
if seq. op == quoted:: KleeneOp :: OneOrMore {
188
+ // FIXME: this should be impossible because we check for this in
189
+ // macro_parser.rs
124
190
// FIXME #2887 blame invoker
125
191
cx. span_fatal ( sp. entire ( ) , "this must repeat at least once" ) ;
126
192
}
127
193
} else {
194
+ // 0 is the initial counter (we have done 0 repretitions so far). `len`
195
+ // is the total number of reptitions we should generate.
128
196
repeats. push ( ( 0 , len) ) ;
197
+
198
+ // The first time we encounter the sequence we push it to the stack. It
199
+ // then gets reused (see the beginning of the loop) until we are done
200
+ // repeating.
129
201
stack. push ( Frame :: Sequence {
130
202
idx : 0 ,
131
203
sep : seq. separator . clone ( ) ,
@@ -135,10 +207,16 @@ pub fn transcribe(
135
207
}
136
208
}
137
209
}
138
- // FIXME #2887: think about span stuff here
210
+
211
+ // Replace the meta-var with the matched token tree from the invocation.
139
212
quoted:: TokenTree :: MetaVar ( mut sp, ident) => {
140
- if let Some ( cur_matched) = lookup_cur_matched ( ident, & interpolations, & repeats) {
213
+ // Find the matched nonterminal from the macro invocation, and use it to replace
214
+ // the meta-var.
215
+ if let Some ( cur_matched) = lookup_cur_matched ( ident, interp, & repeats) {
141
216
if let MatchedNonterminal ( ref nt) = * cur_matched {
217
+ // FIXME #2887: why do we apply a mark when matching a token tree meta-var
218
+ // (e.g. `$x:tt`), but not when we are matching any other type of token
219
+ // tree?
142
220
if let NtTT ( ref tt) = * * nt {
143
221
result. push ( tt. clone ( ) . into ( ) ) ;
144
222
} else {
@@ -147,35 +225,55 @@ pub fn transcribe(
147
225
result. push ( token. into ( ) ) ;
148
226
}
149
227
} else {
228
+ // We were unable to descend far enough. This is an error.
150
229
cx. span_fatal (
151
230
sp, /* blame the macro writer */
152
231
& format ! ( "variable '{}' is still repeating at this depth" , ident) ,
153
232
) ;
154
233
}
155
234
} else {
235
+ // If we aren't able to match the meta-var, we push it back into the result but
236
+ // with modified syntax context. (I believe this supports nested macros).
156
237
let ident =
157
238
Ident :: new ( ident. name , ident. span . apply_mark ( cx. current_expansion . mark ) ) ;
158
239
sp = sp. apply_mark ( cx. current_expansion . mark ) ;
159
240
result. push ( TokenTree :: Token ( sp, token:: Dollar ) . into ( ) ) ;
160
241
result. push ( TokenTree :: Token ( sp, token:: Token :: from_ast_ident ( ident) ) . into ( ) ) ;
161
242
}
162
243
}
244
+
245
+ // If we are entering a new delimiter, we push its contents to the `stack` to be
246
+ // processed, and we push all of the currently produced results to the `result_stack`.
247
+ // We will produce all of the results of the inside of the `Delimited` and then we will
248
+ // jump back out of the Delimited, pop the result_stack and add the new results back to
249
+ // the previous results (from outside the Delimited).
163
250
quoted:: TokenTree :: Delimited ( mut span, delimited) => {
164
251
span = span. apply_mark ( cx. current_expansion . mark ) ;
165
252
stack. push ( Frame :: Delimited { forest : delimited, idx : 0 , span : span } ) ;
166
253
result_stack. push ( mem:: replace ( & mut result, Vec :: new ( ) ) ) ;
167
254
}
255
+
256
+ // Nothing much to do here. Just push the token to the result, being careful to
257
+ // preserve syntax context.
168
258
quoted:: TokenTree :: Token ( sp, tok) => {
169
259
let mut marker = Marker ( cx. current_expansion . mark ) ;
170
260
let mut tt = TokenTree :: Token ( sp, tok) ;
171
261
noop_visit_tt ( & mut tt, & mut marker) ;
172
262
result. push ( tt. into ( ) ) ;
173
263
}
264
+
265
+ // There should be no meta-var declarations in the invocation of a macro.
174
266
quoted:: TokenTree :: MetaVarDecl ( ..) => panic ! ( "unexpected `TokenTree::MetaVarDecl" ) ,
175
267
}
176
268
}
177
269
}
178
270
271
+ /// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
272
+ /// the set of matches `interpolations`.
273
+ ///
274
+ /// See the definition of `repeats` in the `transcribe` function. `repeats` is used to descend
275
+ /// into the right place in nested matchers. If we attempt to descend too far, the macro writer has
276
+ /// made a mistake, and we return `None`.
179
277
fn lookup_cur_matched (
180
278
ident : Ident ,
181
279
interpolations : & FxHashMap < Ident , Rc < NamedMatch > > ,
@@ -195,14 +293,29 @@ fn lookup_cur_matched(
195
293
} )
196
294
}
197
295
296
+ /// An accumulator over a TokenTree to be used with `fold`. During transcription, we need to make
297
+ /// sure that the size of each sequence and all of its nested sequences are the same as the sizes
298
+ /// of all the matched (nested) sequences in the macro invocation. If they don't match, somebody
299
+ /// has made a mistake (either the macro writer or caller).
198
300
#[ derive( Clone ) ]
199
301
enum LockstepIterSize {
302
+ /// No constraints on length of matcher. This is true for any TokenTree variants except a
303
+ /// `MetaVar` with an actual `MatchedSeq` (as opposed to a `MatchedNonterminal`).
200
304
Unconstrained ,
305
+
306
+ /// A `MetaVar` with an actual `MatchedSeq`. The length of the match and the name of the
307
+ /// meta-var are returned.
201
308
Constraint ( usize , Ident ) ,
309
+
310
+ /// Two `Constraint`s on the same sequence had different lengths. This is an error.
202
311
Contradiction ( String ) ,
203
312
}
204
313
205
314
impl LockstepIterSize {
315
+ /// Find incompatibilities in matcher/invocation sizes.
316
+ /// - `Unconstrained` is compatible with everything.
317
+ /// - `Contradiction` is incompatible with everything.
318
+ /// - `Constraint(len)` is only compatible with other constraints of the same length.
206
319
fn with ( self , other : LockstepIterSize ) -> LockstepIterSize {
207
320
match self {
208
321
LockstepIterSize :: Unconstrained => other,
@@ -224,6 +337,12 @@ impl LockstepIterSize {
224
337
}
225
338
}
226
339
340
+ /// Given a `tree`, make sure that all sequences have the same length as the matches for the
341
+ /// appropriate meta-vars in `interpolations`.
342
+ ///
343
+ /// Note that if `repeats` does not match the exact correct depth of a meta-var,
344
+ /// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of
345
+ /// multiple nested matcher sequences.
227
346
fn lockstep_iter_size (
228
347
tree : & quoted:: TokenTree ,
229
348
interpolations : & FxHashMap < Ident , Rc < NamedMatch > > ,
@@ -233,12 +352,12 @@ fn lockstep_iter_size(
233
352
match * tree {
234
353
TokenTree :: Delimited ( _, ref delimed) => {
235
354
delimed. tts . iter ( ) . fold ( LockstepIterSize :: Unconstrained , |size, tt| {
236
- size + lockstep_iter_size ( tt, interpolations, repeats)
355
+ size. with ( lockstep_iter_size ( tt, interpolations, repeats) )
237
356
} )
238
357
}
239
358
TokenTree :: Sequence ( _, ref seq) => {
240
359
seq. tts . iter ( ) . fold ( LockstepIterSize :: Unconstrained , |size, tt| {
241
- size + lockstep_iter_size ( tt, interpolations, repeats)
360
+ size. with ( lockstep_iter_size ( tt, interpolations, repeats) )
242
361
} )
243
362
}
244
363
TokenTree :: MetaVar ( _, name) | TokenTree :: MetaVarDecl ( _, name, _) => {
0 commit comments