57
57
scope : & ' s Scope < L > ,
58
58
text : & ' t str ,
59
59
reductions : Vec < & ' static str > ,
60
+ is_cast_variant : bool ,
60
61
}
61
62
62
63
impl < ' s , ' t , T , L > Parser < ' s , ' t , T , L >
@@ -117,12 +118,14 @@ where
117
118
118
119
/// Shorthand for `parse_variant` where the parsing operation is to
119
120
/// parse the type `V` and then upcast it to the desired result type.
121
+ /// Also marks the variant as a cast variant.
120
122
pub fn parse_variant_cast < V > ( & mut self , variant_precedence : usize )
121
123
where
122
124
V : CoreParse < L > + Upcast < T > ,
123
125
{
124
126
let variant_name = std:: any:: type_name :: < V > ( ) ;
125
127
Self :: parse_variant ( self , variant_name, variant_precedence, |p| {
128
+ p. mark_as_cast_variant ( ) ;
126
129
let v: V = p. nonterminal ( ) ?;
127
130
Ok ( v. upcast ( ) )
128
131
} )
@@ -151,6 +154,7 @@ where
151
154
scope : self . scope ,
152
155
text : self . start_text ,
153
156
reductions : vec ! [ ] ,
157
+ is_cast_variant : false ,
154
158
} ;
155
159
let result = op ( & mut active_variant) ;
156
160
@@ -160,7 +164,12 @@ where
160
164
161
165
match result {
162
166
Ok ( value) => {
163
- active_variant. reductions . push ( variant_name) ;
167
+ // Subtle: for cast variants, don't record the variant name in the reduction lits,
168
+ // as it doesn't carry semantic weight. See `mark_as_cast_variant` for more details.
169
+ if !active_variant. is_cast_variant {
170
+ active_variant. reductions . push ( variant_name) ;
171
+ }
172
+
164
173
self . successes . push ( (
165
174
SuccessfulParse {
166
175
text : active_variant. text ,
@@ -280,6 +289,37 @@ where
280
289
self . text = skip_trailing_comma ( self . text ) ;
281
290
}
282
291
292
+ /// Marks this variant as an cast variant,
293
+ /// which means there is no semantic difference
294
+ /// between the thing you parsed and the reduced form.
295
+ /// We do this automatically for enum variants marked
296
+ /// as `#[cast]` or calls to `parse_variant_cast`.
297
+ ///
298
+ /// Cast variants interact differently with ambiguity detection.
299
+ /// Consider this grammar:
300
+ ///
301
+ /// ```
302
+ /// X = Y | Z // X has two variants
303
+ /// Y = A // Y has 1 variant
304
+ /// Z = A B // Z has 1 variant
305
+ /// A = "a" // A has 1 variant
306
+ /// B = "b" // B has 1 variant
307
+ /// ```
308
+ ///
309
+ /// If you mark the two `X` variants (`X = Y` and `X = Z`)
310
+ /// as cast variants, then the input `"a b"` is considered
311
+ /// unambiguous and is parsed as `X = (Z = (A = "a') (B = "b))`
312
+ /// with no remainder.
313
+ ///
314
+ /// If you don't mark those variants as cast variants,
315
+ /// then we consider this *ambiguous*, because
316
+ /// it could be that you want `X = (Y = (A = "a"))` with
317
+ /// a remainder of `"b"`. This is appropriate
318
+ /// if choosing Y vs Z has different semantic meaning.
319
+ pub fn mark_as_cast_variant ( & mut self ) {
320
+ self . is_cast_variant = true ;
321
+ }
322
+
283
323
/// Expect *exactly* the given text (after skipping whitespace)
284
324
/// in the input string. Reports an error if anything else is observed.
285
325
/// In error case, consumes only whitespace.
@@ -349,6 +389,7 @@ where
349
389
text : self . text ,
350
390
reductions : vec ! [ ] ,
351
391
scope : self . scope ,
392
+ is_cast_variant : false ,
352
393
} ;
353
394
354
395
match this. identifier_like_string ( ) {
@@ -399,6 +440,7 @@ where
399
440
scope : & scope,
400
441
text : self . text ,
401
442
reductions : vec ! [ ] ,
443
+ is_cast_variant : false ,
402
444
} ;
403
445
let result = op ( & mut av) ;
404
446
self . text = av. text ;
0 commit comments