mago-syntax 1.20.1

A correct, fast, and memory-efficient PHP syntax implementation, including Lexer, Parser, AST, and utilities for Mago.
Documentation
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
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
mod runner {
    use std::borrow::Cow;

    use bumpalo::Bump;

    use mago_database::file::File;

    use mago_syntax::ast::*;
    use mago_syntax::parser::parse_file;

    pub fn smoke_test(name: &'static str, code: &'static str) {
        let arena = Bump::new();
        let file = File::ephemeral(Cow::Borrowed(name), Cow::Borrowed(code));
        let program = parse_file(&arena, &file);
        if !program.errors.is_empty() {
            panic!("Test case '{name}' failed to parse. Errors: {:?}", program.errors);
        }
    }

    pub fn run_expression_test(name: &'static str, expression: &'static str, expected: &'static str) {
        fn format_variable(var: &Variable<'_>) -> String {
            match var {
                Variable::Direct(direct_variable) => direct_variable.name.to_string(),
                Variable::Indirect(indirect_variable) => {
                    format!("${{{}}}", format_expression(indirect_variable.expression))
                }
                Variable::Nested(nested_variable) => {
                    format!("${}", format_variable(nested_variable.variable))
                }
            }
        }

        fn format_member_selector(selector: &ClassLikeMemberSelector) -> String {
            match selector {
                ClassLikeMemberSelector::Identifier(identifier) => identifier.value.to_string(),
                ClassLikeMemberSelector::Variable(variable) => format_variable(variable),
                ClassLikeMemberSelector::Expression(s) => {
                    format!("{{{}}}", format_expression(s.expression))
                }
                ClassLikeMemberSelector::Missing(_) => "<missing>".to_string(),
            }
        }

        fn format_constant_selector(selector: &ClassLikeConstantSelector) -> String {
            match selector {
                ClassLikeConstantSelector::Identifier(local_identifier) => local_identifier.value.to_string(),
                ClassLikeConstantSelector::Expression(s) => {
                    format!("{{{}}}", format_expression(s.expression))
                }
                ClassLikeConstantSelector::Missing(_) => "<missing>".to_string(),
            }
        }

        fn format_expression(expr: &Expression<'_>) -> String {
            match expr {
                Expression::Parenthesized(parenthesized) => format_expression(parenthesized.expression),
                Expression::Variable(variable) => format_variable(variable),
                Expression::Binary(binary) => {
                    format!(
                        "({} {} {})",
                        format_expression(binary.lhs),
                        binary.operator.as_str(),
                        format_expression(binary.rhs)
                    )
                }
                Expression::UnaryPrefix(unary_prefix) => {
                    format!("({} {})", unary_prefix.operator.as_str(), format_expression(unary_prefix.operand))
                }
                Expression::UnaryPostfix(unary_postfix) => {
                    format!("({} {})", format_expression(unary_postfix.operand), unary_postfix.operator.as_str())
                }
                Expression::Literal(literal) => match literal {
                    Literal::String(s) => s.raw.to_string(),
                    Literal::Integer(i) => i.raw.to_string(),
                    Literal::Float(f) => f.raw.to_string(),
                    Literal::True(_) => "true".to_string(),
                    Literal::False(_) => "false".to_string(),
                    Literal::Null(_) => "null".to_string(),
                },
                Expression::Assignment(assignment) => {
                    format!(
                        "({} {} {})",
                        format_expression(assignment.lhs),
                        assignment.operator.as_str(),
                        format_expression(assignment.rhs)
                    )
                }
                Expression::Conditional(conditional) => match conditional.then {
                    Some(then) => format!(
                        "( {} ? {} : {} )",
                        format_expression(conditional.condition),
                        format_expression(then),
                        format_expression(conditional.r#else)
                    ),
                    None => format!(
                        "({} ?: {})",
                        format_expression(conditional.condition),
                        format_expression(conditional.r#else)
                    ),
                },
                Expression::ConstantAccess(ConstantAccess { name }) => name.value().to_string(),
                Expression::Identifier(identifier) => identifier.value().to_string(),
                Expression::Construct(Construct::Print(construct)) => {
                    format!("(print {})", format_expression(construct.value))
                }
                Expression::Yield(yield_expr) => match yield_expr {
                    Yield::Value(yield_value) => match yield_value.value {
                        Some(value) => format!("(yield {})", format_expression(value)),
                        None => "yield".to_string(),
                    },
                    Yield::Pair(yield_pair) => format!(
                        "(yield {} => {})",
                        format_expression(yield_pair.key),
                        format_expression(yield_pair.value)
                    ),
                    Yield::From(yield_from) => format!("(yield from {})", format_expression(yield_from.iterator)),
                },
                Expression::Instantiation(instantiation) => {
                    format!("(new {})", format_expression(instantiation.class))
                }
                Expression::Clone(clone) => {
                    format!("(clone {})", format_expression(clone.object))
                }
                Expression::Throw(throw) => {
                    format!("(throw {})", format_expression(throw.exception))
                }
                Expression::Construct(Construct::Require(require_construct)) => {
                    format!("(require {})", format_expression(require_construct.value))
                }
                Expression::Construct(Construct::RequireOnce(require_once_construct)) => {
                    format!("(require_once {})", format_expression(require_once_construct.value))
                }
                Expression::Construct(Construct::Include(include_construct)) => {
                    format!("(include {})", format_expression(include_construct.value))
                }
                Expression::Construct(Construct::IncludeOnce(include_once_construct)) => {
                    format!("(include_once {})", format_expression(include_once_construct.value))
                }
                Expression::Call(call) => match call {
                    Call::Function(function_call) => format!("({}())", format_expression(function_call.function)),
                    Call::Method(method_call) => {
                        format!(
                            "({}->{}())",
                            format_expression(method_call.object),
                            format_member_selector(&method_call.method),
                        )
                    }
                    Call::NullSafeMethod(null_safe_method_call) => {
                        format!(
                            "({}?->{}())",
                            format_expression(null_safe_method_call.object),
                            format_member_selector(&null_safe_method_call.method),
                        )
                    }
                    Call::StaticMethod(static_method_call) => {
                        format!(
                            "({}::{}())",
                            format_expression(static_method_call.class),
                            format_member_selector(&static_method_call.method),
                        )
                    }
                },
                Expression::Access(access) => match access {
                    Access::Property(property_access) => {
                        format!(
                            "({}->{})",
                            format_expression(property_access.object),
                            format_member_selector(&property_access.property),
                        )
                    }
                    Access::NullSafeProperty(null_safe_property_access) => {
                        format!(
                            "({}?->{})",
                            format_expression(null_safe_property_access.object),
                            format_member_selector(&null_safe_property_access.property),
                        )
                    }
                    Access::StaticProperty(static_property_access) => {
                        format!(
                            "({}::{})",
                            format_expression(static_property_access.class),
                            format_variable(&static_property_access.property),
                        )
                    }
                    Access::ClassConstant(class_constant_access) => {
                        format!(
                            "({}::{})",
                            format_expression(class_constant_access.class),
                            format_constant_selector(&class_constant_access.constant),
                        )
                    }
                },
                Expression::Error(_) => "<error>".to_string(),
                _ => {
                    let expression_kind = Node::Expression(expr)
                        .children()
                        .first()
                        .map_or_else(|| "<unknown>".to_string(), |t| t.kind().to_string());

                    panic!("unsupported expression kind for formatting: {expression_kind}");
                }
            }
        }

        let code = format!("<?php {expression};");
        let arena = Bump::new();
        let file = File::ephemeral(Cow::Borrowed(name), Cow::Owned(code));

        let program = parse_file(&arena, &file);
        if !program.errors.is_empty() {
            panic!("Test case '{name}' failed to parse. Errors: {:?}", program.errors);
        }

        let statement = program.statements.get(1).expect("Expected an expression statement here");
        let Statement::Expression(expression) = statement else {
            panic!("Expected an expression statement, found `{statement:#?}`");
        };

        let formatted_ast = format_expression(expression.expression);

        assert_eq!(formatted_ast, expected, "Test case '{name}' failed. Expression does not match expected output.");
    }
}

mod parser {
    macro_rules! test_expression {
        ($name:ident, $expression:expr, $expected:expr) => {
            #[test]
            fn $name() {
                crate::runner::run_expression_test(stringify!($name), $expression, $expected);
            }
        };
    }

    macro_rules! smoke_test {
        ($name:ident, $expression:expr) => {
            #[test]
            fn $name() {
                crate::runner::smoke_test(stringify!($name), $expression);
            }
        };
    }

    test_expression!(assign_ref_static_call, "$a = &B::c()", "($a = (& (B::c())))");
    test_expression!(assign_ref_func_call, "$a = &b()", "($a = (& (b())))");
    test_expression!(assign_ref_method_call, "$a = &$b->c()", "($a = (& ($b->c())))");
    test_expression!(assign_ref_null_method_call, "$a = &$b?->c()", "($a = (& ($b?->c())))");
    test_expression!(unary_minus_vs_mul, "$a = -$b * $c", "($a = ((- $b) * $c))");
    test_expression!(unary_minus_vs_add, "$a = -$b + $c", "($a = ((- $b) + $c))");
    test_expression!(unary_minus_vs_div, "$a = -$b / $c", "($a = ((- $b) / $c))");
    test_expression!(unary_minus_vs_sub, "$a = -$b - $c", "($a = ((- $b) - $c))");
    test_expression!(unary_minus_vs_mod, "$a = -$b % $c", "($a = ((- $b) % $c))");
    test_expression!(unary_minus_vs_pow, "$a = -$b ** $c", "($a = (- ($b ** $c)))");
    test_expression!(unary_minus_vs_shift_left, "$a = -$b << $c", "($a = ((- $b) << $c))");
    test_expression!(unary_minus_vs_shift_right, "$a = -$b >> $c", "($a = ((- $b) >> $c))");
    test_expression!(unary_minus_vs_bitwise_and, "$a = -$b & $c", "($a = ((- $b) & $c))");
    test_expression!(unary_minus_vs_bitwise_or, "$a = -$b | $c", "($a = ((- $b) | $c))");
    test_expression!(unary_minus_vs_bitwise_xor, "$a = -$b ^ $c", "($a = ((- $b) ^ $c))");
    test_expression!(unary_minus_vs_less_than, "$a = -$b < $c", "($a = ((- $b) < $c))");
    test_expression!(unary_minus_vs_less_than_equal, "$a = -$b <= $c", "($a = ((- $b) <= $c))");
    test_expression!(unary_minus_vs_greater_than, "$a = -$b > $c", "($a = ((- $b) > $c))");
    test_expression!(unary_minus_vs_greater_than_equal, "$a = -$b >= $c", "($a = ((- $b) >= $c))");
    test_expression!(unary_minus_vs_equal, "$a = -$b == $c", "($a = ((- $b) == $c))");
    test_expression!(unary_minus_vs_identical, "$a = -$b === $c", "($a = ((- $b) === $c))");
    test_expression!(unary_minus_vs_not_equal, "$a = -$b != $c", "($a = ((- $b) != $c))");
    test_expression!(unary_minus_vs_not_identical, "$a = -$b !== $c", "($a = ((- $b) !== $c))");
    test_expression!(unary_minus_vs_spaceship, "$a = -$b <=> $c", "($a = ((- $b) <=> $c))");
    test_expression!(unary_minus_vs_coalesce, "$a = -$b ?? $c", "($a = ((- $b) ?? $c))");
    test_expression!(unary_minus_vs_logical_and_word, "$a = -$b and $c", "(($a = (- $b)) and $c)");
    test_expression!(unary_minus_vs_logical_or_word, "$a = -$b or $c", "(($a = (- $b)) or $c)");
    test_expression!(unary_minus_vs_logical_xor_word, "$a = -$b xor $c", "(($a = (- $b)) xor $c)");
    test_expression!(unary_minus_vs_logical_and_op, "$a = -$b && $c", "($a = ((- $b) && $c))");
    test_expression!(unary_minus_vs_logical_or_op, "$a = -$b || $c", "($a = ((- $b) || $c))");
    test_expression!(unary_minus_vs_ternary, "$a = -$b ? $c : $d", "($a = ( (- $b) ? $c : $d ))");
    test_expression!(error_control_vs_mul, "$a = @$b * $c", "($a = ((@ $b) * $c))");
    test_expression!(error_control_vs_add, "$a = @$b + $c", "($a = ((@ $b) + $c))");
    test_expression!(error_control_vs_div, "$a = @$b / $c", "($a = ((@ $b) / $c))");
    test_expression!(error_control_vs_sub, "$a = @$b - $c", "($a = ((@ $b) - $c))");
    test_expression!(error_control_vs_mod, "$a = @$b % $c", "($a = ((@ $b) % $c))");
    test_expression!(error_control_vs_pow, "$a = @$b ** $c", "($a = (@ ($b ** $c)))");
    test_expression!(error_control_vs_shift_left, "$a = @$b << $c", "($a = ((@ $b) << $c))");
    test_expression!(error_control_vs_shift_right, "$a = @$b >> $c", "($a = ((@ $b) >> $c))");
    test_expression!(error_control_vs_bitwise_and, "$a = @$b & $c", "($a = ((@ $b) & $c))");
    test_expression!(error_control_vs_bitwise_or, "$a = @$b | $c", "($a = ((@ $b) | $c))");
    test_expression!(error_control_vs_bitwise_xor, "$a = @$b ^ $c", "($a = ((@ $b) ^ $c))");
    test_expression!(error_control_vs_less_than, "$a = @$b < $c", "($a = ((@ $b) < $c))");
    test_expression!(error_control_vs_less_than_equal, "$a = @$b <= $c", "($a = ((@ $b) <= $c))");
    test_expression!(error_control_vs_greater_than, "$a = @$b > $c", "($a = ((@ $b) > $c))");
    test_expression!(error_control_vs_greater_than_equal, "$a = @$b >= $c", "($a = ((@ $b) >= $c))");
    test_expression!(error_control_vs_equal, "$a = @$b == $c", "($a = ((@ $b) == $c))");
    test_expression!(error_control_vs_identical, "$a = @$b === $c", "($a = ((@ $b) === $c))");
    test_expression!(error_control_vs_not_equal, "$a = @$b != $c", "($a = ((@ $b) != $c))");
    test_expression!(error_control_vs_not_identical, "$a = @$b !== $c", "($a = ((@ $b) !== $c))");
    test_expression!(error_control_vs_spaceship, "$a = @$b <=> $c", "($a = ((@ $b) <=> $c))");
    test_expression!(error_control_vs_coalesce, "$a = @$b ?? $c", "($a = ((@ $b) ?? $c))");
    test_expression!(error_control_vs_logical_and_word, "$a = @$b and $c", "(($a = (@ $b)) and $c)");
    test_expression!(error_control_vs_logical_or_word, "$a = @$b or $c", "(($a = (@ $b)) or $c)");
    test_expression!(error_control_vs_logical_xor_word, "$a = @$b xor $c", "(($a = (@ $b)) xor $c)");
    test_expression!(error_control_vs_logical_and_op, "$a = @$b && $c", "($a = ((@ $b) && $c))");
    test_expression!(error_control_vs_logical_or_op, "$a = @$b || $c", "($a = ((@ $b) || $c))");
    test_expression!(error_control_vs_ternary, "$a = @$b ? $c : $d", "($a = ( (@ $b) ? $c : $d ))");
    test_expression!(by_ref_vs_mul, "$a = &$b * $c", "(($a = (& $b)) * $c)");
    test_expression!(by_ref_vs_add, "$a = &$b + $c", "(($a = (& $b)) + $c)");
    test_expression!(by_ref_vs_div, "$a = &$b / $c", "(($a = (& $b)) / $c)");
    test_expression!(by_ref_vs_sub, "$a = &$b - $c", "(($a = (& $b)) - $c)");
    test_expression!(by_ref_vs_mod, "$a = &$b % $c", "(($a = (& $b)) % $c)");
    test_expression!(by_ref_vs_pow, "$a = &$b ** $c", "(($a = (& $b)) ** $c)");
    test_expression!(by_ref_vs_shift_left, "$a = &$b << $c", "(($a = (& $b)) << $c)");
    test_expression!(by_ref_vs_shift_right, "$a = &$b >> $c", "(($a = (& $b)) >> $c)");
    test_expression!(by_ref_vs_bitwise_and, "$a = &$b & $c", "(($a = (& $b)) & $c)");
    test_expression!(by_ref_vs_bitwise_or, "$a = &$b | $c", "(($a = (& $b)) | $c)");
    test_expression!(by_ref_vs_bitwise_xor, "$a = &$b ^ $c", "(($a = (& $b)) ^ $c)");
    test_expression!(by_ref_vs_less_than, "$a = &$b < $c", "(($a = (& $b)) < $c)");
    test_expression!(by_ref_vs_less_than_equal, "$a = &$b <= $c", "(($a = (& $b)) <= $c)");
    test_expression!(by_ref_vs_greater_than, "$a = &$b > $c", "(($a = (& $b)) > $c)");
    test_expression!(by_ref_vs_greater_than_equal, "$a = &$b >= $c", "(($a = (& $b)) >= $c)");
    test_expression!(by_ref_vs_equal, "$a = &$b == $c", "(($a = (& $b)) == $c)");
    test_expression!(by_ref_vs_identical, "$a = &$b === $c", "(($a = (& $b)) === $c)");
    test_expression!(by_ref_vs_not_equal, "$a = &$b != $c", "(($a = (& $b)) != $c)");
    test_expression!(by_ref_vs_not_identical, "$a = &$b !== $c", "(($a = (& $b)) !== $c)");
    test_expression!(by_ref_vs_spaceship, "$a = &$b <=> $c", "(($a = (& $b)) <=> $c)");
    test_expression!(by_ref_vs_coalesce, "$a = &$b ?? $c", "(($a = (& $b)) ?? $c)");
    test_expression!(by_ref_vs_logical_and_word, "$a = &$b and $c", "(($a = (& $b)) and $c)");
    test_expression!(by_ref_vs_logical_or_word, "$a = &$b or $c", "(($a = (& $b)) or $c)");
    test_expression!(by_ref_vs_logical_xor_word, "$a = &$b xor $c", "(($a = (& $b)) xor $c)");
    test_expression!(by_ref_vs_logical_and_op, "$a = &$b && $c", "(($a = (& $b)) && $c)");
    test_expression!(by_ref_vs_logical_or_op, "$a = &$b || $c", "(($a = (& $b)) || $c)");
    test_expression!(by_ref_vs_ternary, "$a = &$b ? $c : $d", "( ($a = (& $b)) ? $c : $d )");
    test_expression!(pre_inc_vs_mul, "$a = ++$b * $c", "($a = ((++ $b) * $c))");
    test_expression!(pre_inc_vs_add, "$a = ++$b + $c", "($a = ((++ $b) + $c))");
    test_expression!(pre_inc_vs_div, "$a = ++$b / $c", "($a = ((++ $b) / $c))");
    test_expression!(pre_inc_vs_sub, "$a = ++$b - $c", "($a = ((++ $b) - $c))");
    test_expression!(pre_inc_vs_mod, "$a = ++$b % $c", "($a = ((++ $b) % $c))");
    test_expression!(pre_inc_vs_pow, "$a = ++$b ** $c", "($a = ((++ $b) ** $c))");
    test_expression!(pre_inc_vs_shift_left, "$a = ++$b << $c", "($a = ((++ $b) << $c))");
    test_expression!(pre_inc_vs_shift_right, "$a = ++$b >> $c", "($a = ((++ $b) >> $c))");
    test_expression!(pre_inc_vs_bitwise_and, "$a = ++$b & $c", "($a = ((++ $b) & $c))");
    test_expression!(pre_inc_vs_bitwise_or, "$a = ++$b | $c", "($a = ((++ $b) | $c))");
    test_expression!(pre_inc_vs_bitwise_xor, "$a = ++$b ^ $c", "($a = ((++ $b) ^ $c))");
    test_expression!(pre_inc_vs_less_than, "$a = ++$b < $c", "($a = ((++ $b) < $c))");
    test_expression!(pre_inc_vs_less_than_equal, "$a = ++$b <= $c", "($a = ((++ $b) <= $c))");
    test_expression!(pre_inc_vs_greater_than, "$a = ++$b > $c", "($a = ((++ $b) > $c))");
    test_expression!(pre_inc_vs_greater_than_equal, "$a = ++$b >= $c", "($a = ((++ $b) >= $c))");
    test_expression!(pre_inc_vs_equal, "$a = ++$b == $c", "($a = ((++ $b) == $c))");
    test_expression!(pre_inc_vs_identical, "$a = ++$b === $c", "($a = ((++ $b) === $c))");
    test_expression!(pre_inc_vs_not_equal, "$a = ++$b != $c", "($a = ((++ $b) != $c))");
    test_expression!(pre_inc_vs_not_identical, "$a = ++$b !== $c", "($a = ((++ $b) !== $c))");
    test_expression!(pre_inc_vs_spaceship, "$a = ++$b <=> $c", "($a = ((++ $b) <=> $c))");
    test_expression!(pre_inc_vs_coalesce, "$a = ++$b ?? $c", "($a = ((++ $b) ?? $c))");
    test_expression!(pre_inc_vs_logical_and_word, "$a = ++$b and $c", "(($a = (++ $b)) and $c)");
    test_expression!(pre_inc_vs_logical_or_word, "$a = ++$b or $c", "(($a = (++ $b)) or $c)");
    test_expression!(pre_inc_vs_logical_xor_word, "$a = ++$b xor $c", "(($a = (++ $b)) xor $c)");
    test_expression!(pre_inc_vs_logical_and_op, "$a = ++$b && $c", "($a = ((++ $b) && $c))");
    test_expression!(pre_inc_vs_logical_or_op, "$a = ++$b || $c", "($a = ((++ $b) || $c))");
    test_expression!(pre_inc_vs_ternary, "$a = ++$b ? $c : $d", "($a = ( (++ $b) ? $c : $d ))");
    test_expression!(pre_dec_vs_mul, "$a = --$b * $c", "($a = ((-- $b) * $c))");
    test_expression!(pre_dec_vs_add, "$a = --$b + $c", "($a = ((-- $b) + $c))");
    test_expression!(pre_dec_vs_div, "$a = --$b / $c", "($a = ((-- $b) / $c))");
    test_expression!(pre_dec_vs_sub, "$a = --$b - $c", "($a = ((-- $b) - $c))");
    test_expression!(pre_dec_vs_mod, "$a = --$b % $c", "($a = ((-- $b) % $c))");
    test_expression!(pre_dec_vs_pow, "$a = --$b ** $c", "($a = ((-- $b) ** $c))");
    test_expression!(pre_dec_vs_shift_left, "$a = --$b << $c", "($a = ((-- $b) << $c))");
    test_expression!(pre_dec_vs_shift_right, "$a = --$b >> $c", "($a = ((-- $b) >> $c))");
    test_expression!(pre_dec_vs_bitwise_and, "$a = --$b & $c", "($a = ((-- $b) & $c))");
    test_expression!(pre_dec_vs_bitwise_or, "$a = --$b | $c", "($a = ((-- $b) | $c))");
    test_expression!(pre_dec_vs_bitwise_xor, "$a = --$b ^ $c", "($a = ((-- $b) ^ $c))");
    test_expression!(pre_dec_vs_less_than, "$a = --$b < $c", "($a = ((-- $b) < $c))");
    test_expression!(pre_dec_vs_less_than_equal, "$a = --$b <= $c", "($a = ((-- $b) <= $c))");
    test_expression!(pre_dec_vs_greater_than, "$a = --$b > $c", "($a = ((-- $b) > $c))");
    test_expression!(pre_dec_vs_greater_than_equal, "$a = --$b >= $c", "($a = ((-- $b) >= $c))");
    test_expression!(pre_dec_vs_equal, "$a = --$b == $c", "($a = ((-- $b) == $c))");
    test_expression!(pre_dec_vs_identical, "$a = --$b === $c", "($a = ((-- $b) === $c))");
    test_expression!(pre_dec_vs_not_equal, "$a = --$b != $c", "($a = ((-- $b) != $c))");
    test_expression!(pre_dec_vs_not_identical, "$a = --$b !== $c", "($a = ((-- $b) !== $c))");
    test_expression!(pre_dec_vs_spaceship, "$a = --$b <=> $c", "($a = ((-- $b) <=> $c))");
    test_expression!(pre_dec_vs_coalesce, "$a = --$b ?? $c", "($a = ((-- $b) ?? $c))");
    test_expression!(pre_dec_vs_logical_and_word, "$a = --$b and $c", "(($a = (-- $b)) and $c)");
    test_expression!(pre_dec_vs_logical_or_word, "$a = --$b or $c", "(($a = (-- $b)) or $c)");
    test_expression!(pre_dec_vs_logical_xor_word, "$a = --$b xor $c", "(($a = (-- $b)) xor $c)");
    test_expression!(pre_dec_vs_logical_and_op, "$a = --$b && $c", "($a = ((-- $b) && $c))");
    test_expression!(pre_dec_vs_logical_or_op, "$a = --$b || $c", "($a = ((-- $b) || $c))");
    test_expression!(pre_dec_vs_ternary, "$a = --$b ? $c : $d", "($a = ( (-- $b) ? $c : $d ))");
    test_expression!(not_vs_mul, "$a = !$b * $c", "($a = ((! $b) * $c))");
    test_expression!(not_vs_add, "$a = !$b + $c", "($a = ((! $b) + $c))");
    test_expression!(not_vs_div, "$a = !$b / $c", "($a = ((! $b) / $c))");
    test_expression!(not_vs_sub, "$a = !$b - $c", "($a = ((! $b) - $c))");
    test_expression!(not_vs_mod, "$a = !$b % $c", "($a = ((! $b) % $c))");
    test_expression!(not_vs_pow, "$a = !$b ** $c", "($a = (! ($b ** $c)))");
    test_expression!(not_vs_shift_left, "$a = !$b << $c", "($a = ((! $b) << $c))");
    test_expression!(not_vs_shift_right, "$a = !$b >> $c", "($a = ((! $b) >> $c))");
    test_expression!(not_vs_bitwise_and, "$a = !$b & $c", "($a = ((! $b) & $c))");
    test_expression!(not_vs_bitwise_or, "$a = !$b | $c", "($a = ((! $b) | $c))");
    test_expression!(not_vs_bitwise_xor, "$a = !$b ^ $c", "($a = ((! $b) ^ $c))");
    test_expression!(not_vs_less_than, "$a = !$b < $c", "($a = ((! $b) < $c))");
    test_expression!(not_vs_less_than_equal, "$a = !$b <= $c", "($a = ((! $b) <= $c))");
    test_expression!(not_vs_greater_than, "$a = !$b > $c", "($a = ((! $b) > $c))");
    test_expression!(not_vs_greater_than_equal, "$a = !$b >= $c", "($a = ((! $b) >= $c))");
    test_expression!(not_vs_equal, "$a = !$b == $c", "($a = ((! $b) == $c))");
    test_expression!(not_vs_identical, "$a = !$b === $c", "($a = ((! $b) === $c))");
    test_expression!(not_vs_not_equal, "$a = !$b != $c", "($a = ((! $b) != $c))");
    test_expression!(not_vs_not_identical, "$a = !$b !== $c", "($a = ((! $b) !== $c))");
    test_expression!(not_vs_spaceship, "$a = !$b <=> $c", "($a = ((! $b) <=> $c))");
    test_expression!(not_vs_coalesce, "$a = !$b ?? $c", "($a = ((! $b) ?? $c))");
    test_expression!(not_vs_logical_and_word, "$a = !$b and $c", "(($a = (! $b)) and $c)");
    test_expression!(not_vs_logical_or_word, "$a = !$b or $c", "(($a = (! $b)) or $c)");
    test_expression!(not_vs_logical_xor_word, "$a = !$b xor $c", "(($a = (! $b)) xor $c)");
    test_expression!(not_vs_logical_and_op, "$a = !$b && $c", "($a = ((! $b) && $c))");
    test_expression!(not_vs_logical_or_op, "$a = !$b || $c", "($a = ((! $b) || $c))");
    test_expression!(not_vs_ternary, "$a = !$b ? $c : $d", "($a = ( (! $b) ? $c : $d ))");
    test_expression!(include_equal, "$a = include $b == $c", "($a = (include ($b == $c)))");
    test_expression!(include_once_equal, "$a = include_once $b == $c", "($a = (include_once ($b == $c)))");
    test_expression!(require_equal, "$a = require $b == $c", "($a = (require ($b == $c)))");
    test_expression!(require_once_equal, "$a = require_once $b == $c", "($a = (require_once ($b == $c)))");
    test_expression!(error_control_include_equal, "$a = @include $b == $c", "($a = (@ (include ($b == $c))))");
    test_expression!(
        error_control_include_once_equal,
        "$a = @include_once $b == $c",
        "($a = (@ (include_once ($b == $c))))"
    );
    test_expression!(error_control_require_equal, "$a = @require $b == $c", "($a = (@ (require ($b == $c))))");
    test_expression!(
        error_control_require_once_equal,
        "$a = @require_once $b == $c",
        "($a = (@ (require_once ($b == $c))))"
    );
    test_expression!(paren_error_control_include_equal, "$a = (@include $b) == $c", "($a = ((@ (include $b)) == $c))");
    test_expression!(
        paren_error_control_include_once_equal,
        "$a = (@include_once $b) == $c",
        "($a = ((@ (include_once $b)) == $c))"
    );
    test_expression!(paren_error_control_require_equal, "$a = (@require $b) == $c", "($a = ((@ (require $b)) == $c))");
    test_expression!(
        paren_error_control_require_once_equal,
        "$a = (@require_once $b) == $c",
        "($a = ((@ (require_once $b)) == $c))"
    );
    test_expression!(paren_new_equal, "$a = (new C) == $b", "($a = ((new C) == $b))");
    test_expression!(no_paren_new_equal, "$a = new C == $b", "($a = ((new C) == $b))");
    test_expression!(paren_error_control_new_equal, "$a = @(new C) == $b", "($a = ((@ (new C)) == $b))");
    test_expression!(no_paren_error_control_new_equal, "$a = @new C == $b", "($a = ((@ (new C)) == $b))");
    test_expression!(
        complex_arithmetic_and_logic,
        "$a = ++$b * -$c + $d / $e ** $f && $g || $h",
        "($a = (((((++ $b) * (- $c)) + ($d / ($e ** $f))) && $g) || $h))"
    );
    test_expression!(
        complex_ternary_and_coalesce,
        "$a = $b ?? $c ? $d + $e : $f - $g",
        "($a = ( ($b ?? $c) ? ($d + $e) : ($f - $g) ))"
    );
    test_expression!(complex_assignments_and_pow, "$a = $b += $c ** $d ** $e", "($a = ($b += ($c ** ($d ** $e))))");
    test_expression!(
        complex_error_control_and_instanceof,
        "$a = @$b instanceof C + $d",
        "($a = (((@ $b) instanceof C) + $d))"
    );
    test_expression!(
        complex_logical_words_and_ops,
        "$a = $b and $c || $d xor $e && $f",
        "((($a = $b) and ($c || $d)) xor ($e && $f))"
    );
    test_expression!(
        complex_shifts_and_arithmetic,
        "$a = $b << $c + $d * $e >> $f - $g",
        "($a = (($b << ($c + ($d * $e))) >> ($f - $g)))"
    );
    test_expression!(
        complex_unary_and_binary_mix,
        "$a = !$b + ~$c * --$d / @$e",
        "($a = ((! $b) + (((~ $c) * (-- $d)) / (@ $e))))"
    );
    test_expression!(
        complex_nested_ternary,
        "$a = $b ? $c ? $d : $e : $f ? $g : $h",
        "($a = ( ( $b ? ( $c ? $d : $e ) : $f ) ? $g : $h ))"
    );
    test_expression!(
        complex_coalesce_and_ternary,
        "$a = $b ?? $c ? $d : $e ?? $f",
        "($a = ( ($b ?? $c) ? $d : ($e ?? $f) ))"
    );
    test_expression!(
        complex_arithmetic_and_spaceship,
        "$a = $b + $c * $d <=> $e / $f - $g",
        "($a = (($b + ($c * $d)) <=> (($e / $f) - $g)))"
    );
    test_expression!(
        complex_all_logical,
        "$a = $b && $c || $d and $e xor $f or $g",
        "(((($a = (($b && $c) || $d)) and $e) xor $f) or $g)"
    );
    test_expression!(complex_pre_inc_and_pow, "$a = ++$b ** $c * $d", "($a = (((++ $b) ** $c) * $d))");
    test_expression!(complex_by_ref_and_coalesce, "$a = &$b ?? $c + $d", "(($a = (& $b)) ?? ($c + $d))");
    test_expression!(complex_minus_pow_mul_add, "$a = -$b ** $c * $d + $e", "($a = (((- ($b ** $c)) * $d) + $e))");
    test_expression!(
        complex_div_mul_mod_add_sub,
        "$a = $b / $c * $d % $e + $f - $g",
        "($a = ((((($b / $c) * $d) % $e) + $f) - $g))"
    );
    test_expression!(
        complex_ternary_with_assignments,
        "$a = $b ? $c = $d : $e = $f",
        "($a = ( $b ? ($c = $d) : ($e = $f) ))"
    );
    test_expression!(
        complex_instanceof_and_logical,
        "$a = $b instanceof C && $d instanceof E",
        "($a = (($b instanceof C) && ($d instanceof E)))"
    );
    test_expression!(complex_not_and_instanceof, "$a = !$b instanceof C", "($a = (! ($b instanceof C)))");
    test_expression!(complex_clone_and_arrow, "$a = clone $b * $c", "($a = ((clone $b) * $c))");
    test_expression!(complex_error_control_on_ternary, "$a = @$b ? $c : $d - $e", "($a = ( (@ $b) ? $c : ($d - $e) ))");
    test_expression!(
        complex_long_arithmetic_chain,
        "$a = $b + $c - $d * $e / $f % $g ** $h",
        "($a = (($b + $c) - ((($d * $e) / $f) % ($g ** $h))))"
    );
    test_expression!(
        complex_right_assoc_chain,
        "$a = $b ?? $c ?? $d ? $e : $f",
        "($a = ( ($b ?? ($c ?? $d)) ? $e : $f ))"
    );
    test_expression!(complex_left_assoc_chain, "$a = $b - $c + $d - $e", "($a = ((($b - $c) + $d) - $e))");
    test_expression!(
        complex_mixed_assoc_chain,
        "$a = $b ** $c + $d - $e ** $f",
        "($a = ((($b ** $c) + $d) - ($e ** $f)))"
    );
    test_expression!(complex_unary_on_right_of_binary, "$a = $b * ++$c - ~$d", "($a = (($b * (++ $c)) - (~ $d)))");
    test_expression!(
        complex_low_precedence_words_interleaved,
        "$a = $b == $c and $d != $e or $f > $g xor $h < $i",
        "((($a = ($b == $c)) and ($d != $e)) or (($f > $g) xor ($h < $i)))"
    );
    test_expression!(
        complex_bitwise_interleaved_with_arithmetic,
        "$a = $b + $c & $d * $e | $f ^ $g - $h",
        "($a = ((($b + $c) & ($d * $e)) | ($f ^ ($g - $h))))"
    );
    test_expression!(complex_ternary_in_coalesce, "$a = $b ?? $c ? $d : $e", "($a = ( ($b ?? $c) ? $d : $e ))");
    test_expression!(complex_coalesce_in_coalesce, "$a = $b ?? $c ?? $d", "($a = ($b ?? ($c ?? $d)))");
    test_expression!(complex_assignment_in_condition, "$a = ($b = $c) ? $d : $e", "($a = ( ($b = $c) ? $d : $e ))");
    test_expression!(complex_multiple_unary, "$a = !-++$b", "($a = (! (- (++ $b))))");
    test_expression!(complex_identical_vs_coalesce, "$a = $b === $c ?? $d", "($a = (($b === $c) ?? $d))");
    test_expression!(complex_coalesce_vs_identical, "$a = $b ?? $c === $d", "($a = ($b ?? ($c === $d)))");
    test_expression!(complex_pow_is_right_associative, "$a = $b ** $c ** $d", "($a = ($b ** ($c ** $d)))");
    test_expression!(complex_concat_is_left_associative, "$a = $b . $c . $d", "($a = (($b . $c) . $d))");
    test_expression!(complex_error_control_and_pre_inc, "$a = @++$b ** $c", "($a = (@ ((++ $b) ** $c)))");
    test_expression!(
        complex_long_chain_with_parens,
        "$a = ($b + $c) * ($d - $e) / (($f % $g) ** $h)",
        "($a = ((($b + $c) * ($d - $e)) / (($f % $g) ** $h)))"
    );
    test_expression!(
        complex_shifts_and_bitwise,
        "$a = $b << $c & $d >> $e | $f",
        "($a = ((($b << $c) & ($d >> $e)) | $f))"
    );
    test_expression!(
        complex_double_ternary_and_coalesce,
        "$a = $b ? $c : $d ?? $e ? $f : $g",
        "($a = ( ( $b ? $c : ($d ?? $e) ) ? $f : $g ))"
    );
    test_expression!(
        complex_ternary_condition_with_logic,
        "$a = $b > $c && $d < $e ? $f : $g",
        "($a = ( (($b > $c) && ($d < $e)) ? $f : $g ))"
    );
    test_expression!(complex_instanceof_with_new, "$a = new C instanceof D", "($a = ((new C) instanceof D))");
    test_expression!(complex_yield_precedence, "$a = $b + yield $c * $d", "($a = ($b + (yield ($c * $d))))");
    test_expression!(complex_yield_from_precedence, "$a = $b and yield from $c", "(($a = $b) and (yield from $c))");
    test_expression!(complex_print_precedence, "$a = $b && print $c", "($a = ($b && (print $c)))");
    test_expression!(
        complex_very_long_chain_of_doom,
        "$a = $b + $c * $d > $e && $f & $g | $h ^ $i or $j = $k ?? $l",
        "(($a = ((($b + ($c * $d)) > $e) && (($f & $g) | ($h ^ $i)))) or ($j = ($k ?? $l)))"
    );
    test_expression!(complex_negation_and_bitwise_not, "$a = !~$b | $c", "($a = ((! (~ $b)) | $c))");
    test_expression!(special_throw_left_vs_and, "throw new E and $c", "(throw ((new E) and $c))");
    test_expression!(special_throw_right_vs_and, "$c and throw new E", "($c and (throw (new E)))");
    test_expression!(special_throw_left_vs_xor, "throw new E xor $c", "(throw ((new E) xor $c))");
    test_expression!(special_throw_right_vs_xor, "$c xor throw new E", "($c xor (throw (new E)))");
    test_expression!(special_throw_left_vs_or, "throw new E or $c", "(throw ((new E) or $c))");
    test_expression!(special_throw_right_vs_or, "$c or throw new E", "($c or (throw (new E)))");

    test_expression!(assignment_associativity_simple, "$a ?? $b %= $c", "($a ?? ($b %= $c))");
    test_expression!(
        assignment_associativity_complex,
        "$a >> $b ?? $c %= $d <=> $e",
        "(($a >> $b) ?? ($c %= ($d <=> $e)))"
    );
    test_expression!(assignment_associativity_with_unary, "$a ** --$b *= $c", "($a ** ((-- $b) *= $c))");
    test_expression!(assignment_associativity_pow, "$a ?? $b **= $c", "($a ?? ($b **= $c))");
    test_expression!(concat_lower_than_shift, "$a . $b << $c", "($a . ($b << $c))");
    test_expression!(shift_higher_than_concat, "$a << $b . $c", "(($a << $b) . $c)");
    test_expression!(concat_and_shift_mixed, "$a . $b << $c . $d", "(($a . ($b << $c)) . $d)");
    test_expression!(expr_1, "$e = $a ? $b : $c;", "($e = ( $a ? $b : $c ))");
    test_expression!(expr_2, "$e = $a ? $b : $c ? $d : $b;", "($e = ( ( $a ? $b : $c ) ? $d : $b ))");
    test_expression!(expr_3, "$f = $a ? $b : ($c ? $d : $b);", "($f = ( $a ? $b : ( $c ? $d : $b ) ))");
    test_expression!(expr_4, "$g = $a ? ($b ? $c : $d) : $b;", "($g = ( $a ? ( $b ? $c : $d ) : $b ))");
    test_expression!(expr_5, "$h = ($a ? $b : $c) ? $d : $b;", "($h = ( ( $a ? $b : $c ) ? $d : $b ))");
    test_expression!(
        expr_6,
        "$i = ($a ? $b : $c) ? ($d ? $a : $b) : $c;",
        "($i = ( ( $a ? $b : $c ) ? ( $d ? $a : $b ) : $c ))"
    );
    test_expression!(
        expr_7,
        "$j = $a ? ($b ? $c : $d) : ($c ? $d : $a);",
        "($j = ( $a ? ( $b ? $c : $d ) : ( $c ? $d : $a ) ))"
    );
    test_expression!(
        expr_8,
        "$k = ($a ? $b : $c) ? $d : ($c ? $a : $b);",
        "($k = ( ( $a ? $b : $c ) ? $d : ( $c ? $a : $b ) ))"
    );
    test_expression!(expr_9, "$l = $a ?: $b ?: $c ?: $d;", "($l = ((($a ?: $b) ?: $c) ?: $d))");
    test_expression!(expr_10, "$m = $a ?: ($b ?: ($c ?: $d));", "($m = ($a ?: ($b ?: ($c ?: $d))))");
    test_expression!(expr_11, "$n = ($a ?: $b) ?: ($c ?: $d);", "($n = (($a ?: $b) ?: ($c ?: $d)))");
    test_expression!(expr_12, "$o = ($a ?: $b) ?: $c ?: $d;", "($o = ((($a ?: $b) ?: $c) ?: $d))");
    test_expression!(expr_13, "$p = $a ?: ($b ?: $c) ?: $d;", "($p = (($a ?: ($b ?: $c)) ?: $d))");
    test_expression!(expr_14, "$q = $a ?: $b ?: ($c ?: $d);", "($q = (($a ?: $b) ?: ($c ?: $d)))");
    test_expression!(expr_15, "$r = $a ?: ($b ?: $c ?: $d);", "($r = ($a ?: (($b ?: $c) ?: $d)))");
    test_expression!(expr_16, "$s = ($a ?: $b ?: $c) ?: $d;", "($s = ((($a ?: $b) ?: $c) ?: $d))");
    test_expression!(expr_17, "$t = $a ? $b : $c ?: $d;", "($t = (( $a ? $b : $c ) ?: $d))");
    test_expression!(rand_0, "$a || $b & $c <=> ++$d - $e or $f", "(($a || ($b & ($c <=> ((++ $d) - $e)))) or $f)");
    test_expression!(rand_1, "$a << $b ?? --$c <=> $d && ++$e", "(($a << $b) ?? (((-- $c) <=> $d) && (++ $e)))");
    test_expression!(rand_2, "$a & $b && -$c ^ $d or $e > $f", "((($a & $b) && ((- $c) ^ $d)) or ($e > $f))");
    test_expression!(rand_4, "$a and $b ** $c and $d and $e ** $f", "((($a and ($b ** $c)) and $d) and ($e ** $f))");
    test_expression!(
        rand_5,
        "$a = $b != ++$c and --$d || $e || $f || ++$g",
        "(($a = ($b != (++ $c))) and ((((-- $d) || $e) || $f) || (++ $g)))"
    );
    test_expression!(rand_7, "$a & $b - $c && $d !== ++$e / $f", "(($a & ($b - $c)) && ($d !== ((++ $e) / $f)))");
    test_expression!(rand_8, "$a or $b >>= $c >> $d << $e | $f", "($a or ($b >>= ((($c >> $d) << $e) | $f)))");
    test_expression!(rand_9, "$a % +$b && +$c - $d << $e", "(($a % (+ $b)) && (((+ $c) - $d) << $e))");
    test_expression!(rand_10, "$a ?? $b == $c || $d or $e", "(($a ?? (($b == $c) || $d)) or $e)");
    test_expression!(rand_11, "$a / $b + $c ** $d && --$e", "((($a / $b) + ($c ** $d)) && (-- $e))");
    test_expression!(
        rand_12,
        "$a & -$b = $c > $d ^ $e xor $f && ++$g ?? $h",
        "(($a & (- ($b = (($c > $d) ^ $e)))) xor (($f && (++ $g)) ?? $h))"
    );
    test_expression!(rand_13, "$a xor $b | $c <= $d >> $e", "($a xor ($b | ($c <= ($d >> $e))))");
    test_expression!(
        rand_14,
        "$a !== $b & $c % $d + $e <= $f | $g xor $h",
        "(((($a !== $b) & ((($c % $d) + $e) <= $f)) | $g) xor $h)"
    );
    test_expression!(
        rand_15,
        "$a ?? +$b << $c or $d ^ ~$e | $f ** $g",
        "(($a ?? ((+ $b) << $c)) or (($d ^ (~ $e)) | ($f ** $g)))"
    );
    test_expression!(rand_16, "$a && $b ?? $c | $d || $e and $f", "((($a && $b) ?? (($c | $d) || $e)) and $f)");
    test_expression!(rand_17, "$a && $b < $c ?? $d and $e", "((($a && ($b < $c)) ?? $d) and $e)");
    test_expression!(
        rand_18,
        "$a and $b / $c xor $d && $e ** $f & $g & --$h & $i",
        "(($a and ($b / $c)) xor ($d && (((($e ** $f) & $g) & (-- $h)) & $i)))"
    );
    test_expression!(rand_19, "$a or $b >> $c << $d << $e", "($a or ((($b >> $c) << $d) << $e))");
    test_expression!(
        rand_20,
        "$a & $b and $c && $d and $e >> $f && $g << $h ?? --$i",
        "((($a & $b) and ($c && $d)) and ((($e >> $f) && ($g << $h)) ?? (-- $i)))"
    );
    test_expression!(rand_21, "$a <= $b & $c && ++$d . $e", "((($a <= $b) & $c) && ((++ $d) . $e))");
    test_expression!(
        rand_22,
        "$a <=> $b ^ @$c and $d xor $e << $f",
        "(((($a <=> $b) ^ (@ $c)) and $d) xor ($e << $f))"
    );
    test_expression!(
        rand_23,
        "$a xor $b and $c xor $d xor $e ?? $f <= $g && ~$h",
        "((($a xor ($b and $c)) xor $d) xor ($e ?? (($f <= $g) && (~ $h))))"
    );
    test_expression!(
        rand_24,
        "$a << $b != $c ** $d ** $e && $f === $g",
        "((($a << $b) != ($c ** ($d ** $e))) && ($f === $g))"
    );
    test_expression!(
        rand_25,
        "$a - $b ?? $c * $d && +$e <= ~$f ** --$g != $h",
        "(($a - $b) ?? (($c * $d) && (((+ $e) <= (~ ($f ** (-- $g)))) != $h)))"
    );
    test_expression!(
        rand_26,
        "$a - $b << $c && --$d ?? $e & $f *= $g .= --$h",
        "(((($a - $b) << $c) && (-- $d)) ?? ($e & ($f *= ($g .= (-- $h)))))"
    );
    test_expression!(rand_27, "$a ?? $b << $c & $d * $e * --$f", "($a ?? (($b << $c) & (($d * $e) * (-- $f))))");
    test_expression!(rand_29, "$a <=> $b & $c > $d <=> $e or -$f", "((($a <=> $b) & (($c > $d) <=> $e)) or (- $f))");
    test_expression!(
        rand_30,
        "$a xor $b xor $c !== @$d || $e &= $f xor $g ^ $h ?? $i",
        "((($a xor $b) xor (($c !== (@ $d)) || ($e &= $f))) xor (($g ^ $h) ?? $i))"
    );
    test_expression!(
        rand_31,
        "$a . $b or $c <= $d | @$e ** $f === +$g ** $h",
        "(($a . $b) or (($c <= $d) | ((@ ($e ** $f)) === (+ ($g ** $h)))))"
    );
    test_expression!(
        rand_32,
        "$a <=> $b and $c || ++$d . $e && ++$f",
        "(($a <=> $b) and ($c || (((++ $d) . $e) && (++ $f))))"
    );
    test_expression!(rand_33, "$a | $b ?? $c || $d or $e", "((($a | $b) ?? ($c || $d)) or $e)");
    test_expression!(rand_34, "$a + $b <<= $c | $d xor $e and $f", "(($a + ($b <<= ($c | $d))) xor ($e and $f))");
    test_expression!(rand_36, "$a > $b xor $c ^ $d >> $e <=> $f", "(($a > $b) xor ($c ^ (($d >> $e) <=> $f)))");
    test_expression!(rand_37, "$a & $b || $c ** $d **= $e xor $f", "((($a & $b) || ($c ** ($d **= $e))) xor $f)");
    test_expression!(rand_38, "$a and +$b || $c | $d xor $e || $f", "(($a and ((+ $b) || ($c | $d))) xor ($e || $f))");
    test_expression!(rand_39, "$a | $b ?? $c <= --$d xor $e", "((($a | $b) ?? ($c <= (-- $d))) xor $e)");
    test_expression!(rand_40, "$a <=> @$b < $c xor -$d | $e | $f", "(($a <=> ((@ $b) < $c)) xor (((- $d) | $e) | $f))");
    test_expression!(rand_41, "$a & $b .= $c and $d <=> $e", "(($a & ($b .= $c)) and ($d <=> $e))");
    test_expression!(
        rand_42,
        "$a <=> $b * -$c ^ $d or $e ** ++$f || $g ** $h",
        "((($a <=> ($b * (- $c))) ^ $d) or (($e ** (++ $f)) || ($g ** $h)))"
    );
    test_expression!(
        rand_44,
        "$a or $b ?? --$c ** $d &= $e | $f ?? $g || $h <=> $i",
        "($a or ($b ?? ((-- $c) ** ($d &= (($e | $f) ?? ($g || ($h <=> $i)))))))"
    );
    test_expression!(
        rand_46,
        "$a <= $b <<= --$c >> $d ^ --$e <=> $f & $g != $h & $i",
        "($a <= ($b <<= (((-- $c) >> $d) ^ ((((-- $e) <=> $f) & ($g != $h)) & $i))))"
    );
    test_expression!(rand_47, "$a % --$b * $c and $d and $e && $f", "(((($a % (-- $b)) * $c) and $d) and ($e && $f))");
    test_expression!(rand_48, "$a and -$b && $c <=> $d + $e", "($a and ((- $b) && ($c <=> ($d + $e))))");
    test_expression!(
        rand_49,
        "$a . $b && $c && $d | $e <=> $f and ++$g && $h ** --$i",
        "(((($a . $b) && $c) && ($d | ($e <=> $f))) and ((++ $g) && ($h ** (-- $i))))"
    );
    test_expression!(
        rand_50,
        "$a >> $b and $c and -$d << $e ** $f || $g ?? $h >= $i",
        "((($a >> $b) and $c) and ((((- $d) << ($e ** $f)) || $g) ?? ($h >= $i)))"
    );
    test_expression!(
        rand_51,
        "$a or $b or $c && $d * $e ?? $f || !$g or ~$h ^ $i",
        "((($a or $b) or (($c && ($d * $e)) ?? ($f || (! $g)))) or ((~ $h) ^ $i))"
    );
    test_expression!(
        rand_52,
        "$a >> $b && $c <= $d <=> $e or $f -= $g",
        "((($a >> $b) && (($c <= $d) <=> $e)) or ($f -= $g))"
    );
    test_expression!(
        rand_54,
        "$a ?? $b | $c && $d ^ $e || $f * $g /= $h * $i",
        "($a ?? ((($b | $c) && ($d ^ $e)) || ($f * ($g /= ($h * $i)))))"
    );
    test_expression!(
        rand_55,
        "$a << $b <= $c | $d or $e <<= $f < !$g",
        "(((($a << $b) <= $c) | $d) or ($e <<= ($f < (! $g))))"
    );
    test_expression!(rand_56, "$a ** $b + $c === ++$d | $e & $f", "(((($a ** $b) + $c) === (++ $d)) | ($e & $f))");
    test_expression!(
        rand_57,
        "$a <=> $b . $c xor $d ??= $e != $f || $g ** $h",
        "(($a <=> ($b . $c)) xor ($d ??= (($e != $f) || ($g ** $h))))"
    );
    test_expression!(
        rand_58,
        "$a ** $b | $c && $d ?? --$e xor ++$f >> $g",
        "((((($a ** $b) | $c) && $d) ?? (-- $e)) xor ((++ $f) >> $g))"
    );
    test_expression!(rand_59, "$a != $b / $c >> $d ** $e ** $f", "($a != (($b / $c) >> ($d ** ($e ** $f))))");
    test_expression!(rand_60, "$a <= $b ??= $c * $d ?? $e", "($a <= ($b ??= (($c * $d) ?? $e)))");
    test_expression!(rand_61, "$a or $b ^ $c or $d <<= $e", "(($a or ($b ^ $c)) or ($d <<= $e))");
    test_expression!(
        rand_62,
        "$a << $b % $c << $d | $e / $f != $g || ++$h",
        "(((($a << ($b % $c)) << $d) | (($e / $f) != $g)) || (++ $h))"
    );
    test_expression!(rand_63, "$a & $b ** $c xor $d === --$e", "(($a & ($b ** $c)) xor ($d === (-- $e)))");
    test_expression!(
        rand_64,
        "$a >> ~$b or --$c && $d ** $e - $f >> $g",
        "(($a >> (~ $b)) or ((-- $c) && ((($d ** $e) - $f) >> $g)))"
    );
    test_expression!(
        rand_65,
        "$a & $b ^= ++$c && $d or $e or ++$f - $g % !$h ** $i",
        "((($a & ($b ^= ((++ $c) && $d))) or $e) or ((++ $f) - ($g % (! ($h ** $i)))))"
    );
    test_expression!(rand_66, "$a && $b & $c & $d and $e xor ++$f", "((($a && (($b & $c) & $d)) and $e) xor (++ $f))");
    test_expression!(
        rand_68,
        "$a >> -$b & $c xor $d ?? $e <=> $f xor @$g . $h << !$i",
        "(((($a >> (- $b)) & $c) xor ($d ?? ($e <=> $f))) xor ((@ $g) . ($h << (! $i))))"
    );
    test_expression!(
        rand_69,
        "$a >> --$b ?? $c ^ $d && $e ?? $f %= $g >> ++$h <=> $i",
        "(($a >> (-- $b)) ?? ((($c ^ $d) && $e) ?? ($f %= (($g >> (++ $h)) <=> $i))))"
    );
    test_expression!(
        rand_70,
        "$a || $b || ++$c & $d + $e && $f != $g & ~$h",
        "(($a || $b) || (((++ $c) & ($d + $e)) && (($f != $g) & (~ $h))))"
    );
    test_expression!(
        rand_71,
        "$a or $b % $c ?? $d and ++$e or !$f && $g < $h + $i",
        "(($a or ((($b % $c) ?? $d) and (++ $e))) or ((! $f) && ($g < ($h + $i))))"
    );
    test_expression!(
        rand_72,
        "$a && $b && $c ^ --$d | ++$e << $f . -$g",
        "(($a && $b) && (($c ^ (-- $d)) | (((++ $e) << $f) . (- $g))))"
    );
    test_expression!(
        rand_74,
        "$a >> $b ^ $c + $d .= $e | $f = $g > $h <<= $i",
        "(($a >> $b) ^ ($c + ($d .= ($e | ($f = ($g > ($h <<= $i)))))))"
    );
    test_expression!(
        rand_75,
        "$a / $b & $c ?? $d | $e << $f %= $g & $h <=> $i",
        "((($a / $b) & $c) ?? ($d | ($e << ($f %= ($g & ($h <=> $i))))))"
    );
    test_expression!(rand_76, "$a xor $b = $c or $d || @$e", "(($a xor ($b = $c)) or ($d || (@ $e)))");
    test_expression!(
        rand_78,
        "$a - $b xor $c or $d = $e xor $f + $g xor $h || $i",
        "((($a - $b) xor $c) or ((($d = $e) xor ($f + $g)) xor ($h || $i)))"
    );
    test_expression!(rand_79, "$a xor $b xor $c % $d || $e < $f", "(($a xor $b) xor (($c % $d) || ($e < $f)))");
    test_expression!(rand_80, "$a << $b xor $c && $d ?? $e xor $f", "((($a << $b) xor (($c && $d) ?? $e)) xor $f)");
    test_expression!(
        rand_82,
        "$a || $b <=> -$c ** $d >> $e ** $f or $g and $h",
        "(($a || ($b <=> ((- ($c ** $d)) >> ($e ** $f)))) or ($g and $h))"
    );
    test_expression!(
        rand_83,
        "$a ^ $b and $c /= $d & $e - $f and !$g >> $h <=> $i",
        "((($a ^ $b) and ($c /= ($d & ($e - $f)))) and (((! $g) >> $h) <=> $i))"
    );
    test_expression!(rand_84, "$a xor $b || $c - $d and $e", "($a xor (($b || ($c - $d)) and $e))");
    test_expression!(
        rand_85,
        "$a >> $b && $c & $d != $e ** $f xor $g",
        "((($a >> $b) && ($c & ($d != ($e ** $f)))) xor $g)"
    );
    test_expression!(rand_86, "$a .= $b = $c || ~$d < $e", "($a .= ($b = ($c || ((~ $d) < $e))))");
    test_expression!(
        rand_87,
        "$a *= $b & -$c | ++$d ?? --$e ** $f",
        "($a *= ((($b & (- $c)) | (++ $d)) ?? ((-- $e) ** $f)))"
    );
    test_expression!(
        rand_88,
        "$a + $b |= $c & $d = $e ^ +$f . $g",
        "($a + ($b |= ($c & ($d = ($e ^ ((+ $f) . $g))))))"
    );
    test_expression!(rand_89, "$a > $b - $c ^ $d or --$e", "((($a > ($b - $c)) ^ $d) or (-- $e))");
    test_expression!(rand_90, "$a >= $b or $c ??= ++$d ^ $e ?? $f", "(($a >= $b) or ($c ??= (((++ $d) ^ $e) ?? $f)))");
    test_expression!(
        rand_91,
        "$a xor $b >= $c and !$d != $e | $f || $g ** +$h < $i",
        "($a xor (($b >= $c) and ((((! $d) != $e) | $f) || (($g ** (+ $h)) < $i))))"
    );
    test_expression!(rand_92, "$a ^ $b ?? $c **= $d & $e != $f", "(($a ^ $b) ?? ($c **= ($d & ($e != $f))))");
    test_expression!(
        rand_93,
        "$a ** $b * $c >> $d xor $e ??= -$f ?? --$g === $h & $i",
        "(((($a ** $b) * $c) >> $d) xor ($e ??= ((- $f) ?? (((-- $g) === $h) & $i))))"
    );
    test_expression!(rand_95, "$a or $b or $c % $d << $e", "(($a or $b) or (($c % $d) << $e))");
    test_expression!(rand_98, "$a xor $b === $c || $d >= ++$e", "($a xor (($b === $c) || ($d >= (++ $e))))");
    test_expression!(
        rand_99,
        "$a ?? $b or $c & $d > !$e xor @$f ^ $g && $h",
        "(($a ?? $b) or (($c & ($d > (! $e))) xor (((@ $f) ^ $g) && $h)))"
    );
    test_expression!(
        rand_100,
        "$a <=> -$b %= $c && $d . +$e |= --$f",
        "($a <=> (- ($b %= ($c && ($d . (+ ($e |= (-- $f))))))))"
    );
    test_expression!(rand_101, "$a ^ @$b ^ $c << $d << $e", "(($a ^ (@ $b)) ^ (($c << $d) << $e))");
    test_expression!(rand_102, "$a and $b - $c <=> $d or $e", "(($a and (($b - $c) <=> $d)) or $e)");
    test_expression!(
        rand_103,
        "$a or $b and $c | $d -= ++$e < $f <=> $g",
        "($a or ($b and ($c | ($d -= (((++ $e) < $f) <=> $g)))))"
    );
    test_expression!(rand_104, "$a || $b &= $c & $d or $e ^ $f", "(($a || ($b &= ($c & $d))) or ($e ^ $f))");
    test_expression!(
        rand_106,
        "$a ^= $b ^ @$c && $d << $e . $f * $g",
        "($a ^= (($b ^ (@ $c)) && (($d << $e) . ($f * $g))))"
    );
    test_expression!(
        rand_107,
        "$a & $b ** $c xor $d and $e && ++$f != $g xor +$h && $i",
        "((($a & ($b ** $c)) xor ($d and ($e && ((++ $f) != $g)))) xor ((+ $h) && $i))"
    );
    test_expression!(
        rand_108,
        "$a ** $b xor $c % ++$d ** --$e ?? $f",
        "(($a ** $b) xor (($c % ((++ $d) ** (-- $e))) ?? $f))"
    );
    test_expression!(rand_109, "$a ?? $b ** $c xor $d ** @$e", "(($a ?? ($b ** $c)) xor ($d ** (@ $e)))");
    test_expression!(rand_110, "$a = $b << !$c && $d ^ $e", "($a = (($b << (! $c)) && ($d ^ $e)))");
    test_expression!(rand_112, "$a & $b ** $c <=> $d <<= $e += $f", "($a & (($b ** $c) <=> ($d <<= ($e += $f))))");
    test_expression!(
        rand_113,
        "$a or $b <=> $c + $d && $e xor $f && ~$g ?? $h << $i",
        "($a or ((($b <=> ($c + $d)) && $e) xor (($f && (~ $g)) ?? ($h << $i))))"
    );
    test_expression!(
        rand_114,
        "$a ** $b << $c / $d or $e and $f ^ $g",
        "((($a ** $b) << ($c / $d)) or ($e and ($f ^ $g)))"
    );
    test_expression!(
        rand_115,
        "$a / $b | $c or $d >> $e && $f & $g << $h + !$i",
        "((($a / $b) | $c) or (($d >> $e) && ($f & ($g << ($h + (! $i))))))"
    );
    test_expression!(
        rand_116,
        "$a **= $b or $c * +$d . --$e and $f",
        "(($a **= $b) or ((($c * (+ $d)) . (-- $e)) and $f))"
    );
    test_expression!(
        rand_117,
        "$a !== $b << $c && $d & $e <=> $f xor $g",
        "((($a !== ($b << $c)) && ($d & ($e <=> $f))) xor $g)"
    );
    test_expression!(rand_118, "$a || $b . $c | $d ?? $e ?? @$f", "(($a || (($b . $c) | $d)) ?? ($e ?? (@ $f)))");
    test_expression!(rand_119, "$a <=> $b + $c xor +$d < $e & $f", "(($a <=> ($b + $c)) xor (((+ $d) < $e) & $f))");
    test_expression!(
        rand_120,
        "$a or $b ?? --$c & $d + $e ?? $f ^ $g . $h",
        "($a or ($b ?? (((-- $c) & ($d + $e)) ?? ($f ^ ($g . $h)))))"
    );
    test_expression!(rand_121, "$a and $b * $c != $d ** $e", "($a and (($b * $c) != ($d ** $e)))");
    test_expression!(
        rand_122,
        "$a ?? $b and $c ** $d and $e || $f && $g ?? $h >> $i",
        "((($a ?? $b) and ($c ** $d)) and (($e || ($f && $g)) ?? ($h >> $i)))"
    );
    test_expression!(
        rand_123,
        "$a / $b ^ $c % $d /= --$e && $f << $g < $h",
        "(($a / $b) ^ ($c % ($d /= ((-- $e) && (($f << $g) < $h)))))"
    );
    test_expression!(
        rand_124,
        "$a ^ $b **= $c . $d << $e != $f ?? $g xor ~$h and $i",
        "(($a ^ ($b **= ((($c . ($d << $e)) != $f) ?? $g))) xor ((~ $h) and $i))"
    );
    test_expression!(rand_125, "$a ^ $b >>= $c > $d or $e > $f", "(($a ^ ($b >>= ($c > $d))) or ($e > $f))");
    test_expression!(
        rand_126,
        "$a xor $b <=> $c | $d or $e or $f >> $g <= --$h ^ $i",
        "((($a xor (($b <=> $c) | $d)) or $e) or ((($f >> $g) <= (-- $h)) ^ $i))"
    );
    test_expression!(rand_127, "$a & $b ?? $c || $d ^ $e xor $f", "((($a & $b) ?? ($c || ($d ^ $e))) xor $f)");
    test_expression!(
        rand_129,
        "$a xor $b | $c ?? $d ** $e .= $f xor $g == $h xor $i",
        "((($a xor (($b | $c) ?? ($d ** ($e .= $f)))) xor ($g == $h)) xor $i)"
    );
    test_expression!(rand_130, "$a * $b = $c **= $d ^ $e * $f", "($a * ($b = ($c **= ($d ^ ($e * $f)))))");
    test_expression!(rand_131, "$a | $b . $c !== $d or ++$e == $f", "(($a | (($b . $c) !== $d)) or ((++ $e) == $f))");
    test_expression!(
        rand_132,
        "$a || $b ** $c >> $d ^ $e != -$f && $g ** $h + $i",
        "($a || (((($b ** $c) >> $d) ^ ($e != (- $f))) && (($g ** $h) + $i)))"
    );
    test_expression!(rand_133, "$a or $b - $c >> $d & $e", "($a or ((($b - $c) >> $d) & $e))");
    test_expression!(rand_134, "$a && $b ** $c & $d ^ ++$e", "($a && ((($b ** $c) & $d) ^ (++ $e)))");
    test_expression!(
        rand_136,
        "$a <=> $b << $c | $d <=> $e ** $f >= $g | $h ?? $i",
        "(((($a <=> ($b << $c)) | ($d <=> (($e ** $f) >= $g))) | $h) ?? $i)"
    );
    test_expression!(rand_138, "$a >> $b * $c and -$d ^ $e xor $f", "((($a >> ($b * $c)) and ((- $d) ^ $e)) xor $f)");
    test_expression!(rand_139, "$a ** $b / $c && $d and $e", "(((($a ** $b) / $c) && $d) and $e)");
    test_expression!(
        rand_140,
        "$a and $b and $c && $d ?? $e | !$f + $g",
        "(($a and $b) and (($c && $d) ?? ($e | ((! $f) + $g))))"
    );
    test_expression!(rand_141, "$a % $b ** $c % $d >> $e", "((($a % ($b ** $c)) % $d) >> $e)");
    test_expression!(
        rand_142,
        "$a ?? ~$b && $c | $d <=> $e ** $f and $g or ~$h | $i",
        "((($a ?? ((~ $b) && ($c | ($d <=> ($e ** $f))))) and $g) or ((~ $h) | $i))"
    );
    test_expression!(
        rand_143,
        "$a xor $b ** $c ** $d | $e ^= $f * ~$g && !$h xor +$i",
        "(($a xor (($b ** ($c ** $d)) | ($e ^= (($f * (~ $g)) && (! $h))))) xor (+ $i))"
    );
    test_expression!(rand_144, "$a && $b %= $c ^ $d ** $e", "($a && ($b %= ($c ^ ($d ** $e))))");
    test_expression!(rand_145, "$a ** $b ?? $c == $d ** $e", "(($a ** $b) ?? ($c == ($d ** $e)))");
    test_expression!(
        rand_146,
        "$a /= --$b ^ $c / $d ?? ++$e >= $f || $g && --$h == $i",
        "($a /= (((-- $b) ^ ($c / $d)) ?? (((++ $e) >= $f) || ($g && ((-- $h) == $i)))))"
    );
    test_expression!(rand_147, "$a && $b && $c / $d && $e", "((($a && $b) && ($c / $d)) && $e)");
    test_expression!(rand_148, "$a ** $b ^ $c | +$d -= $e & $f", "((($a ** $b) ^ $c) | (+ ($d -= ($e & $f))))");
    test_expression!(
        rand_149,
        "$a /= ++$b ^ $c ^ $d >> $e %= $f && $g",
        "($a /= (((++ $b) ^ $c) ^ ($d >> ($e %= ($f && $g)))))"
    );
    test_expression!(
        rand_150,
        "$a and $b .= $c . --$d <=> @$e /= $f << ++$g ?? $h",
        "($a and ($b .= (($c . (-- $d)) <=> (@ ($e /= (($f << (++ $g)) ?? $h))))))"
    );
    test_expression!(rand_151, "$a <=> $b + --$c and $d | $e or $f", "((($a <=> ($b + (-- $c))) and ($d | $e)) or $f)");
    test_expression!(rand_152, "$a -= $b and $c or $d != $e", "((($a -= $b) and $c) or ($d != $e))");
    test_expression!(
        rand_153,
        "$a xor $b = $c | ~$d or +$e or $f or -$g >> $h",
        "(((($a xor ($b = ($c | (~ $d)))) or (+ $e)) or $f) or ((- $g) >> $h))"
    );
    test_expression!(rand_154, "$a or $b << $c and $d != $e or $f", "(($a or (($b << $c) and ($d != $e))) or $f)");
    test_expression!(
        rand_155,
        "$a ?? $b & $c & ~$d >> $e ^ $f >> $g",
        "($a ?? ((($b & $c) & ((~ $d) >> $e)) ^ ($f >> $g)))"
    );
    test_expression!(
        rand_156,
        "$a xor $b & --$c << $d && $e ^ --$f ** ++$g % $h",
        "($a xor (($b & ((-- $c) << $d)) && ($e ^ (((-- $f) ** (++ $g)) % $h))))"
    );
    test_expression!(
        rand_157,
        "$a and --$b <=> $c xor $d ?? $e and $f ** $g",
        "(($a and ((-- $b) <=> $c)) xor (($d ?? $e) and ($f ** $g)))"
    );
    test_expression!(rand_158, "$a ^ --$b ?? $c || $d >> $e", "(($a ^ (-- $b)) ?? ($c || ($d >> $e)))");
    test_expression!(rand_159, "$a = $b xor $c & $d && -$e >> $f", "(($a = $b) xor (($c & $d) && ((- $e) >> $f)))");
    test_expression!(
        rand_160,
        "$a + !$b ** ~$c ^ $d % $e * $f != $g",
        "(($a + (! ($b ** (~ $c)))) ^ ((($d % $e) * $f) != $g))"
    );
    test_expression!(
        rand_161,
        "$a - $b or $c + $d >> $e + $f ** $g & $h ?? $i",
        "(($a - $b) or (((($c + $d) >> ($e + ($f ** $g))) & $h) ?? $i))"
    );
    test_expression!(
        rand_162,
        "$a % $b ** --$c <=> $d & $e < $f ?? $g ?? $h",
        "(((($a % ($b ** (-- $c))) <=> $d) & ($e < $f)) ?? ($g ?? $h))"
    );
    test_expression!(
        rand_163,
        "$a && $b < $c | ++$d or ++$e ^ $f",
        "(($a && (($b < $c) | (++ $d))) or ((++ $e) ^ $f))"
    );
    test_expression!(rand_164, "$a && $b | $c > $d . ~$e", "($a && ($b | ($c > ($d . (~ $e)))))");
    test_expression!(
        rand_165,
        "$a *= $b & $c ** $d && -$e && +$f > $g | $h",
        "($a *= ((($b & ($c ** $d)) && (- $e)) && (((+ $f) > $g) | $h)))"
    );
    test_expression!(rand_167, "$a ^ $b <=> $c && $d === $e & $f", "(($a ^ ($b <=> $c)) && (($d === $e) & $f))");
    test_expression!(rand_168, "$a && $b |= $c ^ $d && $e", "($a && ($b |= (($c ^ $d) && $e)))");
    test_expression!(
        rand_169,
        "$a ** -$b ^ $c or $d xor ~$e + $f xor $g",
        "((($a ** (- $b)) ^ $c) or (($d xor ((~ $e) + $f)) xor $g))"
    );
    test_expression!(
        rand_170,
        "$a | $b && ++$c | ++$d . $e & $f && $g + $h || $i",
        "(((($a | $b) && ((++ $c) | (((++ $d) . $e) & $f))) && ($g + $h)) || $i)"
    );
    test_expression!(rand_171, "$a ^ $b / @$c | $d += --$e", "(($a ^ ($b / (@ $c))) | ($d += (-- $e)))");
    test_expression!(
        rand_172,
        "$a or $b <=> $c / $d | $e / $f | $g",
        "($a or ((($b <=> ($c / $d)) | ($e / $f)) | $g))"
    );
    test_expression!(
        rand_174,
        "$a or $b && $c <= $d || $e | -$f ^ $g !== $h or $i",
        "(($a or (($b && ($c <= $d)) || ($e | ((- $f) ^ ($g !== $h))))) or $i)"
    );
    test_expression!(rand_175, "$a || $b >> $c ^ $d >> $e | $f", "($a || ((($b >> $c) ^ ($d >> $e)) | $f))");
    test_expression!(
        rand_176,
        "$a << ~$b & $c | $d xor $e ** $f xor $g",
        "((((($a << (~ $b)) & $c) | $d) xor ($e ** $f)) xor $g)"
    );
    test_expression!(rand_177, "$a or ++$b >= $c **= $d << $e", "($a or ((++ $b) >= ($c **= ($d << $e))))");
    test_expression!(rand_178, "$a % $b ** $c && $d . $e", "(($a % ($b ** $c)) && ($d . $e))");
    test_expression!(rand_179, "$a xor $b + $c <=> $d or $e", "(($a xor (($b + $c) <=> $d)) or $e)");
    test_expression!(
        rand_180,
        "$a | $b > $c and --$d or $e ^ $f ^ $g ?? $h",
        "((($a | ($b > $c)) and (-- $d)) or ((($e ^ $f) ^ $g) ?? $h))"
    );
    test_expression!(
        rand_181,
        "$a ^ $b && $c && $d -= --$e & $f and $g",
        "(((($a ^ $b) && $c) && ($d -= ((-- $e) & $f))) and $g)"
    );
    test_expression!(rand_182, "$a <=> $b /= +$c >= $d >> $e", "($a <=> ($b /= ((+ $c) >= ($d >> $e))))");
    test_expression!(rand_183, "$a || $b . $c * $d and $e << --$f", "(($a || ($b . ($c * $d))) and ($e << (-- $f)))");
    test_expression!(rand_184, "$a + $b or $c + $d || $e", "(($a + $b) or (($c + $d) || $e))");
    test_expression!(
        rand_186,
        "$a xor $b >> $c / $d == $e ?? --$f !== ++$g",
        "($a xor ((($b >> ($c / $d)) == $e) ?? ((-- $f) !== (++ $g))))"
    );
    test_expression!(
        rand_187,
        "$a & $b xor -$c << $d -= +$e && $f <=> $g",
        "(($a & $b) xor ((- $c) << ($d -= ((+ $e) && ($f <=> $g)))))"
    );
    test_expression!(rand_188, "$a += $b or $c << $d and $e ^ $f", "(($a += $b) or (($c << $d) and ($e ^ $f)))");
    test_expression!(
        rand_190,
        "$a | $b ^ !$c xor !$d % ++$e <=> $f ^ $g | $h ** !$i",
        "(($a | ($b ^ (! $c))) xor (((((! $d) % (++ $e)) <=> $f) ^ $g) | ($h ** (! $i))))"
    );
    test_expression!(
        rand_191,
        "$a & $b - +$c xor $d or $e ?? $f === $g | $h **= $i",
        "((($a & ($b - (+ $c))) xor $d) or ($e ?? (($f === $g) | ($h **= $i))))"
    );
    test_expression!(
        rand_192,
        "$a and $b or ++$c <=> $d - $e or $f & ++$g <=> $h",
        "((($a and $b) or ((++ $c) <=> ($d - $e))) or ($f & ((++ $g) <=> $h)))"
    );
    test_expression!(rand_193, "$a || $b == ~$c - $d | $e | $f", "($a || ((($b == ((~ $c) - $d)) | $e) | $f))");
    test_expression!(
        rand_194,
        "$a ^ $b && $c ?? $d and $e >= $f ^ $g && $h",
        "(((($a ^ $b) && $c) ?? $d) and ((($e >= $f) ^ $g) && $h))"
    );
    test_expression!(rand_195, "$a || $b != $c xor $d ** $e", "(($a || ($b != $c)) xor ($d ** $e))");
    test_expression!(
        rand_196,
        "$a xor $b || $c & $d xor $e <=> --$f ?? ~$g",
        "(($a xor ($b || ($c & $d))) xor (($e <=> (-- $f)) ?? (~ $g)))"
    );
    test_expression!(
        rand_197,
        "$a >> $b and ++$c >> $d ?? !$e . $f ^ $g <= $h && $i",
        "(($a >> $b) and (((++ $c) >> $d) ?? ((((! $e) . $f) ^ ($g <= $h)) && $i)))"
    );
    test_expression!(
        rand_198,
        "$a **= --$b and $c xor $d & $e ^ $f xor $g != $h",
        "(((($a **= (-- $b)) and $c) xor (($d & $e) ^ $f)) xor ($g != $h))"
    );
    test_expression!(rand_199, "$a or $b || $c ** $d >= $e", "($a or ($b || (($c ** $d) >= $e)))");

    smoke_test!(closing_tag_echo_tag, "<?= $a ?> <?= $b;");

    // Keywords in array access within string interpolation should be treated as identifiers
    smoke_test!(keyword_class_in_string_array_access, "<?php echo \"$arr[class]\";");
    smoke_test!(keyword_interface_in_string_array_access, "<?php echo \"$arr[interface]\";");
    smoke_test!(keyword_function_in_string_array_access, "<?php echo \"$arr[function]\";");
    smoke_test!(keyword_namespace_in_string_array_access, "<?php echo \"$arr[namespace]\";");
    smoke_test!(keyword_if_in_string_array_access, "<?php echo \"$arr[if]\";");
    smoke_test!(keyword_return_in_string_array_access, "<?php echo \"$arr[return]\";");
    smoke_test!(keyword_trait_in_string_array_access, "<?php echo \"$arr[trait]\";");
    smoke_test!(keyword_abstract_in_string_array_access, "<?php echo \"$arr[abstract]\";");
    smoke_test!(keyword_final_in_string_array_access, "<?php echo \"$arr[final]\";");
    smoke_test!(keyword_public_in_string_array_access, "<?php echo \"$arr[public]\";");
    smoke_test!(keyword_private_in_string_array_access, "<?php echo \"$arr[private]\";");
    smoke_test!(keyword_protected_in_string_array_access, "<?php echo \"$arr[protected]\";");

    // Namespaced class constants in braced string interpolation
    smoke_test!(namespaced_constant_in_braced_string_interpolation, r#"<?php echo "{$arr[A\B::VALUE]}";"#);
    smoke_test!(fully_qualified_constant_in_braced_string_interpolation, r#"<?php echo "{$arr[\Foo\Bar::VALUE]}";"#);
    smoke_test!(deeply_qualified_constant_in_braced_string_interpolation, r#"<?php echo "{$arr[A\B\C\D::VALUE]}";"#);
    smoke_test!(namespaced_static_method_in_braced_string_interpolation, r#"<?php echo "{$arr[A\B::method()]}";"#);

    smoke_test!(fcc_function, "<?php foo(...);");
    smoke_test!(fcc_method, "<?php $obj->method(...);");
    smoke_test!(fcc_static_method, "<?php Foo::method(...);");
    smoke_test!(fcc_clone, "<?php clone(...);");

    smoke_test!(pfa_single_placeholder, "<?php foo(?);");
    smoke_test!(pfa_single_variadic, "<?php bar(...);");
    smoke_test!(pfa_two_placeholders, "<?php foo(?, ?);");
    smoke_test!(pfa_three_placeholders, "<?php foo(?, ?, ?);");
    smoke_test!(pfa_mixed_value_placeholder, "<?php foo(1, ?);");
    smoke_test!(pfa_mixed_placeholder_value, "<?php foo(?, 2);");
    smoke_test!(pfa_mixed_complex, "<?php foo(1, ?, 3, ?);");
    smoke_test!(pfa_mixed_with_vars, "<?php foo($x, ?, $y, ?);");
    smoke_test!(pfa_named_args_only, "<?php foo(a: 1, b: 2);");
    smoke_test!(pfa_positional_then_named, "<?php foo(?, a: 2);");
    smoke_test!(pfa_positional_multiple_named, "<?php foo(1, ?, a: 3, b: 4);");
    smoke_test!(pfa_named_placeholder_single, "<?php foo(a: ?);");
    smoke_test!(pfa_named_placeholder_multiple, "<?php foo(a: ?, b: ?);");
    smoke_test!(pfa_named_mixed, "<?php foo(a: 1, b: ?);");
    smoke_test!(pfa_named_mixed_reverse, "<?php foo(a: ?, b: 2);");
    smoke_test!(pfa_positional_named_placeholder, "<?php foo(?, a: ?, b: 2);");
    smoke_test!(pfa_trailing_variadic_simple, "<?php foo(1, ...);");
    smoke_test!(pfa_placeholder_variadic, "<?php foo(?, ...);");
    smoke_test!(pfa_mixed_variadic, "<?php foo(1, ?, ...);");
    smoke_test!(pfa_named_variadic, "<?php foo(a: 1, ...);");
    smoke_test!(pfa_full_mix, "<?php foo(1, ?, a: 2, b: ?, ...);");
    smoke_test!(pfa_method_placeholder, "<?php $obj->method(?);");
    smoke_test!(pfa_method_mixed, "<?php $obj->method(1, ?);");
    smoke_test!(pfa_method_named, "<?php $obj->method(a: ?);");
    smoke_test!(pfa_method_variadic, "<?php $obj->method(?, ...);");
    smoke_test!(pfa_static_placeholder, "<?php Foo::bar(?);");
    smoke_test!(pfa_static_mixed, "<?php Foo::bar(?, 2, ?);");
    smoke_test!(pfa_static_named, "<?php Foo::bar(a: ?, b: 2);");
    smoke_test!(pfa_static_variadic, "<?php Foo::bar(1, ...);");
    smoke_test!(pfa_unpacking_positional, "<?php foo(...$args);");
    smoke_test!(pfa_unpacking_mixed, "<?php foo(?, ...$args);");
    smoke_test!(pfa_unpacking_named, "<?php foo(a: 1, ...$args);");
    smoke_test!(pfa_clone_placeholder, "<?php clone(?);");
    smoke_test!(pfa_clone_mixed, "<?php clone(?, ...);");
    smoke_test!(pfa_nested_call, "<?php foo(bar(?))(?);");
    smoke_test!(pfa_chained, "<?php $obj->method(?)->bindTo(?);");
    smoke_test!(pfa_array_element, "<?php $arr[0](?);");
    smoke_test!(binary_prefix_single_quoted, "<?php echo b'hello';");
    smoke_test!(binary_prefix_single_quoted_upper, "<?php echo B'hello';");
    smoke_test!(binary_prefix_double_quoted, "<?php echo b\"hello\";");
    smoke_test!(binary_prefix_double_quoted_upper, "<?php echo B\"hello\";");
    smoke_test!(binary_prefix_double_quoted_interpolated, "<?php echo b\"hello $name\";");
    smoke_test!(binary_prefix_heredoc, "<?php echo b<<<EOT\nhello\nEOT;");
    smoke_test!(binary_prefix_heredoc_double_quoted, "<?php echo b<<<\"EOT\"\nhello\nEOT;");
    smoke_test!(binary_prefix_nowdoc, "<?php echo b<<<'EOT'\nhello\nEOT;");
    smoke_test!(binary_prefix_escape_sequences, "<?php echo b\"hello\\nworld\";");
    smoke_test!(binary_prefix_single_quoted_escape, "<?php echo b'hello\\'world';");
}