35
35
failures : Set < ParseError < ' t > > ,
36
36
}
37
37
38
- #[ derive( Debug , PartialEq , Eq , PartialOrd , Ord , Clone , Copy ) ]
39
- pub struct Precedence ( usize ) ;
38
+ /// The *precedence* of a variant determines how to manage
39
+ /// recursive invocations.
40
+ ///
41
+ /// The general rule is that an expression
42
+ /// with lower-precedence cannot be embedded
43
+ /// into an expression of higher-precedence.
44
+ /// So given `1 + 2 * 3`, the `+` cannot be a
45
+ /// (direct) child of the `*`, because `+` is
46
+ /// lower precedence.
47
+ ///
48
+ /// The tricky bit is what happens with *equal*
49
+ /// precedence. In that case, we have to consider
50
+ /// the [`Associativity`][] (see enum for details).
51
+ #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
52
+ pub struct Precedence {
53
+ level : usize ,
54
+ associativity : Associativity ,
55
+ }
56
+
57
+ /// Determines what happens when you have equal precedence.
58
+ /// The result is dependent on whether you are embedding
59
+ /// in left position (i.e., a recurrence before having parsed any
60
+ /// tokens) or right position (a recurrence after parsed tokens).
61
+ /// So given `1 + 2 + 3`, `1 + 2` is a *left* occurrence of the second `+`.
62
+ /// And `2 + 3` is a *right* occurence of the first `+`.
63
+ ///
64
+ /// With `Associativity::Left`, equal precedence is allowed in left matches
65
+ /// but not right. So `1 + 2 + 3` parses as `(1 + 2) + 3`, as you would expect.
66
+ ///
67
+ /// With `Associativity::Right`, equal precedence is allowed in right matches
68
+ /// but not left. So `1 + 2 + 3` parses as `1 + (2 + 3)`. That's probably not what you wanted
69
+ /// for arithemetic expressions, but could be useful for (say) curried function types,
70
+ /// where `1 -> 2 -> 3` should parse as `1 -> (2 -> 3)`.
71
+ ///
72
+ /// With `Associativity::None`, equal precedence is not allowed anywhere, so
73
+ /// `1 + 2 + 3` is just an error and you have to explicitly add parentheses.
74
+ ///
75
+ /// Use `Precedence::default` for cases where precedence is not relevant.
76
+ #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
77
+ pub enum Associativity {
78
+ Left ,
79
+ Right ,
80
+ None ,
81
+ }
82
+
83
+ impl Precedence {
84
+ /// Left associative with the given precedence level
85
+ pub fn left ( level : usize ) -> Self {
86
+ Self :: new ( level, Associativity :: Left )
87
+ }
88
+
89
+ /// Right associative with the given precedence level
90
+ pub fn right ( level : usize ) -> Self {
91
+ Self :: new ( level, Associativity :: Right )
92
+ }
93
+
94
+ /// Non-associative with the given precedence level
95
+ pub fn none ( level : usize ) -> Self {
96
+ Self :: new ( level, Associativity :: None )
97
+ }
98
+
99
+ /// Construct a new precedence.
100
+ fn new ( level : usize , associativity : Associativity ) -> Self {
101
+ Self {
102
+ level,
103
+ associativity,
104
+ }
105
+ }
106
+ }
107
+
108
+ impl Default for Precedence {
109
+ fn default ( ) -> Self {
110
+ Self :: new ( 0 , Associativity :: None )
111
+ }
112
+ }
40
113
41
114
/// The "active variant" struct is the main struct
42
115
/// you will use if you are writing custom parsing logic.
81
154
mut op : impl FnMut ( & mut ActiveVariant < ' s , ' t , L > ) -> Result < T , Set < ParseError < ' t > > > ,
82
155
) -> ParseResult < ' t , T > {
83
156
Parser :: multi_variant ( scope, text, nonterminal_name, |parser| {
84
- parser. parse_variant ( nonterminal_name, 0 , & mut op) ;
157
+ parser. parse_variant ( nonterminal_name, Precedence :: default ( ) , & mut op) ;
85
158
} )
86
159
}
87
160
@@ -126,7 +199,7 @@ where
126
199
/// Shorthand for `parse_variant` where the parsing operation is to
127
200
/// parse the type `V` and then upcast it to the desired result type.
128
201
/// Also marks the variant as a cast variant.
129
- pub fn parse_variant_cast < V > ( & mut self , variant_precedence : usize )
202
+ pub fn parse_variant_cast < V > ( & mut self , variant_precedence : Precedence )
130
203
where
131
204
V : CoreParse < L > + Upcast < T > ,
132
205
{
@@ -146,19 +219,17 @@ where
146
219
pub fn parse_variant (
147
220
& mut self ,
148
221
variant_name : & ' static str ,
149
- variant_precedence : usize ,
222
+ variant_precedence : Precedence ,
150
223
op : impl FnOnce ( & mut ActiveVariant < ' s , ' t , L > ) -> Result < T , Set < ParseError < ' t > > > ,
151
224
) {
152
225
let span = tracing:: span!(
153
226
tracing:: Level :: TRACE ,
154
227
"variant" ,
155
228
name = variant_name,
156
- variant_precedence = variant_precedence
229
+ ? variant_precedence,
157
230
) ;
158
231
let guard = span. enter ( ) ;
159
232
160
- let variant_precedence = Precedence ( variant_precedence) ;
161
-
162
233
let mut active_variant =
163
234
ActiveVariant :: new ( variant_precedence, self . scope , self . start_text ) ;
164
235
let result = op ( & mut active_variant) ;
@@ -263,8 +334,9 @@ where
263
334
l1. len ( ) > l2. len ( ) && ( 0 ..l2. len ( ) ) . all ( |i| l1[ i] == l2[ i] )
264
335
}
265
336
266
- s_i. precedence > s_j. precedence
267
- || ( s_i. precedence == s_j. precedence && has_prefix ( & s_i. reductions , & s_j. reductions ) )
337
+ s_i. precedence . level > s_j. precedence . level
338
+ || ( s_i. precedence . level == s_j. precedence . level
339
+ && has_prefix ( & s_i. reductions , & s_j. reductions ) )
268
340
}
269
341
}
270
342
@@ -608,11 +680,21 @@ where
608
680
pub fn nonterminal < T > ( & mut self ) -> Result < T , Set < ParseError < ' t > > >
609
681
where
610
682
T : CoreParse < L > ,
611
- L : Language ,
612
683
{
613
684
self . nonterminal_with ( T :: parse)
614
685
}
615
686
687
+ /// Gives an error if `T` is parsable here.
688
+ pub fn reject_nonterminal < T > ( & mut self ) -> Result < ( ) , Set < ParseError < ' t > > >
689
+ where
690
+ T : CoreParse < L > ,
691
+ {
692
+ self . reject (
693
+ |p| p. nonterminal :: < T > ( ) ,
694
+ |value| ParseError :: at ( self . text ( ) , format ! ( "unexpected `{value:?}`" ) ) ,
695
+ )
696
+ }
697
+
616
698
/// Try to parse the current point as `T` and return `None` if there is nothing there.
617
699
///
618
700
/// **NB:** If the parse partially succeeds, i.e., we are able to consume some tokens
0 commit comments