@@ -10,6 +10,40 @@ mod test_reachable;
10
10
11
11
pub type JudgmentStack < J , O > = RefCell < FixedPointStack < J , Set < O > > > ;
12
12
13
+ /// `judgment_fn!` allows construction of inference rules using a more logic-like notation.
14
+ ///
15
+ /// The macro input looks like so:
16
+ ///
17
+ /// ```ignore
18
+ /// (
19
+ /// ( /* condition 1 */) // each condition is a parenthesized group
20
+ /// ( /* condition 2 */)
21
+ /// ( /* condition 3 */)! // `!` is optional and indicates match commit point, see below
22
+ /// -------------------- // 3 or more `-` separate the condition from the conclusion
23
+ /// ( /* conclusion */) // as is the conclusion
24
+ /// )
25
+ /// ```
26
+ ///
27
+ /// The conditions can be the following
28
+ ///
29
+ /// * `(<expr> => <binding>)` -- used to apply judgments, but really `<expr>` can be anything with an `into_iter` method.
30
+ /// * `(if <expr>)`
31
+ /// * `(if let <pat> = <expr>)`
32
+ /// * `(let <binding> = <expr>)`
33
+ ///
34
+ /// The conclusions can be the following
35
+ ///
36
+ /// * `(<pat> => <binding>)
37
+ ///
38
+ /// ## Failure reporting and match commit points
39
+ ///
40
+ /// When we fail to prove a judgment, we'll produce a failure that includes
41
+ /// all rules that partially matched. By default this lits includes every
42
+ /// rule that matches the patterns in its conclusion.
43
+ /// Sometimes this is annoyingly verbose.
44
+ /// You can place a `!` after a condition to mark it as a "match commit point".
45
+ /// Rules that fail before reaching the match commit point will not be included
46
+ /// in the failure result.
13
47
#[ macro_export]
14
48
macro_rules! judgment_fn {
15
49
(
@@ -111,30 +145,6 @@ macro_rules! judgment_fn {
111
145
}
112
146
}
113
147
114
- /// push_rules! allows construction of inference rules using a more logic-like notation.
115
- ///
116
- /// The macro input looks like: `push_rules!(builder, (...) (...) (...))` where each
117
- /// parenthesized group `(...)` is an inference rule. Inference rules are written like so:
118
- ///
119
- /// ```ignore
120
- /// (
121
- /// ( /* condition 1 */) // each condition is a parenthesized group
122
- /// ( /* condition 2 */)
123
- /// -------------------- // 3 or more `-` separate the condition from the conclusion
124
- /// ( /* conclusion */) // as is the conclusion
125
- /// )
126
- /// ```
127
- ///
128
- /// The conditions can be the following
129
- ///
130
- /// * `(<expr> => <binding>)` -- used to apply judgments, but really `<expr>` can be anything with an `into_iter` method.
131
- /// * `(if <expr>)`
132
- /// * `(if let <pat> = <expr>)`
133
- /// * `(let <binding> = <expr>)`
134
- ///
135
- /// The conclusions can be the following
136
- ///
137
- /// * `(<pat> => <binding>)
138
148
#[ macro_export]
139
149
macro_rules! push_rules {
140
150
( $judgment_name: ident, $input_value: expr, $output: expr, $failed_rules: expr, $input_names: tt => $output_ty: ty, $( $rule: tt) * ) => {
@@ -144,21 +154,27 @@ macro_rules! push_rules {
144
154
// `@rule (builder) rule` phase: invoked for each rule, emits `push_rule` call
145
155
146
156
( @rule ( $judgment_name: ident, $input_value: expr, $output: expr, $failed_rules: expr, $input_names: tt => $output_ty: ty) ( $( $m: tt) * ) ) => {
157
+ // Start accumulating.
147
158
$crate:: push_rules!( @accum
148
159
args( $judgment_name, $input_value, $output, $failed_rules, $input_names => $output_ty)
149
- accum( )
150
- $( $m) *
160
+ accum( ( 1 - 1 ) ; 0 ; )
161
+ input ( $( $m) * )
151
162
) ;
152
163
} ;
153
164
154
- // `@accum (conditions)` phase: accumulates the contents of a given rule,
165
+ // `@accum ($match_index; $current_index; conditions)` phase: accumulates the contents of a given rule,
155
166
// pushing tokens into `conditions` until the `-----` and conclusion are found.
167
+ //
168
+ // The `$match_index` stores the location where `!` was found. It is expected to start
169
+ // at 0. The `current_index` is also expected to start as the expression `0`.
156
170
157
171
( @accum
158
172
args( $judgment_name: ident, $input_value: expr, $output: expr, $failed_rules: expr, ( $( $input_names: ident) ,* ) => $output_ty: ty)
159
- accum( $( $m: tt) * )
160
- ---$( -) * ( $n: literal)
161
- ( $conclusion_name: ident( $( $patterns: tt) * ) => $v: expr)
173
+ accum( $match_index: expr; $current_index: expr; $( $m: tt) * )
174
+ input(
175
+ ---$( -) * ( $n: literal)
176
+ ( $conclusion_name: ident( $( $patterns: tt) * ) => $v: expr)
177
+ )
162
178
) => {
163
179
// Found the conclusion.
164
180
{
@@ -173,23 +189,53 @@ macro_rules! push_rules {
173
189
} ;
174
190
175
191
if let Some ( __JudgmentStruct( $( $input_names) ,* ) ) = Some ( $input_value) {
176
- $crate:: push_rules!( @match inputs( $( $input_names) * ) patterns( $( $patterns) * , ) args( @body ( $judgment_name; $n; $v; $output) ; ( $failed_rules, ( $( $input_names) ,* ) , $n) ; $( $m) * ) ) ;
192
+ $crate:: push_rules!( @match
193
+ inputs( $( $input_names) * )
194
+ patterns( $( $patterns) * , )
195
+ args( @body
196
+ ( $judgment_name; $n; $v; $output) ;
197
+ ( $failed_rules, $match_index, ( $( $input_names) ,* ) , $n) ;
198
+ $( $m) *
199
+ )
200
+ ) ;
177
201
}
178
202
}
179
203
} ;
180
204
181
- ( @accum args $args: tt accum( $( $m: tt) * ) ( $( $n: tt) * ) $( $o: tt) * ) => {
182
- // Push the condition into the list `$m`.
183
- $crate:: push_rules!( @accum args $args accum( $( $m) * ( $( $n) * ) ) $( $o) * )
205
+ ( @accum
206
+ args $args: tt
207
+ accum( $match_index: expr; $current_index: expr; $( $m: tt) * )
208
+ input( ! $( $o: tt) * )
209
+ ) => {
210
+ // If we see a `!` in the list, that represents a "match commit point".
211
+ // Overwrite `$match_index` with `$current_index` and proceed.
212
+ $crate:: push_rules!( @accum
213
+ args $args
214
+ accum( $current_index; $current_index; $( $m) * )
215
+ input( $( $o) * )
216
+ )
217
+ } ;
218
+
219
+ ( @accum
220
+ args $args: tt
221
+ accum( $match_index: expr; $current_index: expr; $( $m: tt) * )
222
+ input( ( $( $n: tt) * ) $( $o: tt) * )
223
+ ) => {
224
+ // Move one parenthesized condition `($n*)` onto the list after `$m`.
225
+ $crate:: push_rules!( @accum
226
+ args $args
227
+ accum( $match_index; $current_index+1 ; $( $m) * ( $( $n) * ) )
228
+ input( $( $o) * )
229
+ )
184
230
} ;
185
231
186
232
// Matching phase: peel off the patterns one by one and match them against the values
187
233
// extracted from the input. For anything that is not an identity pattern, invoke `downcast`.
188
234
189
- ( @match inputs( ) patterns( ) args( @body ( $judgment_name: ident; $n: literal; $v: expr; $output: expr) ; ( $failed_rules : expr , $ inputs: tt, $rule_name : literal ) ; $( $m: tt) * ) ) => {
235
+ ( @match inputs( ) patterns( ) args( @body ( $judgment_name: ident; $n: literal; $v: expr; $output: expr) ; $ inputs: tt; $( $m: tt) * ) ) => {
190
236
tracing:: trace_span!( "matched rule" , rule = $n, judgment = stringify!( $judgment_name) ) . in_scope( || {
191
237
let mut step_index = 0 ;
192
- $crate:: push_rules!( @body ( $judgment_name, $n, $v, $output) ; ( $failed_rules , $ inputs, $rule_name ) ; step_index; $( $m) * ) ;
238
+ $crate:: push_rules!( @body ( $judgment_name, $n, $v, $output) ; $ inputs; step_index; $( $m) * ) ;
193
239
} ) ;
194
240
} ;
195
241
@@ -302,24 +348,36 @@ macro_rules! push_rules {
302
348
303
349
//
304
350
305
- ( @record_failure ( $failed_rules: expr, $inputs: tt, $rule_name: literal) ; $step_index: ident; $cause: expr) => {
306
- tracing:: trace!(
307
- "rule {rn} failed at step {s} because {cause} ({file}:{line}:{column})" ,
308
- rn = $rule_name,
309
- s = $step_index,
310
- cause = $cause,
311
- file = file!( ) ,
312
- line = line!( ) ,
313
- column = column!( ) ,
314
- ) ;
315
- $failed_rules. insert(
316
- $crate:: judgment:: FailedRule {
317
- rule_name_index: Some ( ( $rule_name. to_string( ) , $step_index) ) ,
318
- file: file!( ) . to_string( ) ,
319
- line: line!( ) ,
320
- column: column!( ) ,
321
- cause: $cause,
322
- }
323
- ) ;
351
+ ( @record_failure ( $failed_rules: expr, $match_index: expr, $inputs: tt, $rule_name: literal) ; $step_index: ident; $cause: expr) => {
352
+ if $step_index >= $match_index {
353
+ tracing:: debug!(
354
+ "rule {rn} failed at step {s} because {cause} ({file}:{line}:{column})" ,
355
+ rn = $rule_name,
356
+ s = $step_index,
357
+ cause = $cause,
358
+ file = file!( ) ,
359
+ line = line!( ) ,
360
+ column = column!( ) ,
361
+ ) ;
362
+ $failed_rules. insert(
363
+ $crate:: judgment:: FailedRule {
364
+ rule_name_index: Some ( ( $rule_name. to_string( ) , $step_index) ) ,
365
+ file: file!( ) . to_string( ) ,
366
+ line: line!( ) ,
367
+ column: column!( ) ,
368
+ cause: $cause,
369
+ }
370
+ ) ;
371
+ } else {
372
+ tracing:: trace!(
373
+ "rule {rn} failed at step {s} because {cause} ({file}:{line}:{column})" ,
374
+ rn = $rule_name,
375
+ s = $step_index,
376
+ cause = $cause,
377
+ file = file!( ) ,
378
+ line = line!( ) ,
379
+ column = column!( ) ,
380
+ ) ;
381
+ }
324
382
}
325
383
}
0 commit comments