@@ -206,6 +206,46 @@ impl PestToElement for AstRuleType {
206
206
}
207
207
}
208
208
209
+ /// Signalling result for the callback in [`flatten`].
210
+ enum ShouldFlatten {
211
+ /// Indicates that the children of the operand should be flattened recursively.
212
+ Yes ( Box < Expr > , Box < Expr > ) ,
213
+ /// Indicates that the operand should not be flattened and is transfered back to the caller.
214
+ No ( Box < Expr > ) ,
215
+ }
216
+
217
+ /// High order function to flatten associative binary nodes in a Pest expression.
218
+ ///
219
+ /// Certain nodes like the `choice` and `sequence` nodes are associative
220
+ /// (though not commutative) and can be flattened in to a variadic node instead
221
+ /// of the fixed binary one that the Pest AST has.
222
+ ///
223
+ /// The caller is responsible for seeding the vector with the tag (e.g. `choice`).
224
+ ///
225
+ /// The `determine_flatten` function parameter returns [`ShouldFlatten::Yes`] when the underlying
226
+ /// binary operator for an operand should be flattened recursively (for its underlying children),
227
+ /// and returns [`ShouldFlatten::No`] when it should not be flattened and the operand is moved
228
+ /// back to the caller to covert to [`Element`] normally.
229
+ fn flatten < F > (
230
+ sexp_elements : & mut Vec < OwnedElement > ,
231
+ left : Box < Expr > ,
232
+ right : Box < Expr > ,
233
+ determine_flatten : F ,
234
+ ) where
235
+ F : Fn ( Box < Expr > ) -> ShouldFlatten + Copy ,
236
+ {
237
+ for operand in std:: array:: IntoIter :: new ( [ left, right] ) {
238
+ match determine_flatten ( operand) {
239
+ ShouldFlatten :: Yes ( child_left, child_right) => {
240
+ flatten ( sexp_elements, child_left, child_right, determine_flatten) ;
241
+ }
242
+ ShouldFlatten :: No ( original) => {
243
+ sexp_elements. push ( original. pest_to_element ( ) ) ;
244
+ }
245
+ }
246
+ }
247
+ }
248
+
209
249
impl PestToElement for Expr {
210
250
type Element = OwnedElement ;
211
251
@@ -240,16 +280,36 @@ impl PestToElement for Expr {
240
280
text_token( "negative" ) . into( ) ,
241
281
expr. pest_to_element( ) ,
242
282
] ,
243
- Expr :: Seq ( left, right) => vec ! [
244
- text_token( "sequence" ) . into( ) ,
245
- left. pest_to_element( ) ,
246
- right. pest_to_element( ) ,
247
- ] ,
248
- Expr :: Choice ( left, right) => vec ! [
249
- text_token( "choice" ) . into( ) ,
250
- left. pest_to_element( ) ,
251
- right. pest_to_element( ) ,
252
- ] ,
283
+ Expr :: Seq ( left, right) => {
284
+ let mut elements = vec ! [ text_token( "sequence" ) . into( ) ] ;
285
+ flatten (
286
+ & mut elements,
287
+ left,
288
+ right,
289
+ |operand : Box < _ > | match * operand {
290
+ Expr :: Seq ( child_left, child_right) => {
291
+ ShouldFlatten :: Yes ( child_left, child_right)
292
+ }
293
+ _ => ShouldFlatten :: No ( operand) ,
294
+ } ,
295
+ ) ;
296
+ elements
297
+ }
298
+ Expr :: Choice ( left, right) => {
299
+ let mut elements = vec ! [ text_token( "choice" ) . into( ) ] ;
300
+ flatten (
301
+ & mut elements,
302
+ left,
303
+ right,
304
+ |operand : Box < _ > | match * operand {
305
+ Expr :: Choice ( child_left, child_right) => {
306
+ ShouldFlatten :: Yes ( child_left, child_right)
307
+ }
308
+ _ => ShouldFlatten :: No ( operand) ,
309
+ } ,
310
+ ) ;
311
+ elements
312
+ }
253
313
Expr :: Opt ( expr) => {
254
314
vec ! [ text_token( "optional" ) . into( ) , expr. pest_to_element( ) ]
255
315
}
@@ -371,10 +431,8 @@ mod tests {
371
431
type: normal,
372
432
expression:
373
433
(sequence
374
- (sequence
375
- (string exact "a")
376
- (string insensitive "b")
377
- )
434
+ (string exact "a")
435
+ (string insensitive "b")
378
436
(string exact "c")
379
437
)
380
438
}
@@ -388,10 +446,8 @@ mod tests {
388
446
type: normal,
389
447
expression:
390
448
(choice
391
- (choice
392
- (string exact "a")
393
- (string insensitive "b")
394
- )
449
+ (string exact "a")
450
+ (string insensitive "b")
395
451
(string exact "c")
396
452
)
397
453
}
@@ -405,18 +461,14 @@ mod tests {
405
461
type: normal,
406
462
expression:
407
463
(choice
408
- (choice
409
- (sequence
410
- (string exact "a")
411
- (string insensitive "b")
412
- )
413
- (sequence
414
- (sequence
415
- (string exact "c")
416
- (string insensitive "d")
417
- )
418
- (string exact "e")
419
- )
464
+ (sequence
465
+ (string exact "a")
466
+ (string insensitive "b")
467
+ )
468
+ (sequence
469
+ (string exact "c")
470
+ (string insensitive "d")
471
+ (string exact "e")
420
472
)
421
473
(sequence
422
474
(string exact "f")
@@ -426,6 +478,48 @@ mod tests {
426
478
}
427
479
}"#
428
480
) ]
481
+ #[ case:: mix_choice_grouping_1(
482
+ r#"a = { "a" ~ (^"b" | "c") ~ ^"d" ~ ("e" | "f") ~ "g" }"# ,
483
+ r#"
484
+ {
485
+ a: {
486
+ type: normal,
487
+ expression:
488
+ (sequence
489
+ (string exact "a")
490
+ (choice
491
+ (string insensitive "b")
492
+ (string exact "c")
493
+ )
494
+ (string insensitive "d")
495
+ (choice
496
+ (string exact "e")
497
+ (string exact "f")
498
+ )
499
+ (string exact "g")
500
+ )
501
+ }
502
+ }"#
503
+ ) ]
504
+ #[ case:: all_choice_grouping(
505
+ r#"a = { "a" | (^"b" | "c") | ^"d" | ("e" | "f") | "g" }"# ,
506
+ r#"
507
+ {
508
+ a: {
509
+ type: normal,
510
+ expression:
511
+ (choice
512
+ (string exact "a")
513
+ (string insensitive "b")
514
+ (string exact "c")
515
+ (string insensitive "d")
516
+ (string exact "e")
517
+ (string exact "f")
518
+ (string exact "g")
519
+ )
520
+ }
521
+ }"#
522
+ ) ]
429
523
#[ case:: optional(
430
524
r#"a = { "a"? }"# ,
431
525
r#"
0 commit comments