expression_builder.pony

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
use "itertools"

use ".."
use ast = "../ast"

class ExpressionBuilder
  let _context: Context

  let _trivia: TriviaBuilder
  let _token: TokenBuilder
  let _keyword: KeywordBuilder
  let _operator: OperatorBuilder
  let _literal: LiteralBuilder
  let _type_type: TypeBuilder

  let annotation: NamedRule = NamedRule("an annotation" where memoize' = true)
  let item: NamedRule = NamedRule("an expression" where memoize' = true)
  let infix: NamedRule = NamedRule("an infix expression" where memoize' = true)
  let seq: NamedRule = NamedRule("an expression sequence" where memoize' = true)
  let tuple_pattern: NamedRule = NamedRule("a tuple destructuring pattern")
  let _method_params: NamedRule
  let _typedef_members: NamedRule

  new create(
    context: Context,
    trivia: TriviaBuilder,
    token: TokenBuilder,
    keyword: KeywordBuilder,
    operator: OperatorBuilder,
    literal: LiteralBuilder,
    type_type: TypeBuilder,
    method_params: NamedRule,
    typedef_members: NamedRule)
  =>
    _context = context
    _trivia = trivia
    _token = token
    _keyword = keyword
    _operator = operator
    _literal = literal
    _type_type = type_type

    _method_params = method_params
    _typedef_members = typedef_members

    _build_annotation()
    _build_expression()

  fun ref _build_annotation() =>
    let bs = _token(ast.Tokens.backslash())
    let comma = _token(ast.Tokens.comma())
    let id = _token.identifier

    annotation.set_body(
      Conj(
        [ bs
          id
          Star(Conj([ comma; id ]))
          bs ],
        _ExpActions~_annotation()))

  fun ref _build_expression() =>
    let call_arg_named: NamedRule = NamedRule("a named argument")
    let call_args_named: NamedRule = NamedRule("named arguments")
    let call_args_pos: NamedRule = NamedRule("positional arguments")
    let call_args: NamedRule = NamedRule("a positional argument")
    let exp_array: NamedRule = NamedRule("an array literal")
    let exp_assignment: NamedRule = NamedRule("an assignment")
    let exp_atom: NamedRule = NamedRule("an atomic expression")
    let exp_cond: NamedRule = NamedRule("an if condition")
    let exp_consume: NamedRule = NamedRule("a consume expression")
    let exp_decl: NamedRule = NamedRule("a binding declaration")
    let exp_ffi: NamedRule = NamedRule("an FFI call")
    let exp_for: NamedRule = NamedRule("a for loop")
    let exp_hash: NamedRule = NamedRule("a compile-time expression")
    let exp_if: NamedRule = NamedRule("an if expression")
    let exp_ifdef: NamedRule = NamedRule("an ifdef expression")
    let exp_iftype: NamedRule = NamedRule("an iftype expression")
    let exp_jump: NamedRule = NamedRule("a jump expression")
    let exp_lambda: NamedRule = NamedRule("a lambda literal")
    let exp_match: NamedRule = NamedRule("a match expression")
    let exp_object: NamedRule = NamedRule("an object literal")
    let exp_parens: NamedRule = NamedRule("a parenthesized expression")
    let exp_postfix: NamedRule = NamedRule("a postfix expression")
    let exp_prefix: NamedRule = NamedRule("a prefix expression")
    let exp_recover: NamedRule = NamedRule("a recover expression")
    let exp_repeat: NamedRule = NamedRule("a repeat loop")
    let exp_term: NamedRule = NamedRule("a term")
    let exp_try: NamedRule = NamedRule("a try expression")
    let exp_tuple: NamedRule = NamedRule("a tuple literal")
    let exp_while: NamedRule = NamedRule("a while loop")
    let exp_with: NamedRule = NamedRule("a with expression")
    let match_case: NamedRule = NamedRule("a match case")
    let match_pattern: NamedRule = NamedRule("a match pattern")
    let with_elem: NamedRule = NamedRule("a with element")

    let amp = _token(ast.Tokens.amp())
    let arrow = _token(ast.Tokens.arrow())
    let at = _token(ast.Tokens.at())
    let bar = _token(ast.Tokens.bar())
    let binary_op = _operator.binary_op
    let ccurly = _token(ast.Tokens.close_curly())
    let colon = _token(ast.Tokens.colon())
    let comma = _token(ast.Tokens.comma())
    let cparen = _token(ast.Tokens.close_paren())
    let csquare = _token(ast.Tokens.close_square())
    let dot = _token(ast.Tokens.dot())
    let equals = _token(ast.Tokens.equals())
    let equal_arrow = _token(ast.Tokens.equal_arrow())
    let hash = _token(ast.Tokens.hash())
    let id = _token.identifier
    let kwd = _keyword.kwd
    let kwd_as = _keyword(ast.Keywords.kwd_as())
    let kwd_break = _keyword(ast.Keywords.kwd_break())
    let kwd_cap = _keyword.cap
    let kwd_compile_error = _keyword(ast.Keywords.kwd_compile_error())
    let kwd_compile_intrinsic = _keyword(ast.Keywords.kwd_compile_intrinsic())
    let kwd_consume = _keyword(ast.Keywords.kwd_consume())
    let kwd_continue = _keyword(ast.Keywords.kwd_continue())
    let kwd_do = _keyword(ast.Keywords.kwd_do())
    let kwd_else = _keyword(ast.Keywords.kwd_else())
    let kwd_elseif = _keyword(ast.Keywords.kwd_elseif())
    let kwd_embed = _keyword(ast.Keywords.kwd_embed())
    let kwd_end = _keyword(ast.Keywords.kwd_end())
    let kwd_error = _keyword(ast.Keywords.kwd_error())
    let kwd_for = _keyword(ast.Keywords.kwd_for())
    let kwd_if = _keyword(ast.Keywords.kwd_if())
    let kwd_ifdef = _keyword(ast.Keywords.kwd_ifdef())
    let kwd_iftype = _keyword(ast.Keywords.kwd_iftype())
    let kwd_in = _keyword(ast.Keywords.kwd_in())
    let kwd_is = _keyword(ast.Keywords.kwd_is())
    let kwd_let = _keyword(ast.Keywords.kwd_let())
    let kwd_loc = _keyword(ast.Keywords.kwd_loc())
    let kwd_match = _keyword(ast.Keywords.kwd_match())
    let kwd_object = _keyword(ast.Keywords.kwd_object())
    let kwd_recover = _keyword(ast.Keywords.kwd_recover())
    let kwd_repeat = _keyword(ast.Keywords.kwd_repeat())
    let kwd_return = _keyword(ast.Keywords.kwd_return())
    let kwd_then = _keyword(ast.Keywords.kwd_then())
    let kwd_this = _keyword(ast.Keywords.kwd_this())
    let kwd_try = _keyword(ast.Keywords.kwd_try())
    let kwd_until = _keyword(ast.Keywords.kwd_until())
    let kwd_var = _keyword(ast.Keywords.kwd_var())
    let kwd_where = _keyword(ast.Keywords.kwd_where())
    let kwd_while = _keyword(ast.Keywords.kwd_while())
    let kwd_with = _keyword(ast.Keywords.kwd_with())
    let literal = _literal.literal
    let literal_string = _literal.string
    let not_kwd = _keyword.not_kwd
    let ocurly = _token(ast.Tokens.open_curly())
    let oparen = _token(ast.Tokens.open_paren())
    let osquare = _token(ast.Tokens.open_square())
    let postfix_op = _operator.postfix_op
    let prefix_op = _operator.prefix_op
    let ques = _token(ast.Tokens.ques())
    let semicolon = _token(ast.Tokens.semicolon())
    let subtype = _token(ast.Tokens.subtype())
    let trivia = _trivia.trivia
    let type_args = _type_type.args
    let type_arrow = _type_type.arrow
    let type_params = _type_type.params

    // seq <= annotation? item (';'? item)*
    let seq_ann = Variable("seq_ann")
    let seq_body = Variable("seq_body")
    seq.set_body(
      Conj(
        [ Bind(seq_ann, Ques(annotation))
          Bind(
            seq_body,
            Conj(
              [ item
                Star(Conj([ Ques(semicolon); item ]))
              ]))
        ],
        _ExpActions~_seq(seq_ann, seq_body)))

    // item <= assignment / jump / infix
    item.set_body(
      Disj(
        [ exp_assignment
          exp_jump
          infix ]))

    // assignment <= (infix '=' assignment) / infix
    let ass_lhs = Variable("ass_lhs")
    let ass_op = Variable("ass_op")
    let ass_rhs = Variable("ass_rhs")
    exp_assignment.set_body(
      Disj(
        [ Conj(
            [ Bind(ass_lhs, infix)
              Bind(ass_op, equals)
              Bind(ass_rhs, exp_assignment) ],
            _ExpActions~_binop(ass_lhs, ass_op, ass_rhs, None))
        infix ]))

    // jump <= ('return' / 'break') assignment? /
    //         'continue' /
    //         'error' /
    //         'compile_intrinsic' /
    //         'compile_error'
    let jump_keyword = Variable("jump_keyword")
    let jump_rhs = Variable("jump_rhs")
    exp_jump.set_body(
      Disj(
        [ Conj(
            [ Disj(
                [ Bind(jump_keyword, kwd_return)
                  Bind(jump_keyword, kwd_break) ])
              Ques(Bind(jump_rhs, exp_assignment)) ])
          Bind(jump_keyword, kwd_continue)
          Bind(jump_keyword, kwd_error)
          Bind(jump_keyword, kwd_compile_intrinsic)
          Bind(jump_keyword, kwd_compile_error) ],
        _ExpActions~_jump(jump_keyword, jump_rhs)))

    // infix <= (term binary_op infix) / (term 'as' type) / term
    let infix_lhs = Variable("infix_lhs")
    let infix_op = Variable("infix_op")
    let infix_partial = Variable("infix_partial")
    let infix_rhs = Variable("infix_rhs")
    infix.set_body(
      Disj(
        [ Disj(
            [ Conj(
                [ Bind(infix_lhs, exp_term)
                  Bind(infix_op, kwd_as)
                  Bind(infix_rhs, type_arrow) ])
              Conj(
                [ Bind(infix_lhs, exp_term)
                  Bind(infix_op, kwd_is)
                  Bind(infix_rhs, infix) ])
              Conj(
                [ Bind(infix_lhs, exp_term)
                  Bind(infix_op, binary_op)
                  Ques(Bind(infix_partial, ques))
                  Bind(infix_rhs, infix) ])
            ],
            _ExpActions~_binop(
              infix_lhs, infix_op, infix_rhs, infix_partial))
          exp_term ]))

    // term <= if / ifdef / iftype / match / while / repeate / for / with /
    //         try / recover / consume / decl / prefix / hash
    exp_term.set_body(
      Disj(
        [ exp_if
          exp_ifdef
          exp_iftype
          exp_match
          exp_while
          exp_repeat
          exp_for
          exp_with
          exp_try
          exp_recover
          exp_consume
          exp_decl
          exp_prefix
          exp_hash
        ]))

    // if <= 'if' cond ('elseif' cond)* ('else' seq)? 'end'
    let if_firstif = Variable("if_firstif")
    let if_elseifs = Variable("if_elseifs")
    let if_else_block = Variable("if_else_block")
    exp_if.set_body(
      Conj(
        [ kwd_if
          Bind(if_firstif, exp_cond)
          Bind(if_elseifs, Star(Conj([ kwd_elseif; exp_cond ])))
          Ques(Conj([ kwd_else; Bind(if_else_block, seq) ]))
          kwd_end ],
        _ExpActions~_if(if_firstif, if_elseifs, if_else_block)))

    // cond <= seq 'then' seq
    let cond_if_true = Variable("cond_if_true")
    let cond_then_block = Variable("cond_then_block")
    exp_cond.set_body(
      Conj(
        [ Bind(cond_if_true, seq)
          kwd_then
          Bind(cond_then_block, seq) ],
        _ExpActions~_ifcond(cond_if_true, cond_then_block)))

    // ifdef <= 'ifdef' cond ('elseif' cond)* ('else' seq)? 'end'
    let ifdef_firstif = Variable("ifdef_firstif")
    let ifdef_elseifs = Variable("ifdef_elseifs")
    let ifdef_else_block = Variable("ifdef_else_block")
    exp_ifdef.set_body(
      Conj(
        [ kwd_ifdef
          Bind(ifdef_firstif, exp_cond)
          Bind(
            ifdef_elseifs,
            Star(
              Conj(
                [ kwd_elseif
                  exp_cond ])))
          Ques(
            Conj(
              [ kwd_else
                Bind(ifdef_else_block, seq) ]))
          kwd_end ],
        _ExpActions~_ifdef(ifdef_firstif, ifdef_elseifs, ifdef_else_block)))

    // iftype <= 'iftype' type '<:' type 'then' seq ('elseif' type '<:' type)*
    //           ('else' seq)? 'end'
    let iftype_firstif = Variable("iftype_firstif")
    let iftype_if_true = Variable("iftype_if_true")
    let iftype_lhs = Variable("iftype_lhs")
    let iftype_op = Variable("iftype_op")
    let iftype_rhs = Variable("iftype_rhs")
    let iftype_then_block = Variable("iftype_then_block")
    let iftype_elseifs = Variable("iftype_elseifs")
    let iftype_else_block = Variable("iftype_else_block")
    exp_iftype.set_body(
      Conj(
        [ kwd_iftype
          Bind(
            iftype_firstif,
            Conj(
              [ Bind(iftype_if_true,
                  Conj(
                    [ Bind(iftype_lhs, type_arrow)
                      Bind(iftype_op, subtype)
                      Bind(iftype_rhs, type_arrow) ]))
                kwd_then
                Bind(iftype_then_block, seq) ],
              _ExpActions~_iftype_cond(
                iftype_if_true,
                iftype_lhs,
                iftype_op,
                iftype_rhs,
                iftype_then_block)))
          Bind(
            iftype_elseifs,
            Star(
              Conj(
                [ kwd_elseif
                  Bind(iftype_if_true,
                    Conj(
                      [ Bind(iftype_lhs, type_arrow)
                        Bind(iftype_op, subtype)
                        Bind(iftype_rhs, type_arrow) ]))
                  kwd_then
                  Bind(iftype_then_block, seq) ],
                _ExpActions~_iftype_cond(
                  iftype_if_true,
                  iftype_lhs,
                  iftype_op,
                  iftype_rhs,
                  iftype_then_block))))
          Ques(
            Conj(
              [ kwd_else
                Bind(iftype_else_block, seq) ]))
          kwd_end ],
        _ExpActions~_iftype(
          iftype_firstif, iftype_elseifs, iftype_else_block)))

    // match <= 'match' match_case+ ('else' seq)? 'end'
    let match_exp = Variable("match_exp")
    let match_cases = Variable("match_cases")
    let match_else_block = Variable("match_else_block")
    exp_match.set_body(
      Conj(
        [ kwd_match
          Bind(match_exp, seq)
          Bind(match_cases, Plus(match_case))
          Ques(Conj([ kwd_else; Bind(match_else_block, seq) ]))
          kwd_end ]),
      _ExpActions~_match(match_exp, match_cases, match_else_block))

    // match_pattern <= '|' item ('if' seq)?
    let match_pattern_pattern = Variable("match_pattern_pattern")
    let match_pattern_condition = Variable("match_pattern_condition")
    match_pattern.set_body(
      Conj(
        [ bar
          Bind(
            match_pattern_pattern,
            Disj([ exp_decl; exp_prefix; exp_hash ]))
          Ques(Conj([ kwd_if; Bind(match_pattern_condition, seq) ]))
        ]),
      _ExpActions~_match_pattern(
        match_pattern_pattern, match_pattern_condition))

    // match_case <= match_case_pattern+ '=>' seq
    let match_case_patterns = Variable("match_case_patterns")
    let match_case_body = Variable("match_case_body")
    match_case.set_body(
      Conj(
        [ Bind(match_case_patterns, Star(match_pattern, 1))
          equal_arrow
          Bind(match_case_body, seq)
        ]),
      _ExpActions~_match_case(match_case_patterns, match_case_body))

    // while <= 'while' seq 'do' seq ('else' seq)? 'end'
    let while_cond = Variable("while_cond")
    let while_body = Variable("while_body")
    let while_else_block = Variable("while_else_block")
    exp_while.set_body(
      Conj(
        [ kwd_while
          Bind(while_cond, seq)
          kwd_do
          Bind(while_body, seq)
          Ques(Conj([ kwd_else; Bind(while_else_block, seq) ]))
          kwd_end ]),
      _ExpActions~_while(while_cond, while_body, while_else_block))

    // repeat <= 'repeat' seq 'until' seq ('else' seq)? 'end'
    let repeat_body = Variable("repeat_body")
    let repeat_cond = Variable("repeat_cond")
    let repeat_else_block = Variable("repeat_else_block")
    exp_repeat.set_body(
      Conj(
        [ kwd_repeat
          Bind(repeat_body, seq)
          kwd_until
          Bind(repeat_cond, seq)
          Ques(Conj([ kwd_else; Bind(repeat_else_block, seq) ]))
          kwd_end ]),
      _ExpActions~_repeat(repeat_body, repeat_cond, repeat_else_block))

    // for <= 'for' (id / '(' id (',' id)* ')') 'in' seq 'do'
    //   seq ('else' seq)?
    // 'end'
    let for_ids = Variable("for_ids")
    let for_seq = Variable("for_seq")
    let for_body = Variable("for_body")
    let for_else_block = Variable("for_else_block")
    exp_for.set_body(
      Conj(
        [ kwd_for
          Bind(for_ids, tuple_pattern)
          kwd_in
          Bind(for_seq, seq)
          kwd_do
          Bind(for_body, seq)
          Ques(Conj([ kwd_else; Bind(for_else_block, seq)]))
          kwd_end ]),
      _ExpActions~_for(for_ids, for_seq, for_body, for_else_block))

    // tuple_pattern <= id / '(' tuple_pattern (',' tuple_pattern)* ')'
    tuple_pattern.set_body(
      Disj(
        [ id
          Conj(
            [ oparen
              tuple_pattern
              Star(Conj([ comma; tuple_pattern ]))
              cparen ]) ]),
      _ExpActions~_tuple_pattern())

    // with <= 'with' with_elem (',' with_elem)*
    //         'do' seq ('else' seq)? 'end'
    let with_elems = Variable("with_elems")
    let with_body = Variable("with_body")
    exp_with.set_body(
      Conj(
        [ kwd_with
          Bind(with_elems,
            Conj([ with_elem; Star(Conj([ comma; with_elem ])) ]))
          kwd_do
          Bind(with_body, seq)
          kwd_end ]),
      _ExpActions~_with(with_elems, with_body))

    // with_elem <= (id / ('(' id (',' id)*)) '=' seq
    let with_elem_pattern = Variable("with_elem_pattern")
    let with_elem_body = Variable("with_elem_body")
    with_elem.set_body(
      Conj(
        [ Bind(with_elem_pattern, tuple_pattern)
          equals
          Bind(with_elem_body, seq) ]),
      _ExpActions~_with_elem(with_elem_pattern, with_elem_body))

    // try <= 'try' seq ('else' seq)? 'end'
    let try_body = Variable("try_body")
    let try_else_block = Variable("try_else_block")
    exp_try.set_body(
      Conj(
        [ kwd_try
          Bind(try_body, Ques(seq))
          Ques(Conj([ kwd_else; Bind(try_else_block, seq) ]))
          kwd_end ]),
      _ExpActions~_try(try_body, try_else_block))

    // recover <= 'recover' cap? seq 'end'
    let recover_cap = Variable("recover_cap")
    let recover_body = Variable("recover_body")
    exp_recover.set_body(
      Conj(
        [ kwd_recover
          Ques(Bind(recover_cap, kwd_cap))
          Bind(recover_body, seq)
          kwd_end ]),
      _ExpActions~_recover(recover_cap, recover_body))

    // consume <= 'consume' cap? term
    let consume_cap = Variable("consume_cap")
    let consume_body = Variable("consume_body")
    exp_consume.set_body(
      Conj(
        [ kwd_consume
          Ques(Bind(consume_cap, kwd_cap))
          Bind(consume_body, exp_term) ]),
      _ExpActions~_consume(consume_cap, consume_body))

    // decl <= ('var' / 'let' / 'embed') id (':' type_type)?
    let decl_kind = Variable("decl_kind")
    let decl_identifier = Variable("decl_identifier")
    let decl_type = Variable("decl_type")
    exp_decl.set_body(
      Conj(
        [ Bind(decl_kind, Disj([ kwd_var; kwd_let; kwd_embed ]))
          Bind(decl_identifier, id)
          Ques(
            Conj(
              [ colon
                Bind(decl_type, type_arrow) ])) ]),
      _ExpActions~_decl(decl_kind, decl_identifier, decl_type))

    // prefix <= (prefix_op prefix) / postfix
    let prefix_opv = Variable("prefix_opv")
    let prefix_rhs = Variable("prefix_rhs")
    exp_prefix.set_body(
      Disj(
        [ Conj(
            [ Bind(prefix_opv, prefix_op)
              Bind(prefix_rhs, exp_prefix) ],
            _ExpActions~_prefix(prefix_opv, prefix_rhs))
          exp_postfix ]))

    // exp_hash <= '#' exp_postfix
    let hash_rhs = Variable("hash_rhs")
    exp_hash.set_body(
      Conj(
        [ hash
          Bind(hash_rhs, exp_postfix) ]),
        _ExpActions~_hash(hash_rhs))

    // postfix <= (postfix postfix_op identifier) /
    //            (postfix type_args) /
    //            (postfix call_args) /
    //            atom
    let postfix_lhs = Variable("postfix_lhs")
    let postfix_opv = Variable("postfix_opv")
    let postfix_rhs = Variable("postfix_rhs")
    let postfix_args = Variable("postfix_type_args")
    let postfix_partial = Variable("postfix_partial")
    exp_postfix.set_body(
      Disj(
        [ Conj(
            [ Bind(postfix_lhs, exp_postfix)
              Bind(postfix_opv, postfix_op)
              Bind(postfix_rhs, id) ],
            _ExpActions~_binop(postfix_lhs, postfix_opv, postfix_rhs, None))
          Conj(
            [ Bind(postfix_lhs, exp_postfix)
              Bind(postfix_args, type_args) ],
            _ExpActions~_postfix_type_args(postfix_lhs, postfix_args))
          Conj(
            [ Bind(postfix_lhs, exp_postfix)
              Bind(postfix_args, call_args)
              Ques(Bind(postfix_partial, ques)) ],
            _ExpActions~_postfix_call_args(
              postfix_lhs, postfix_args, postfix_partial))
          exp_atom ]))

    // atom <= tuple / parens / array / ffi / bare_lambda / lambda /
    //         object / '__loc' / 'this' / literal / (~keyword identifier)
    let atom_body = Variable("atom_body")
    exp_atom.set_body(
      Disj(
        [ exp_tuple
          exp_parens
          exp_array
          exp_lambda
          exp_ffi
          exp_object
          Conj(
            [ Bind(atom_body,
                Disj([ kwd_loc; kwd_this; literal; Conj([ not_kwd; id ]) ])) ],
            _ExpActions~_atom(atom_body))
        ]))

    // call_args <= '(' call_args_pos? call_args_named? ')'
    let call_args_posv = Variable("call_args_posv")
    let call_args_namedv = Variable("call_args_named")
    call_args.set_body(
      Conj(
        [ oparen
          Ques(Bind(call_args_posv, call_args_pos))
          Ques(Bind(call_args_namedv, call_args_named))
          cparen ]),
      _ExpActions~_call_args(call_args_posv, call_args_namedv))

    // call_args_pos <= seq (',' seq)*
    call_args_pos.set_body(
      Conj([ seq; Star(Conj([ comma; seq ])) ]))

    // call_args_named <= 'where' call_arg_named
    //                    (',' call_arg_named)*
    call_args_named.set_body(
      Conj(
        [ kwd_where
          call_arg_named
          Star(
            Conj(
              [ comma
                call_arg_named ])) ]))

    // call_arg_named <= identifier '=' seq
    let call_arg_name = Variable("call_arg_named_name")
    let call_arg_op = Variable("call_arg_named_op")
    let call_arg_rhs = Variable("call_arg_named_rhs")
    call_arg_named.set_body(
      Conj(
        [ Bind(call_arg_name, id)
          Bind(call_arg_op, equals)
          Bind(call_arg_rhs, seq) ]),
      _ExpActions~_binop(
          call_arg_name, call_arg_op, call_arg_rhs, None))

    // tuple <= '(' seq (',' seq)+ ')'
    let tuple_seqs = Variable("tuple_seqs")
    exp_tuple.set_body(
      Conj(
        [ oparen
          Bind(tuple_seqs, Conj([ seq; Plus(Conj([ comma; seq ])) ]))
          cparen ]),
      _ExpActions~_tuple(tuple_seqs))

    // parens <= '(' seq ')'
    let parens_body = Variable("parens_body")
    exp_parens.set_body(
      Conj(
        [ oparen
          Bind(parens_body, seq)
          cparen ]),
      _ExpActions~_atom(parens_body))

    // lambda
    let lambda_bare = Variable("lambda_bare")
    let lambda_ann = Variable("lambda_ann")
    let lambda_this_cap = Variable("lambda_this_cap")
    let lambda_id = Variable("lambda_id")
    let lambda_type_params = Variable("lambda_type_params")
    let lambda_params = Variable("lambda_params")
    let lambda_captures = Variable("lambda_captures")
    let lambda_ret_type = Variable("lambda_ret_type")
    let lambda_partial = Variable("lambda_partial")
    let lambda_body = Variable("lambda_body")
    let lambda_ref_cap = Variable("lambda_ref_cap")
    exp_lambda.set_body(
      Conj(
        [ Ques(Bind(lambda_bare, at))
          ocurly
          Ques(Bind(lambda_ann, annotation))
          Ques(Bind(lambda_this_cap, kwd_cap))
          Ques(Bind(lambda_id, id))
          Ques(Bind(lambda_type_params, type_params))
          oparen
          Ques(Bind(lambda_params, _method_params))
          cparen
          Ques(Conj([ oparen; Bind(lambda_captures, _method_params); cparen ]))
          Ques(Conj([ colon; Bind(lambda_ret_type, type_arrow) ]))
          Ques(Bind(lambda_partial, ques))
          equal_arrow
          Bind(lambda_body, seq)
          ccurly
          Ques(Bind(lambda_ref_cap, kwd_cap)) ]),
      _ExpActions~_lambda(
        lambda_bare,
        lambda_ann,
        lambda_this_cap,
        lambda_id,
        lambda_type_params,
        lambda_params,
        lambda_captures,
        lambda_ret_type,
        lambda_partial,
        lambda_body,
        lambda_ref_cap))

    // array <= '[' ('as' type_arrow ':') seq ']'
    let array_type = Variable("array_type")
    let array_body = Variable("array_body")
    exp_array.set_body(
      Conj(
        [ osquare
          Ques(Conj([ kwd_as; Bind(array_type, type_arrow); colon ]))
          Ques(Bind(array_body, seq))
          csquare ]),
      _ExpActions~_array(array_type, array_body))

    // ffi <= '@' (id / string) type_args? call_args '?'?
    let ffi_identifier = Variable("ffi_identifier")
    let ffi_type_args = Variable("ffi_type_args")
    let ffi_call_args = Variable("ffi_call_args")
    let ffi_partial = Variable("ffi_partial")
    exp_ffi.set_body(
      Conj(
        [ at
          Bind(ffi_identifier, Disj([ id; literal_string ]))
          Bind(ffi_type_args, Ques(type_args))
          Bind(ffi_call_args, call_args)
          Bind(ffi_partial, Ques(ques)) ]),
      _ExpActions~_ffi(
        ffi_identifier, ffi_type_args, ffi_call_args, ffi_partial))

    // object <=
    let obj_ann = Variable("obj_ann")
    let obj_cap = Variable("obj_cap")
    let obj_type = Variable("obj_type")
    let obj_members = Variable("obj_members")
    exp_object.set_body(
      Conj(
        [ kwd_object
          Ques(Bind(obj_ann, annotation))
          Ques(Bind(obj_cap, kwd_cap))
          Ques(Conj([ kwd_is; Bind(obj_type, type_arrow) ]))
          Bind(obj_members, _typedef_members)
          Disj(
            [ kwd_end
              Error(ErrorMsg.exp_object_unterminated())
            ])
        ]),
      _ExpActions~_object(obj_ann, obj_cap, obj_type, obj_members))