33
33
nonterminal_name : & ' static str ,
34
34
successes : Vec < SuccessfulParse < ' t , T > > ,
35
35
failures : Set < ParseError < ' t > > ,
36
+ min_precedence_level : usize ,
36
37
}
37
38
38
39
/// The *precedence* of a variant determines how to manage
@@ -78,6 +79,7 @@ pub enum Associativity {
78
79
Left ,
79
80
Right ,
80
81
None ,
82
+ Both ,
81
83
}
82
84
83
85
impl Precedence {
@@ -98,16 +100,31 @@ impl Precedence {
98
100
99
101
/// Construct a new precedence.
100
102
fn new ( level : usize , associativity : Associativity ) -> Self {
103
+ // We require level to be STRICTLY LESS than usize::MAX
104
+ // so that we can always add 1.
105
+ assert ! ( level < std:: usize :: MAX ) ;
101
106
Self {
102
107
level,
103
- associativity,
108
+ associativity : associativity ,
104
109
}
105
110
}
106
111
}
107
112
108
113
impl Default for Precedence {
109
114
fn default ( ) -> Self {
110
- Self :: new ( std:: usize:: MAX , Associativity :: None )
115
+ // Default precedence:
116
+ //
117
+ // Use MAX-1 because we sometimes try to add 1 when we detect recursion, and we don't want overflow.
118
+ //
119
+ // Use Right associativity because if you have a variant like `T = [T]`
120
+ // then you want to be able to (by default) embed arbitrary T in that recursive location.
121
+ // If you use LEFT or NONE, then this recursive T would have a minimum level of std::usize::MAX
122
+ // (and hence never be satisfied).
123
+ //
124
+ // Using RIGHT feels a bit weird here but seems to behave correctly all the time.
125
+ // It's tempting to add something like "Both" or "N/A" that would just not set a min
126
+ // prec level when there's recursion.
127
+ Self :: new ( std:: usize:: MAX - 1 , Associativity :: Both )
111
128
}
112
129
}
113
130
@@ -172,7 +189,7 @@ where
172
189
) -> ParseResult < ' t , T > {
173
190
let text = skip_whitespace ( text) ;
174
191
175
- left_recursion:: enter ( scope, text, || {
192
+ left_recursion:: enter ( scope, text, |min_precedence_level | {
176
193
let tracing_span = tracing:: span!(
177
194
tracing:: Level :: TRACE ,
178
195
"nonterminal" ,
@@ -188,6 +205,7 @@ where
188
205
nonterminal_name,
189
206
successes : vec ! [ ] ,
190
207
failures : set ! [ ] ,
208
+ min_precedence_level,
191
209
} ;
192
210
193
211
op ( & mut parser) ;
@@ -230,6 +248,15 @@ where
230
248
) ;
231
249
let guard = span. enter ( ) ;
232
250
251
+ if variant_precedence. level < self . min_precedence_level {
252
+ tracing:: trace!(
253
+ "variant has precedence level {} which is below parser minimum of {}" ,
254
+ variant_precedence. level,
255
+ self . min_precedence_level,
256
+ ) ;
257
+ return ;
258
+ }
259
+
233
260
let mut active_variant =
234
261
ActiveVariant :: new ( variant_precedence, self . scope , self . start_text ) ;
235
262
let result = op ( & mut active_variant) ;
@@ -480,7 +507,7 @@ where
480
507
}
481
508
482
509
/// Accepts any of the given keywords.
483
- #[ tracing:: instrument( level = "trace" , ret) ]
510
+ #[ tracing:: instrument( level = "trace" , skip ( self ) , ret) ]
484
511
pub fn expect_keyword_in ( & mut self , expected : & [ & str ] ) -> Result < String , Set < ParseError < ' t > > > {
485
512
let text0 = self . current_text ;
486
513
match self . identifier_like_string ( ) {
@@ -495,6 +522,7 @@ where
495
522
/// Attempts to execute `op` and, if it successfully parses, then returns an error
496
523
/// with the value (which is meant to be incorporated into a par)
497
524
/// If `op` fails to parse then the result is `Ok`.
525
+ #[ tracing:: instrument( level = "trace" , skip( self , op, err) , ret) ]
498
526
pub fn reject < T > (
499
527
& self ,
500
528
op : impl Fn ( & mut ActiveVariant < ' s , ' t , L > ) -> Result < T , Set < ParseError < ' t > > > ,
@@ -519,7 +547,6 @@ where
519
547
/// Does not consume any input.
520
548
/// You can this to implement positional keywords -- just before parsing an identifier or variable,
521
549
/// you can invoke `reject_custom_keywords` to reject anything that you don't want to permit in this position.
522
- #[ tracing:: instrument( level = "trace" , ret) ]
523
550
pub fn reject_custom_keywords ( & self , keywords : & [ & str ] ) -> Result < ( ) , Set < ParseError < ' t > > > {
524
551
self . reject (
525
552
|p| p. expect_keyword_in ( keywords) ,
@@ -534,7 +561,7 @@ where
534
561
535
562
/// Extracts a string that meets the regex for an identifier
536
563
/// (but it could also be a keyword).
537
- #[ tracing:: instrument( level = "trace" , ret) ]
564
+ #[ tracing:: instrument( level = "trace" , skip ( self ) , ret) ]
538
565
pub fn identifier_like_string ( & mut self ) -> Result < String , Set < ParseError < ' t > > > {
539
566
self . string (
540
567
|ch| matches ! ( ch, 'a' ..='z' | 'A' ..='Z' | '_' ) ,
@@ -547,7 +574,7 @@ where
547
574
/// following the usual rules. **Disallows language keywords.**
548
575
/// If you want to disallow additional keywords,
549
576
/// see the `reject_custom_keywords` method.
550
- #[ tracing:: instrument( level = "trace" , ret) ]
577
+ #[ tracing:: instrument( level = "trace" , skip ( self ) , ret) ]
551
578
pub fn identifier ( & mut self ) -> Result < String , Set < ParseError < ' t > > > {
552
579
self . reject_custom_keywords ( L :: KEYWORDS ) ?;
553
580
self . identifier_like_string ( )
@@ -610,7 +637,7 @@ where
610
637
/// It also allows parsing where you use variables to stand for
611
638
/// more complex parameters, which is kind of combining parsing
612
639
/// and substitution and can be convenient in tests.
613
- #[ tracing:: instrument( level = "trace" , ret) ]
640
+ #[ tracing:: instrument( level = "trace" , skip ( self ) , ret) ]
614
641
pub fn variable < R > ( & mut self ) -> Result < R , Set < ParseError < ' t > > >
615
642
where
616
643
R : Debug + DowncastFrom < CoreParameter < L > > ,
@@ -636,7 +663,7 @@ where
636
663
}
637
664
638
665
/// Extract a number from the input, erroring if the input does not start with a number.
639
- #[ tracing:: instrument( level = "trace" , ret) ]
666
+ #[ tracing:: instrument( level = "trace" , skip ( self ) , ret) ]
640
667
pub fn number < T > ( & mut self ) -> Result < T , Set < ParseError < ' t > > >
641
668
where
642
669
T : FromStr + std:: fmt:: Debug ,
@@ -702,7 +729,7 @@ where
702
729
/// because we consumed the open paren `(` from `T` but then encountered an error
703
730
/// looking for the closing paren `)`.
704
731
#[ track_caller]
705
- #[ tracing:: instrument( level = "Trace" , ret) ]
732
+ #[ tracing:: instrument( level = "Trace" , skip ( self ) , ret) ]
706
733
pub fn opt_nonterminal < T > ( & mut self ) -> Result < Option < T > , Set < ParseError < ' t > > >
707
734
where
708
735
T : CoreParse < L > ,
@@ -726,6 +753,7 @@ where
726
753
727
754
/// Continue parsing instances of `T` while we can.
728
755
/// This is a greedy parse.
756
+ #[ tracing:: instrument( level = "Trace" , skip( self ) , ret) ]
729
757
pub fn many_nonterminal < T > ( & mut self ) -> Result < Vec < T > , Set < ParseError < ' t > > >
730
758
where
731
759
T : CoreParse < L > ,
@@ -737,7 +765,7 @@ where
737
765
Ok ( result)
738
766
}
739
767
740
- #[ tracing:: instrument( level = "Trace" , ret) ]
768
+ #[ tracing:: instrument( level = "Trace" , skip ( self ) , ret) ]
741
769
pub fn delimited_nonterminal < T > (
742
770
& mut self ,
743
771
open : char ,
0 commit comments