vyre-libs 0.6.3

vyre Category A library ecosystem - pure-IR compositions over vyre-ops hardware primitives
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
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
//! GPU `#if` / `#elif` expression evaluator.
//!
//! Phase 17b.4: per-thread iterative shunting-yard parser over the
//! payload bytes of one `#if`/`#elif` directive. Composes the literal
//! and char-constant scanners from 17b.2/17b.3 and the defined-name
//! lookup table from 17b.1.
//!
//! ## Stack design
//!
//! Per-thread fixed-depth stacks (depth 16): value stack and operator
//! stack. Both backed by 16 let_bind slots each  -  this keeps the IR
//! free of shared-memory dependencies and gives every thread an
//! independent scratch area. Real `#if` expressions in production C
//! corpora are usually 4-8 deep (paren nesting + ternary). 16 is a
//! comfortable cap; deeper expressions force the kernel into a `done`
//! state with the failsafe value of 0.
//!
//! ## Operator codes
//!
//! Stored on the op stack as small u32 opcodes so the apply step can
//! switch on them. Higher numeric value = higher precedence (loosely
//! ordered to match the C precedence ladder; the apply loop uses the
//! `precedence_of(op)` helper rather than the raw code so re-ordering
//! is safe).
//!
//! ## Operand inputs (binding layout)
//!
//! Inputs:
//!   - `tok_starts` (U32)  -  per-token byte offset.
//!   - `tok_lens` (U32)  -  per-token byte length.
//!   - `directive_kinds` (U32)  -  output of `gpu_directive_metadata`.
//!   - `source` (U32 packed bytes for `gpu_if_expression`, raw U8 bytes for
//!     `gpu_if_expression_u8`)  -  original source bytes.
//!   - `macro_names_packed` (U32 packed bytes for `gpu_if_expression`, raw U8
//!     bytes for `gpu_if_expression_u8`), `macro_offsets` (U32),
//!     `macro_values` (U32)  -  GNU/Clang builtin perfect-hash table followed
//!     by the defined object-like macro integer values.
//!
//! Outputs:
//!   - `directive_values` (U32)  -  per-token value: `1`/`0` for
//!     `if`/`elif` rows; `0` for every other directive kind.
//!
//! ## Scope of this commit (17b.4 first cut)
//!
//! Operators supported:
//!   - Unary: `!`, `~`, `+`, `-`
//!   - Multiplicative: `*`, `/`, `%`
//!   - Additive: `+`, `-`
//!   - Shift: `<<`, `>>`
//!   - Relational: `<`, `<=`, `>`, `>=`
//!   - Equality: `==`, `!=`
//!   - Bitwise: `&`, `^`, `|`
//!   - Logical: `&&`, `||`
//!   - Ternary: `?:`
//!   - Parens: `(` `)`
//!   - `defined(X)` and `defined X`
//!   - Integer literals (via inlined logic from 17b.2)
//!   - Char constants (via inlined logic from 17b.3)
//!   - Identifier macro reference: bare ident → object-like integer macro value, or 0 if absent
//!
//! Tested under `tests/gpu_if_expression_roundtrip.rs` against the CPU
//! `reference_c_preprocessor_directive_metadata` for `if`/`elif` rows.

use crate::parsing::c::lex::tokens::{TOK_PP_ELIF, TOK_PP_IF};
use vyre::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};

mod abi;
mod apply;
mod builtin_calls;
mod byte_load;
mod opcodes;
mod stack;
#[cfg(test)]
mod tests;

use super::gpu_source_bytes::{
    safe_load_source_layout_byte_expr, source_buffer_element, source_byte_len_expr,
    SourceByteLayout,
};
pub use abi::{
    BINDING_DIRECTIVE_KINDS, BINDING_DIRECTIVE_VALUES, BINDING_MACRO_NAMES_PACKED,
    BINDING_MACRO_OFFSETS, BINDING_MACRO_VALUES, BINDING_SOURCE, BINDING_TOK_LENS,
    BINDING_TOK_STARTS, MAX_IDENT_LEN, MAX_PAYLOAD_BYTES, OP_ID, STACK_DEPTH,
};
use apply::apply_top_op;
use builtin_calls::{ident_hash_equals, push_has_builtin_call_parser};
use byte_load::safe_load_src_expr;
use opcodes::*;
use stack::{peek_stack, pop_stack, push_stack};
use vyre_primitives::hash::fnv1a::{fnv1a32_initial_expr, fnv1a32_update_byte_expr};

/// Build the 17b.4 `#if`/`#elif` evaluator `Program`.
///
/// `macro_names_len` and `num_macros` were previously construction-time
/// parameters baked into safe_load bounds and buffer counts. They are
/// no longer accepted: the kernel reads its macro count and packed-name
/// byte capacity at runtime via `Expr::buf_len("macro_offsets")` and
/// `Expr::buf_len("macro_names_packed")`. One program shape per process.
#[must_use]
pub fn gpu_if_expression(num_tokens: u32, source_len: u32) -> Program {
    gpu_if_expression_with_byte_layouts(
        num_tokens,
        source_len,
        SourceByteLayout::PackedU32,
        SourceByteLayout::PackedU32,
    )
}

/// Build the `#if`/`#elif` evaluator over raw `DataType::U8` source and macro
/// name bytes.
///
/// Binding order and runtime-sized buffer shape are identical to the packed ABI,
/// but byte buffers use `U8` so pipeline callers can avoid repacking retained
/// source rows and macro names into U32 words.
#[must_use]
pub fn gpu_if_expression_u8(num_tokens: u32, source_len: u32) -> Program {
    gpu_if_expression_with_byte_layouts(
        num_tokens,
        source_len,
        SourceByteLayout::RawU8,
        SourceByteLayout::RawU8,
    )
}

fn gpu_if_expression_with_byte_layouts(
    num_tokens: u32,
    source_len: u32,
    source_layout: SourceByteLayout,
    macro_names_layout: SourceByteLayout,
) -> Program {
    let _ = source_len;
    let source_byte_len = source_byte_len_expr("source", source_layout);
    let macro_names_byte_len = source_byte_len_expr("macro_names_packed", macro_names_layout);
    let t = Expr::var("t");

    let safe_load_src =
        |addr: Expr| -> Expr { safe_load_src_expr(source_layout, addr, source_byte_len.clone()) };
    let safe_load_macro_name = |addr: Expr| -> Expr {
        safe_load_source_layout_byte_expr(
            "macro_names_packed",
            macro_names_layout,
            addr,
            macro_names_byte_len.clone(),
        )
    };

    let mut body: Vec<Node> = Vec::new();
    body.push(Node::let_bind("t", Expr::InvocationId { axis: 0 }));
    body.push(Node::if_then(
        Expr::lt(t.clone(), Expr::u32(num_tokens)),
        {
            let mut inner: Vec<Node> = Vec::new();
            inner.push(Node::let_bind("kind", Expr::load("directive_kinds", t.clone())));
            inner.push(Node::let_bind("expr_value_out", Expr::u32(0)));
            inner.push(Node::let_bind("expr_invalid", Expr::u32(0)));

            // Only if/elif tokens get evaluated by THIS kernel.
            let mut evaluate: Vec<Node> = Vec::new();
            evaluate.push(Node::let_bind("tok_start", Expr::load("tok_starts", t.clone())));
            evaluate.push(Node::let_bind("tok_len", Expr::load("tok_lens", t.clone())));
            evaluate.push(Node::let_bind(
                "tok_end",
                Expr::add(Expr::var("tok_start"), Expr::var("tok_len")),
            ));
            // Step past leading whitespace, `#`, optional whitespace,
            // and the keyword (`if` = 2, `elif` = 4). After this
            // `scan_pos` points at the first byte of the payload.
            evaluate.push(Node::let_bind(
                "keyword_len",
                Expr::select(
                    Expr::eq(Expr::var("kind"), Expr::u32(TOK_PP_IF)),
                    Expr::u32(2),
                    Expr::u32(4),
                ),
            ));
            evaluate.push(Node::let_bind("scan_pos", Expr::var("tok_start")));
            for step in &["pre_hash", "pre_kw", "pre_payload"] {
                let done = format!("ws_done_{step}");
                evaluate.push(Node::let_bind(&done, Expr::u32(0)));
                evaluate.push(Node::loop_for(
                    &format!("ws_{step}"),
                    Expr::u32(0),
                    Expr::var("tok_len"),
                    vec![Node::if_then(
                        Expr::eq(Expr::var(&done), Expr::u32(0)),
                        vec![
                            Node::let_bind("wb", safe_load_src(Expr::var("scan_pos"))),
                            Node::let_bind(
                                "wb_ws",
                                Expr::select(
                                    Expr::or(
                                        Expr::or(
                                            Expr::eq(Expr::var("wb"), Expr::u32(b' ' as u32)),
                                            Expr::eq(Expr::var("wb"), Expr::u32(b'\t' as u32)),
                                        ),
                                        Expr::or(
                                            Expr::eq(Expr::var("wb"), Expr::u32(0x0B)),
                                            Expr::eq(Expr::var("wb"), Expr::u32(0x0C)),
                                        ),
                                    ),
                                    Expr::u32(1),
                                    Expr::u32(0),
                                ),
                            ),
                            Node::if_then_else(
                                Expr::eq(Expr::var("wb_ws"), Expr::u32(1)),
                                vec![Node::assign(
                                    "scan_pos",
                                    Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                )],
                                vec![Node::assign(&done, Expr::u32(1))],
                            ),
                        ],
                    )],
                ));
                if *step == "pre_hash" {
                    // Step past the `#`.
                    evaluate.push(Node::if_then(
                        Expr::eq(safe_load_src(Expr::var("scan_pos")), Expr::u32(b'#' as u32)),
                        vec![Node::assign(
                            "scan_pos",
                            Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                        )],
                    ));
                } else if *step == "pre_kw" {
                    // Step past keyword bytes.
                    evaluate.push(Node::assign(
                        "scan_pos",
                        Expr::add(Expr::var("scan_pos"), Expr::var("keyword_len")),
                    ));
                }
            }

            // ---------- Initialise stacks ----------
            evaluate.push(Node::let_bind("vsp", Expr::u32(0)));
            evaluate.push(Node::let_bind("osp", Expr::u32(0)));
            for slot in 0..STACK_DEPTH {
                evaluate.push(Node::let_bind(&format!("val_stack_{slot}"), Expr::u32(0)));
                evaluate.push(Node::let_bind(&format!("op_stack_{slot}"), Expr::u32(0)));
            }
            // Last-token-was-value flag  -  drives unary vs binary
            // disambiguation for `+` / `-`.
            evaluate.push(Node::let_bind("last_was_value", Expr::u32(0)));
            evaluate.push(Node::let_bind("scan_done", Expr::u32(0)));

            // ---------- Main scan loop ----------
            evaluate.push(Node::loop_for(
                "scan_iter",
                Expr::u32(0),
                Expr::var("tok_len"),
                vec![Node::if_then(
                    Expr::and(
                        Expr::eq(Expr::var("scan_done"), Expr::u32(0)),
                        Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                    ),
                    {
                        let mut iter_body: Vec<Node> = Vec::new();
                        // Skip horizontal whitespace. Production pipeline
                        // inputs have already passed phase-2 line splicing
                        // and phase-3 comment replacement, so this evaluator
                        // does not duplicate comment scanning in the hot loop.
                        iter_body.push(Node::let_bind("inner_ws_done", Expr::u32(0)));
                        iter_body.push(Node::loop_for(
                            "ws_skip",
                            Expr::u32(0),
                            Expr::var("tok_len"),
                            vec![Node::if_then(
                                Expr::and(
                                    Expr::eq(Expr::var("inner_ws_done"), Expr::u32(0)),
                                    Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                ),
                                vec![
                                    Node::let_bind("wb2", safe_load_src(Expr::var("scan_pos"))),
                                    Node::let_bind(
                                        "wb2_ws",
                                        Expr::select(
                                            Expr::or(
                                                Expr::or(
                                                    Expr::eq(Expr::var("wb2"), Expr::u32(b' ' as u32)),
                                                    Expr::eq(Expr::var("wb2"), Expr::u32(b'\t' as u32)),
                                                ),
                                                Expr::or(
                                                    Expr::eq(Expr::var("wb2"), Expr::u32(0x0B)),
                                                    Expr::eq(Expr::var("wb2"), Expr::u32(0x0C)),
                                                ),
                                            ),
                                            Expr::u32(1),
                                            Expr::u32(0),
                                        ),
                                    ),
                                    Node::if_then_else(
                                        Expr::eq(Expr::var("wb2_ws"), Expr::u32(1)),
                                        vec![Node::assign(
                                            "scan_pos",
                                            Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                        )],
                                        vec![Node::assign("inner_ws_done", Expr::u32(1))],
                                    ),
                                ],
                            )],
                        ));
                        // End of payload?
                        iter_body.push(Node::if_then(
                            Expr::ge(Expr::var("scan_pos"), Expr::var("tok_end")),
                            vec![Node::assign("scan_done", Expr::u32(1))],
                        ));
                        // Read the next byte.
                        iter_body.push(Node::if_then(
                            Expr::eq(Expr::var("scan_done"), Expr::u32(0)),
                            {
                                let mut classify: Vec<Node> = Vec::new();
                                classify.push(Node::let_bind("c", safe_load_src(Expr::var("scan_pos"))));
                                classify.push(Node::let_bind("c1", safe_load_src(Expr::add(Expr::var("scan_pos"), Expr::u32(1)))));

                                // ---- Integer literal ----
                                classify.push(Node::let_bind(
                                    "is_dec_digit",
                                    Expr::select(
                                        Expr::and(
                                            Expr::ge(Expr::var("c"), Expr::u32(b'0' as u32)),
                                            Expr::le(Expr::var("c"), Expr::u32(b'9' as u32)),
                                        ),
                                        Expr::u32(1),
                                        Expr::u32(0),
                                    ),
                                ));
                                classify.push(Node::if_then(
                                    Expr::eq(Expr::var("is_dec_digit"), Expr::u32(1)),
                                    {
                                        let mut lit_nodes: Vec<Node> = Vec::new();
                                        // Inline integer-literal scan
                                        // (mirrors gpu_int_literal_scan
                                        // semantics; simplified to u32
                                        // wrapping). Detect radix.
                                        lit_nodes.push(Node::let_bind(
                                            "is_hex",
                                            Expr::select(
                                                Expr::and(
                                                    Expr::eq(Expr::var("c"), Expr::u32(b'0' as u32)),
                                                    Expr::or(
                                                        Expr::eq(Expr::var("c1"), Expr::u32(b'x' as u32)),
                                                        Expr::eq(Expr::var("c1"), Expr::u32(b'X' as u32)),
                                                    ),
                                                ),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        lit_nodes.push(Node::let_bind(
                                            "is_bin",
                                            Expr::select(
                                                Expr::and(
                                                    Expr::eq(Expr::var("c"), Expr::u32(b'0' as u32)),
                                                    Expr::or(
                                                        Expr::eq(Expr::var("c1"), Expr::u32(b'b' as u32)),
                                                        Expr::eq(Expr::var("c1"), Expr::u32(b'B' as u32)),
                                                    ),
                                                ),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        lit_nodes.push(Node::let_bind(
                                            "is_oct",
                                            Expr::select(
                                                Expr::and(
                                                    Expr::eq(Expr::var("c"), Expr::u32(b'0' as u32)),
                                                    Expr::and(
                                                        Expr::eq(Expr::var("is_hex"), Expr::u32(0)),
                                                        Expr::eq(Expr::var("is_bin"), Expr::u32(0)),
                                                    ),
                                                ),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        lit_nodes.push(Node::let_bind(
                                            "lit_radix",
                                            Expr::select(
                                                Expr::eq(Expr::var("is_hex"), Expr::u32(1)),
                                                Expr::u32(16),
                                                Expr::select(
                                                    Expr::eq(Expr::var("is_bin"), Expr::u32(1)),
                                                    Expr::u32(2),
                                                    Expr::select(
                                                        Expr::eq(Expr::var("is_oct"), Expr::u32(1)),
                                                        Expr::u32(8),
                                                        Expr::u32(10),
                                                    ),
                                                ),
                                            ),
                                        ));
                                        lit_nodes.push(Node::let_bind(
                                            "lit_skip",
                                            Expr::select(
                                                Expr::or(
                                                    Expr::eq(Expr::var("is_hex"), Expr::u32(1)),
                                                    Expr::eq(Expr::var("is_bin"), Expr::u32(1)),
                                                ),
                                                Expr::u32(2),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        lit_nodes.push(Node::assign(
                                            "scan_pos",
                                            Expr::add(Expr::var("scan_pos"), Expr::var("lit_skip")),
                                        ));
                                        lit_nodes.push(Node::let_bind("lit_value", Expr::u32(0)));
                                        lit_nodes.push(Node::let_bind("lit_done", Expr::u32(0)));
                                        lit_nodes.push(Node::loop_for(
                                            "lit_d",
                                            Expr::u32(0),
                                            Expr::var("tok_len"),
                                            vec![Node::if_then(
                                                Expr::and(
                                                    Expr::eq(Expr::var("lit_done"), Expr::u32(0)),
                                                    Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                ),
                                                vec![
                                                    Node::let_bind("ldb", safe_load_src(Expr::var("scan_pos"))),
                                                    Node::let_bind(
                                                        "ldb1",
                                                        safe_load_src(Expr::add(
                                                            Expr::var("scan_pos"),
                                                            Expr::u32(1),
                                                        )),
                                                    ),
                                                    Node::let_bind(
                                                        "ld_dec",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::ge(Expr::var("ldb"), Expr::u32(b'0' as u32)),
                                                                Expr::le(Expr::var("ldb"), Expr::u32(b'9' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld_lc",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::ge(Expr::var("ldb"), Expr::u32(b'a' as u32)),
                                                                Expr::le(Expr::var("ldb"), Expr::u32(b'f' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld_uc",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::ge(Expr::var("ldb"), Expr::u32(b'A' as u32)),
                                                                Expr::le(Expr::var("ldb"), Expr::u32(b'F' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld_v",
                                                        Expr::select(
                                                            Expr::eq(Expr::var("ld_dec"), Expr::u32(1)),
                                                            Expr::sub(Expr::var("ldb"), Expr::u32(b'0' as u32)),
                                                            Expr::select(
                                                                Expr::eq(Expr::var("ld_lc"), Expr::u32(1)),
                                                                Expr::add(
                                                                    Expr::sub(Expr::var("ldb"), Expr::u32(b'a' as u32)),
                                                                    Expr::u32(10),
                                                                ),
                                                                Expr::select(
                                                                    Expr::eq(Expr::var("ld_uc"), Expr::u32(1)),
                                                                    Expr::add(
                                                                        Expr::sub(Expr::var("ldb"), Expr::u32(b'A' as u32)),
                                                                        Expr::u32(10),
                                                                    ),
                                                                    Expr::u32(99),
                                                                ),
                                                            ),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld1_dec",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::ge(Expr::var("ldb1"), Expr::u32(b'0' as u32)),
                                                                Expr::le(Expr::var("ldb1"), Expr::u32(b'9' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld1_lc",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::ge(Expr::var("ldb1"), Expr::u32(b'a' as u32)),
                                                                Expr::le(Expr::var("ldb1"), Expr::u32(b'f' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld1_uc",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::ge(Expr::var("ldb1"), Expr::u32(b'A' as u32)),
                                                                Expr::le(Expr::var("ldb1"), Expr::u32(b'F' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "ld1_v",
                                                        Expr::select(
                                                            Expr::eq(Expr::var("ld1_dec"), Expr::u32(1)),
                                                            Expr::sub(Expr::var("ldb1"), Expr::u32(b'0' as u32)),
                                                            Expr::select(
                                                                Expr::eq(Expr::var("ld1_lc"), Expr::u32(1)),
                                                                Expr::add(
                                                                    Expr::sub(Expr::var("ldb1"), Expr::u32(b'a' as u32)),
                                                                    Expr::u32(10),
                                                                ),
                                                                Expr::select(
                                                                    Expr::eq(Expr::var("ld1_uc"), Expr::u32(1)),
                                                                    Expr::add(
                                                                        Expr::sub(Expr::var("ldb1"), Expr::u32(b'A' as u32)),
                                                                        Expr::u32(10),
                                                                    ),
                                                                    Expr::u32(99),
                                                                ),
                                                            ),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "lit_separator",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::eq(Expr::var("ldb"), Expr::u32(b'\'' as u32)),
                                                                Expr::lt(Expr::var("ld1_v"), Expr::var("lit_radix")),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::if_then_else(
                                                        Expr::or(
                                                            Expr::lt(Expr::var("ld_v"), Expr::var("lit_radix")),
                                                            Expr::eq(Expr::var("lit_separator"), Expr::u32(1)),
                                                        ),
                                                        vec![
                                                            Node::assign(
                                                                "lit_value",
                                                                Expr::select(
                                                                    Expr::eq(Expr::var("lit_separator"), Expr::u32(1)),
                                                                    Expr::var("lit_value"),
                                                                    Expr::add(
                                                                        Expr::mul(
                                                                            Expr::var("lit_value"),
                                                                            Expr::var("lit_radix"),
                                                                        ),
                                                                        Expr::var("ld_v"),
                                                                    ),
                                                                ),
                                                            ),
                                                            Node::assign(
                                                                "scan_pos",
                                                                Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                            ),
                                                        ],
                                                        vec![Node::assign("lit_done", Expr::u32(1))],
                                                    ),
                                                ],
                                            )],
                                        ));
                                        // Skip suffix u/U/l/L/z/Z/wb/WB (up to 4 loop iterations).
                                        lit_nodes.push(Node::let_bind("suf_done", Expr::u32(0)));
                                        lit_nodes.push(Node::loop_for(
                                            "lit_suf",
                                            Expr::u32(0),
                                            Expr::u32(4),
                                            vec![Node::if_then(
                                                Expr::and(
                                                    Expr::eq(Expr::var("suf_done"), Expr::u32(0)),
                                                    Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                ),
                                                vec![
                                                    Node::let_bind("sfb", safe_load_src(Expr::var("scan_pos"))),
                                                    Node::let_bind(
                                                        "sfb1",
                                                        safe_load_src(Expr::add(
                                                            Expr::var("scan_pos"),
                                                            Expr::u32(1),
                                                        )),
                                                    ),
                                                    Node::let_bind(
                                                        "is_single_suf",
                                                        Expr::select(
                                                            Expr::or(
                                                                Expr::or(
                                                                    Expr::eq(Expr::var("sfb"), Expr::u32(b'u' as u32)),
                                                                    Expr::eq(Expr::var("sfb"), Expr::u32(b'U' as u32)),
                                                                ),
                                                                Expr::or(
                                                                    Expr::eq(Expr::var("sfb"), Expr::u32(b'l' as u32)),
                                                                    Expr::eq(Expr::var("sfb"), Expr::u32(b'L' as u32)),
                                                                ),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "is_z_suf",
                                                        Expr::select(
                                                            Expr::or(
                                                                Expr::eq(Expr::var("sfb"), Expr::u32(b'z' as u32)),
                                                                Expr::eq(Expr::var("sfb"), Expr::u32(b'Z' as u32)),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::let_bind(
                                                        "is_wb_suf",
                                                        Expr::select(
                                                            Expr::and(
                                                                Expr::or(
                                                                    Expr::eq(Expr::var("sfb"), Expr::u32(b'w' as u32)),
                                                                    Expr::eq(Expr::var("sfb"), Expr::u32(b'W' as u32)),
                                                                ),
                                                                Expr::or(
                                                                    Expr::eq(Expr::var("sfb1"), Expr::u32(b'b' as u32)),
                                                                    Expr::eq(Expr::var("sfb1"), Expr::u32(b'B' as u32)),
                                                                ),
                                                            ),
                                                            Expr::u32(1),
                                                            Expr::u32(0),
                                                        ),
                                                    ),
                                                    Node::if_then_else(
                                                        Expr::or(
                                                            Expr::or(
                                                                Expr::eq(Expr::var("is_single_suf"), Expr::u32(1)),
                                                                Expr::eq(Expr::var("is_z_suf"), Expr::u32(1)),
                                                            ),
                                                            Expr::eq(Expr::var("is_wb_suf"), Expr::u32(1)),
                                                        ),
                                                        vec![Node::assign(
                                                            "scan_pos",
                                                            Expr::add(
                                                                Expr::var("scan_pos"),
                                                                Expr::select(
                                                                    Expr::eq(Expr::var("is_wb_suf"), Expr::u32(1)),
                                                                    Expr::u32(2),
                                                                    Expr::u32(1),
                                                                ),
                                                            ),
                                                        )],
                                                        vec![Node::assign("suf_done", Expr::u32(1))],
                                                    ),
                                                ],
                                            )],
                                        ));
                                        lit_nodes.extend(push_stack("val_stack", "vsp", Expr::var("lit_value")));
                                        lit_nodes.push(Node::assign("last_was_value", Expr::u32(1)));
                                        lit_nodes
                                    },
                                ));

                                // ---- Identifier (defined / bare macro) ----
                                classify.push(Node::let_bind(
                                    "is_alpha_start",
                                    Expr::select(
                                        Expr::or(
                                            Expr::or(
                                                Expr::and(
                                                    Expr::ge(Expr::var("c"), Expr::u32(b'a' as u32)),
                                                    Expr::le(Expr::var("c"), Expr::u32(b'z' as u32)),
                                                ),
                                                Expr::and(
                                                    Expr::ge(Expr::var("c"), Expr::u32(b'A' as u32)),
                                                    Expr::le(Expr::var("c"), Expr::u32(b'Z' as u32)),
                                                ),
                                            ),
                                            Expr::eq(Expr::var("c"), Expr::u32(b'_' as u32)),
                                        ),
                                        Expr::u32(1),
                                        Expr::u32(0),
                                    ),
                                ));
                                classify.push(Node::if_then(
                                    Expr::and(
                                        Expr::eq(Expr::var("is_alpha_start"), Expr::u32(1)),
                                        Expr::eq(Expr::var("is_dec_digit"), Expr::u32(0)),
                                    ),
                                    {
                                        let mut id_nodes: Vec<Node> = Vec::new();
                                        id_nodes.push(Node::let_bind("ident_start", Expr::add(Expr::var("scan_pos"), Expr::u32(0))));
                                        id_nodes.push(Node::let_bind("ident_len", Expr::u32(0)));
                                        id_nodes.push(Node::loop_for(
                                            "id_read",
                                            Expr::u32(0),
                                            Expr::select(
                                                Expr::lt(Expr::var("ident_start"), Expr::var("tok_end")),
                                                Expr::sub(Expr::var("tok_end"), Expr::var("ident_start")),
                                                Expr::u32(0),
                                            ),
                                            vec![Node::if_then(
                                                Expr::eq(Expr::var("ident_len"), Expr::var("id_read")),
                                                vec![
                                                    Node::let_bind(
                                                        "id_pos",
                                                        Expr::add(Expr::var("ident_start"), Expr::var("id_read")),
                                                    ),
                                                    Node::if_then(
                                                        Expr::lt(Expr::var("id_pos"), Expr::var("tok_end")),
                                                        vec![
                                                            Node::let_bind("idb", safe_load_src(Expr::var("id_pos"))),
                                                            Node::let_bind(
                                                                "id_alpha",
                                                                Expr::select(
                                                                    Expr::or(
                                                                        Expr::and(
                                                                            Expr::ge(Expr::var("idb"), Expr::u32(b'a' as u32)),
                                                                            Expr::le(Expr::var("idb"), Expr::u32(b'z' as u32)),
                                                                        ),
                                                                        Expr::and(
                                                                            Expr::ge(Expr::var("idb"), Expr::u32(b'A' as u32)),
                                                                            Expr::le(Expr::var("idb"), Expr::u32(b'Z' as u32)),
                                                                        ),
                                                                    ),
                                                                    Expr::u32(1),
                                                                    Expr::u32(0),
                                                                ),
                                                            ),
                                                            Node::let_bind(
                                                                "id_digit",
                                                                Expr::select(
                                                                    Expr::and(
                                                                        Expr::ge(Expr::var("idb"), Expr::u32(b'0' as u32)),
                                                                        Expr::le(Expr::var("idb"), Expr::u32(b'9' as u32)),
                                                                    ),
                                                                    Expr::u32(1),
                                                                    Expr::u32(0),
                                                                ),
                                                            ),
                                                            Node::let_bind(
                                                                "id_under",
                                                                Expr::select(
                                                                    Expr::eq(Expr::var("idb"), Expr::u32(b'_' as u32)),
                                                                    Expr::u32(1),
                                                                    Expr::u32(0),
                                                                ),
                                                            ),
                                                            Node::let_bind(
                                                                "id_cont",
                                                                Expr::select(
                                                                    Expr::or(
                                                                        Expr::or(
                                                                            Expr::eq(Expr::var("id_alpha"), Expr::u32(1)),
                                                                            Expr::eq(Expr::var("id_digit"), Expr::u32(1)),
                                                                        ),
                                                                        Expr::eq(Expr::var("id_under"), Expr::u32(1)),
                                                                    ),
                                                                    Expr::u32(1),
                                                                    Expr::u32(0),
                                                                ),
                                                            ),
                                                            Node::if_then(
                                                                Expr::eq(Expr::var("id_cont"), Expr::u32(1)),
                                                                vec![Node::assign(
                                                                    "ident_len",
                                                                    Expr::add(Expr::var("ident_len"), Expr::u32(1)),
                                                                )],
                                                            ),
                                                        ],
                                                    ),
                                                ],
                                            )],
                                        ));
                                        id_nodes.push(Node::assign(
                                            "scan_pos",
                                            Expr::add(Expr::var("ident_start"), Expr::var("ident_len")),
                                        ));
                                        id_nodes.push(Node::let_bind("ident_hash", fnv1a32_initial_expr()));
                                        id_nodes.push(Node::loop_for(
                                            "ident_hash_bytes",
                                            Expr::u32(0),
                                            Expr::var("ident_len"),
                                            vec![
                                                Node::let_bind(
                                                    "idhb",
                                                    safe_load_src(Expr::add(
                                                        Expr::var("ident_start"),
                                                        Expr::var("ident_hash_bytes"),
                                                    )),
                                                ),
                                                Node::assign(
                                                    "ident_hash",
                                                    fnv1a32_update_byte_expr(
                                                        Expr::var("ident_hash"),
                                                        Expr::var("idhb"),
                                                    ),
                                                ),
                                            ],
                                        ));
                                        // Preprocessor operators that look like identifiers.
                                        id_nodes.push(Node::let_bind(
                                            "is_defined_kw",
                                            Expr::select(
                                                ident_hash_equals(b"defined"),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        id_nodes.push(Node::let_bind(
                                            "is_has_builtin_kw",
                                            Expr::select(
                                                ident_hash_equals(b"__has_builtin"),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        id_nodes.push(Node::let_bind(
                                            "is_has_constexpr_builtin_kw",
                                            Expr::select(
                                                ident_hash_equals(b"__has_constexpr_builtin"),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        id_nodes.push(Node::let_bind(
                                            "is_has_x_kw",
                                            Expr::select(
                                                Expr::or(
                                                    Expr::or(
                                                        ident_hash_equals(b"__has_include"),
                                                        ident_hash_equals(b"__has_include_next"),
                                                    ),
                                                    Expr::or(
                                                        Expr::or(
                                                            ident_hash_equals(b"__has_embed"),
                                                            ident_hash_equals(b"__has_attribute"),
                                                        ),
                                                        Expr::or(
                                                            Expr::or(
                                                                ident_hash_equals(b"__has_c_attribute"),
                                                                ident_hash_equals(b"__has_cpp_attribute"),
                                                            ),
                                                            Expr::or(
                                                                Expr::or(
                                                                    ident_hash_equals(b"__has_declspec_attribute"),
                                                                    ident_hash_equals(b"__has_feature"),
                                                                ),
                                                                ident_hash_equals(b"__has_extension"),
                                                            ),
                                                        ),
                                                    ),
                                                ),
                                                Expr::u32(1),
                                                Expr::u32(0),
                                            ),
                                        ));
                                        // For both "defined X" and "defined(X)" we need to
                                        // capture the inner identifier and look it up.
                                        id_nodes.push(Node::if_then_else(
                                            Expr::eq(Expr::var("is_defined_kw"), Expr::u32(1)),
                                            {
                                                let mut def_nodes: Vec<Node> = Vec::new();
                                                // Skip whitespace.
                                                def_nodes.push(Node::let_bind("def_ws_done", Expr::u32(0)));
                                                def_nodes.push(Node::loop_for(
                                                    "def_ws",
                                                    Expr::u32(0),
                                                    Expr::var("tok_len"),
                                                    vec![Node::if_then(
                                                        Expr::and(
                                                            Expr::eq(Expr::var("def_ws_done"), Expr::u32(0)),
                                                            Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                        ),
                                                        vec![
                                                            Node::let_bind("dwsb", safe_load_src(Expr::var("scan_pos"))),
                                                            Node::let_bind(
                                                                "dws_is_ws",
                                                                Expr::select(
                                                                    Expr::or(
                                                                        Expr::or(
                                                                            Expr::eq(Expr::var("dwsb"), Expr::u32(b' ' as u32)),
                                                                            Expr::eq(Expr::var("dwsb"), Expr::u32(b'\t' as u32)),
                                                                        ),
                                                                        Expr::or(
                                                                            Expr::eq(Expr::var("dwsb"), Expr::u32(0x0B)),
                                                                            Expr::eq(Expr::var("dwsb"), Expr::u32(0x0C)),
                                                                        ),
                                                                    ),
                                                                    Expr::u32(1),
                                                                    Expr::u32(0),
                                                                ),
                                                            ),
                                                            Node::if_then_else(
                                                                Expr::eq(Expr::var("dws_is_ws"), Expr::u32(1)),
                                                                vec![Node::assign(
                                                                    "scan_pos",
                                                                    Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                                )],
                                                                vec![Node::assign("def_ws_done", Expr::u32(1))],
                                                            ),
                                                        ],
                                                    )],
                                                ));
                                                // Optional `(`.
                                                def_nodes.push(Node::let_bind("def_open", safe_load_src(Expr::var("scan_pos"))));
                                                def_nodes.push(Node::let_bind(
                                                    "had_paren",
                                                    Expr::select(
                                                        Expr::eq(Expr::var("def_open"), Expr::u32(b'(' as u32)),
                                                        Expr::u32(1),
                                                        Expr::u32(0),
                                                    ),
                                                ));
                                                def_nodes.push(Node::if_then(
                                                    Expr::eq(Expr::var("had_paren"), Expr::u32(1)),
                                                    vec![Node::assign(
                                                        "scan_pos",
                                                        Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                    )],
                                                ));
                                                // Skip ws after `(`.
                                                def_nodes.push(Node::let_bind("def_ws2_done", Expr::u32(0)));
                                                def_nodes.push(Node::loop_for(
                                                    "def_ws2",
                                                    Expr::u32(0),
                                                    Expr::var("tok_len"),
                                                    vec![Node::if_then(
                                                        Expr::and(
                                                            Expr::eq(Expr::var("def_ws2_done"), Expr::u32(0)),
                                                            Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                        ),
                                                        vec![
                                                            Node::let_bind("dws2b", safe_load_src(Expr::var("scan_pos"))),
                                                            Node::let_bind(
                                                                "dws2_is_ws",
                                                                Expr::select(
                                                                    Expr::or(
                                                                        Expr::or(
                                                                            Expr::eq(Expr::var("dws2b"), Expr::u32(b' ' as u32)),
                                                                            Expr::eq(Expr::var("dws2b"), Expr::u32(b'\t' as u32)),
                                                                        ),
                                                                        Expr::or(
                                                                            Expr::eq(Expr::var("dws2b"), Expr::u32(0x0B)),
                                                                            Expr::eq(Expr::var("dws2b"), Expr::u32(0x0C)),
                                                                        ),
                                                                    ),
                                                                    Expr::u32(1),
                                                                    Expr::u32(0),
                                                                ),
                                                            ),
                                                            Node::if_then_else(
                                                                Expr::eq(Expr::var("dws2_is_ws"), Expr::u32(1)),
                                                                vec![Node::assign(
                                                                    "scan_pos",
                                                                    Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                                )],
                                                                vec![Node::assign("def_ws2_done", Expr::u32(1))],
                                                            ),
                                                        ],
                                                    )],
                                                ));
                                                // Capture inner ident.
                                                def_nodes.push(Node::let_bind("inner_start", Expr::var("scan_pos")));
                                                def_nodes.push(Node::let_bind("inner_len", Expr::u32(0)));
                                                def_nodes.push(Node::loop_for(
                                                    "inner_id",
                                                    Expr::u32(0),
                                                    Expr::select(
                                                        Expr::lt(Expr::var("inner_start"), Expr::var("tok_end")),
                                                        Expr::sub(Expr::var("tok_end"), Expr::var("inner_start")),
                                                        Expr::u32(0),
                                                    ),
                                                    vec![Node::if_then(
                                                        Expr::eq(Expr::var("inner_len"), Expr::var("inner_id")),
                                                        vec![
                                                            Node::let_bind(
                                                                "ip",
                                                                Expr::add(Expr::var("inner_start"), Expr::var("inner_id")),
                                                            ),
                                                            Node::if_then(
                                                                Expr::lt(Expr::var("ip"), Expr::var("tok_end")),
                                                                vec![
                                                                    Node::let_bind("ib", safe_load_src(Expr::var("ip"))),
                                                                    Node::let_bind(
                                                                        "ib_alpha",
                                                                        Expr::select(
                                                                            Expr::or(
                                                                                Expr::and(
                                                                                    Expr::ge(Expr::var("ib"), Expr::u32(b'a' as u32)),
                                                                                    Expr::le(Expr::var("ib"), Expr::u32(b'z' as u32)),
                                                                                ),
                                                                                Expr::and(
                                                                                    Expr::ge(Expr::var("ib"), Expr::u32(b'A' as u32)),
                                                                                    Expr::le(Expr::var("ib"), Expr::u32(b'Z' as u32)),
                                                                                ),
                                                                            ),
                                                                            Expr::u32(1),
                                                                            Expr::u32(0),
                                                                        ),
                                                                    ),
                                                                    Node::let_bind(
                                                                        "ib_digit",
                                                                        Expr::select(
                                                                            Expr::and(
                                                                                Expr::ge(Expr::var("ib"), Expr::u32(b'0' as u32)),
                                                                                Expr::le(Expr::var("ib"), Expr::u32(b'9' as u32)),
                                                                            ),
                                                                            Expr::u32(1),
                                                                            Expr::u32(0),
                                                                        ),
                                                                    ),
                                                                    Node::let_bind(
                                                                        "ib_under",
                                                                        Expr::select(
                                                                            Expr::eq(Expr::var("ib"), Expr::u32(b'_' as u32)),
                                                                            Expr::u32(1),
                                                                            Expr::u32(0),
                                                                        ),
                                                                    ),
                                                                    Node::let_bind(
                                                                        "ib_cont",
                                                                        Expr::select(
                                                                            Expr::or(
                                                                                Expr::or(
                                                                                    Expr::eq(Expr::var("ib_alpha"), Expr::u32(1)),
                                                                                    Expr::eq(Expr::var("ib_digit"), Expr::u32(1)),
                                                                                ),
                                                                                Expr::eq(Expr::var("ib_under"), Expr::u32(1)),
                                                                            ),
                                                                            Expr::u32(1),
                                                                            Expr::u32(0),
                                                                        ),
                                                                    ),
                                                                    Node::if_then(
                                                                        Expr::eq(Expr::var("ib_cont"), Expr::u32(1)),
                                                                        vec![Node::assign(
                                                                            "inner_len",
                                                                            Expr::add(Expr::var("inner_len"), Expr::u32(1)),
                                                                        )],
                                                                    ),
                                                                ],
                                                            ),
                                                        ],
                                                    )],
                                                ));
                                                def_nodes.push(Node::assign(
                                                    "scan_pos",
                                                    Expr::add(Expr::var("inner_start"), Expr::var("inner_len")),
                                                ));
                                                // Lookup against macro table.
                                                def_nodes.push(Node::let_bind("def_found", Expr::u32(0)));
                                                def_nodes.push(Node::loop_for(
                                                    "dm",
                                                    Expr::u32(0),
                                                    Expr::select(
                                                        Expr::gt(Expr::buf_len("macro_offsets"), Expr::u32(0)),
                                                        Expr::sub(Expr::buf_len("macro_offsets"), Expr::u32(1)),
                                                        Expr::u32(0),
                                                    ),
                                                    vec![Node::if_then(
                                                        Expr::eq(Expr::var("def_found"), Expr::u32(0)),
                                                        vec![
                                                            Node::let_bind(
                                                                "dm_s",
                                                                Expr::load("macro_offsets", Expr::var("dm")),
                                                            ),
                                                            Node::let_bind(
                                                                "dm_e",
                                                                Expr::load("macro_offsets", Expr::add(Expr::var("dm"), Expr::u32(1))),
                                                            ),
                                                            Node::let_bind(
                                                                "dm_l",
                                                                Expr::sub(Expr::var("dm_e"), Expr::var("dm_s")),
                                                            ),
                                                            Node::if_then(
                                                                Expr::eq(Expr::var("dm_l"), Expr::var("inner_len")),
                                                                vec![
                                                                    Node::let_bind("dm_match", Expr::u32(1)),
                                                                    Node::loop_for(
                                                                        "dmk",
                                                                        Expr::u32(0),
                                                                        Expr::var("inner_len"),
                                                                        vec![
                                                                            Node::let_bind(
                                                                                "dms_b",
                                                                                safe_load_src(Expr::add(Expr::var("inner_start"), Expr::var("dmk"))),
                                                                            ),
                                                                            Node::let_bind(
                                                                                "dmm_b",
                                                                                safe_load_macro_name(Expr::add(Expr::var("dm_s"), Expr::var("dmk"))),
                                                                            ),
                                                                            Node::if_then(
                                                                                Expr::ne(Expr::var("dms_b"), Expr::var("dmm_b")),
                                                                                vec![Node::assign("dm_match", Expr::u32(0))],
                                                                            ),
                                                                        ],
                                                                    ),
                                                                    Node::if_then(
                                                                        Expr::eq(Expr::var("dm_match"), Expr::u32(1)),
                                                                        vec![Node::assign("def_found", Expr::u32(1))],
                                                                    ),
                                                                ],
                                                            ),
                                                        ],
                                                    )],
                                                ));
                                                // Skip closing `)` if there was an opener.
                                                def_nodes.push(Node::if_then(
                                                    Expr::eq(Expr::var("had_paren"), Expr::u32(1)),
                                                    vec![
                                                        // Skip ws.
                                                        Node::let_bind("def_ws3_done", Expr::u32(0)),
                                                        Node::loop_for(
                                                            "def_ws3",
                                                            Expr::u32(0),
                                                            Expr::var("tok_len"),
                                                            vec![Node::if_then(
                                                                Expr::and(
                                                                    Expr::eq(Expr::var("def_ws3_done"), Expr::u32(0)),
                                                                    Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                                ),
                                                                vec![
                                                                    Node::let_bind("dws3b", safe_load_src(Expr::var("scan_pos"))),
                                                                    Node::let_bind(
                                                                        "dws3_is_ws",
                                                                        Expr::select(
                                                                            Expr::or(
                                                                                Expr::or(
                                                                                    Expr::eq(Expr::var("dws3b"), Expr::u32(b' ' as u32)),
                                                                                    Expr::eq(Expr::var("dws3b"), Expr::u32(b'\t' as u32)),
                                                                                ),
                                                                                Expr::or(
                                                                                    Expr::eq(Expr::var("dws3b"), Expr::u32(0x0B)),
                                                                                    Expr::eq(Expr::var("dws3b"), Expr::u32(0x0C)),
                                                                                ),
                                                                            ),
                                                                            Expr::u32(1),
                                                                            Expr::u32(0),
                                                                        ),
                                                                    ),
                                                                    Node::if_then_else(
                                                                        Expr::eq(Expr::var("dws3_is_ws"), Expr::u32(1)),
                                                                        vec![Node::assign(
                                                                            "scan_pos",
                                                                            Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                                        )],
                                                                        vec![Node::assign("def_ws3_done", Expr::u32(1))],
                                                                    ),
                                                                ],
                                                            )],
                                                        ),
                                                        // Consume `)` if present.
                                                        Node::if_then(
                                                            Expr::eq(safe_load_src(Expr::var("scan_pos")), Expr::u32(b')' as u32)),
                                                            vec![Node::assign(
                                                                "scan_pos",
                                                                Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                            )],
                                                        ),
                                                    ],
                                                ));
                                                def_nodes.extend(push_stack("val_stack", "vsp", Expr::var("def_found")));
                                                def_nodes.push(Node::assign("last_was_value", Expr::u32(1)));
                                                def_nodes
                                            },
                                            {
                                                // Bare ident: treat as an object-like integer macro reference.
                                                // Push the packed macro value when defined, otherwise 0.
                                                let mut bare_nodes: Vec<Node> = Vec::new();
                                                bare_nodes.push(Node::let_bind("bare_found", Expr::u32(0)));
                                                bare_nodes.push(Node::let_bind("bare_value", Expr::u32(0)));
                                                bare_nodes.push(Node::let_bind(
                                                    "bare_ident_base",
                                                    Expr::sub(Expr::var("scan_pos"), Expr::var("ident_len")),
                                                ));
                                                push_has_builtin_call_parser(
                                                    &mut bare_nodes,
                                                    "ehb",
                                                    "bare_ident_base",
                                                    "tok_end",
                                                    "tok_len",
                                                    source_layout,
                                                    source_byte_len.clone(),
                                                    "scan_pos",
                                                    "bare_found",
                                                    "bare_value",
                                                );
                                                bare_nodes.push(Node::if_then(
                                                    Expr::and(
                                                        Expr::eq(Expr::var("bare_found"), Expr::u32(0)),
                                                        Expr::eq(Expr::var("is_has_x_kw"), Expr::u32(1)),
                                                    ),
                                                    {
                                                        let mut hx_nodes: Vec<Node> = Vec::new();
                                                        hx_nodes.push(Node::let_bind("hx_ws_done", Expr::u32(0)));
                                                        hx_nodes.push(Node::loop_for(
                                                            "hx_ws",
                                                            Expr::u32(0),
                                                            Expr::var("tok_len"),
                                                            vec![Node::if_then(
                                                                Expr::and(
                                                                    Expr::eq(Expr::var("hx_ws_done"), Expr::u32(0)),
                                                                    Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                                ),
                                                                vec![
                                                                    Node::let_bind("hxwsb", safe_load_src(Expr::var("scan_pos"))),
                                                                    Node::let_bind(
                                                                        "hxws_is_ws",
                                                                        Expr::select(
                                                                            Expr::or(
                                                                                Expr::or(
                                                                                    Expr::eq(Expr::var("hxwsb"), Expr::u32(b' ' as u32)),
                                                                                    Expr::eq(Expr::var("hxwsb"), Expr::u32(b'\t' as u32)),
                                                                                ),
                                                                                Expr::or(
                                                                                    Expr::eq(Expr::var("hxwsb"), Expr::u32(0x0B)),
                                                                                    Expr::eq(Expr::var("hxwsb"), Expr::u32(0x0C)),
                                                                                ),
                                                                            ),
                                                                            Expr::u32(1),
                                                                            Expr::u32(0),
                                                                        ),
                                                                    ),
                                                                    Node::if_then_else(
                                                                        Expr::eq(Expr::var("hxws_is_ws"), Expr::u32(1)),
                                                                        vec![Node::assign(
                                                                            "scan_pos",
                                                                            Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                                        )],
                                                                        vec![Node::assign("hx_ws_done", Expr::u32(1))],
                                                                    ),
                                                                ],
                                                            )],
                                                        ));
                                                        hx_nodes.push(Node::let_bind("hx_open", safe_load_src(Expr::var("scan_pos"))));
                                                        hx_nodes.push(Node::if_then(
                                                            Expr::eq(Expr::var("hx_open"), Expr::u32(b'(' as u32)),
                                                            {
                                                                let mut paren_nodes: Vec<Node> = Vec::new();
                                                                paren_nodes.push(Node::assign(
                                                                    "scan_pos",
                                                                    Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                                ));
                                                                paren_nodes.push(Node::let_bind("hx_depth", Expr::u32(1)));
                                                                paren_nodes.push(Node::loop_for(
                                                                    "hx_arg_scan",
                                                                    Expr::u32(0),
                                                                    Expr::var("tok_len"),
                                                                    vec![Node::if_then(
                                                                        Expr::and(
                                                                            Expr::gt(Expr::var("hx_depth"), Expr::u32(0)),
                                                                            Expr::lt(Expr::var("scan_pos"), Expr::var("tok_end")),
                                                                        ),
                                                                        vec![
                                                                            Node::let_bind("hxab", safe_load_src(Expr::var("scan_pos"))),
                                                                            Node::if_then(
                                                                                Expr::eq(Expr::var("hxab"), Expr::u32(b'(' as u32)),
                                                                                vec![Node::assign(
                                                                                    "hx_depth",
                                                                                    Expr::add(Expr::var("hx_depth"), Expr::u32(1)),
                                                                                )],
                                                                            ),
                                                                            Node::if_then(
                                                                                Expr::eq(Expr::var("hxab"), Expr::u32(b')' as u32)),
                                                                                vec![Node::assign(
                                                                                    "hx_depth",
                                                                                    Expr::sub(Expr::var("hx_depth"), Expr::u32(1)),
                                                                                )],
                                                                            ),
                                                                            Node::assign(
                                                                                "scan_pos",
                                                                                Expr::add(Expr::var("scan_pos"), Expr::u32(1)),
                                                                            ),
                                                                        ],
                                                                    )],
                                                                ));
                                                                paren_nodes
                                                            },
                                                        ));
                                                        hx_nodes.push(Node::assign("bare_found", Expr::u32(1)));
                                                        hx_nodes.push(Node::assign("bare_value", Expr::u32(0)));
                                                        hx_nodes
                                                    },
                                                ));
                                                bare_nodes.push(Node::loop_for(
                                                    "bm",
                                                    Expr::u32(0),
                                                    Expr::select(
                                                        Expr::gt(Expr::buf_len("macro_offsets"), Expr::u32(0)),
                                                        Expr::sub(Expr::buf_len("macro_offsets"), Expr::u32(1)),
                                                        Expr::u32(0),
                                                    ),
                                                    vec![Node::if_then(
                                                        Expr::eq(Expr::var("bare_found"), Expr::u32(0)),
                                                        vec![
                                                            Node::let_bind(
                                                                "bm_s",
                                                                Expr::load("macro_offsets", Expr::var("bm")),
                                                            ),
                                                            Node::let_bind(
                                                                "bm_e",
                                                                Expr::load("macro_offsets", Expr::add(Expr::var("bm"), Expr::u32(1))),
                                                            ),
                                                            Node::let_bind(
                                                                "bm_l",
                                                                Expr::sub(Expr::var("bm_e"), Expr::var("bm_s")),
                                                            ),
                                                            Node::let_bind("bm_match", Expr::u32(0)),
                                                            Node::if_then(
                                                                Expr::eq(Expr::var("bm_l"), Expr::var("ident_len")),
                                                                vec![
                                                                    Node::assign("bm_match", Expr::u32(1)),
                                                                    Node::loop_for(
                                                                        "bmk",
                                                                        Expr::u32(0),
                                                                        Expr::var("ident_len"),
                                                                        vec![
                                                                            Node::let_bind(
                                                                                "bms_b",
                                                                                safe_load_src(Expr::add(Expr::var("bare_ident_base"), Expr::var("bmk"))),
                                                                            ),
                                                                            Node::let_bind(
                                                                                "bmm_b",
                                                                                safe_load_macro_name(Expr::add(Expr::var("bm_s"), Expr::var("bmk"))),
                                                                            ),
                                                                            Node::if_then(
                                                                                Expr::ne(Expr::var("bms_b"), Expr::var("bmm_b")),
                                                                                vec![Node::assign("bm_match", Expr::u32(0))],
                                                                            ),
                                                                        ],
                                                                    ),
                                                                    Node::if_then(
                                                                        Expr::eq(Expr::var("bm_match"), Expr::u32(1)),
                                                                        vec![
                                                                            Node::assign("bare_found", Expr::u32(1)),
                                                                            Node::assign(
                                                                                "bare_value",
                                                                                Expr::load(
                                                                                    "macro_values",
                                                                                    Expr::add(
                                                                                        Expr::u32(
                                                                                            crate::parsing::c::parse::gnu_builtins::GPU_BUILTIN_HASH_TABLE_SIZE as u32,
                                                                                        ),
                                                                                        Expr::var("bm"),
                                                                                    ),
                                                                                ),
                                                                            ),
                                                                        ],
                                                                    ),
                                                                ],
                                                            ),
                                                        ],
                                                    )],
                                                ));
                                                bare_nodes.extend(push_stack("val_stack", "vsp", Expr::var("bare_value")));
                                                bare_nodes.push(Node::assign("last_was_value", Expr::u32(1)));
                                                bare_nodes
                                            },
                                        ));
                                        id_nodes
                                    },
                                ));

                                // ---- Operators / parens ----
                                classify.push(Node::if_then(
                                    Expr::and(
                                        Expr::eq(Expr::var("is_dec_digit"), Expr::u32(0)),
                                        Expr::eq(Expr::var("is_alpha_start"), Expr::u32(0)),
                                    ),
                                    {
                                        let mut op_nodes: Vec<Node> = Vec::new();
                                        // Detect each operator. We always
                                        // start by classifying as one of:
                                        // (, ), 2-char op, 1-char op, or
                                        // unknown (terminate).
                                        op_nodes.push(Node::let_bind("op_picked", Expr::u32(0)));
                                        op_nodes.push(Node::let_bind("op_skip", Expr::u32(1)));
                                        // Open paren.
                                        op_nodes.push(Node::if_then(
                                            Expr::eq(Expr::var("c"), Expr::u32(b'(' as u32)),
                                            vec![
                                                Node::assign("op_picked", Expr::u32(OP_LPAREN)),
                                            ],
                                        ));
                                        // Close paren  -  pop until LPAREN.
                                        op_nodes.push(Node::if_then(
                                            Expr::eq(Expr::var("c"), Expr::u32(b')' as u32)),
                                            {
                                                let mut close: Vec<Node> = Vec::new();
                                                close.push(Node::let_bind("close_done", Expr::u32(0)));
                                                close.push(Node::loop_for(
                                                    "close_pop",
                                                    Expr::u32(0),
                                                    Expr::u32(STACK_DEPTH),
                                                    vec![Node::if_then(
                                                        Expr::and(
                                                            Expr::eq(Expr::var("close_done"), Expr::u32(0)),
                                                            Expr::gt(Expr::var("osp"), Expr::u32(0)),
                                                        ),
                                                        {
                                                            let mut iter: Vec<Node> = Vec::new();
                                                            iter.push(Node::let_bind("top_op_close", Expr::u32(0)));
                                                            iter.extend(peek_stack("op_stack", "osp", "top_op_close"));
                                                            iter.push(Node::if_then_else(
                                                                Expr::eq(Expr::var("top_op_close"), Expr::u32(OP_LPAREN)),
                                                                vec![
                                                                    Node::assign("osp", Expr::sub(Expr::var("osp"), Expr::u32(1))),
                                                                    Node::assign("close_done", Expr::u32(1)),
                                                                ],
                                                                apply_top_op(),
                                                            ));
                                                            iter
                                                        },
                                                    )],
                                                ));
                                                close.push(Node::assign("op_picked", Expr::u32(OP_LPAREN))); // marker so we don't re-push
                                                close.push(Node::assign("op_skip", Expr::u32(1)));
                                                close.push(Node::assign("last_was_value", Expr::u32(1)));
                                                close
                                            },
                                        ));
                                        // Unary !/~/+/- (when last token wasn't a value).
                                        // Push as deferred opcode onto op_stack so it
                                        // applies AFTER its operand has been pushed.
                                        op_nodes.push(Node::if_then(
                                            Expr::and(
                                                Expr::eq(Expr::var("last_was_value"), Expr::u32(0)),
                                                Expr::or(
                                                    Expr::eq(Expr::var("c"), Expr::u32(b'+' as u32)),
                                                    Expr::or(
                                                        Expr::eq(Expr::var("c"), Expr::u32(b'-' as u32)),
                                                        Expr::or(
                                                            Expr::eq(Expr::var("c"), Expr::u32(b'!' as u32)),
                                                            Expr::eq(Expr::var("c"), Expr::u32(b'~' as u32)),
                                                        ),
                                                    ),
                                                ),
                                            ),
                                            {
                                                let mut un: Vec<Node> = Vec::new();
                                                un.push(Node::let_bind(
                                                    "un_op",
                                                    Expr::select(
                                                        Expr::eq(Expr::var("c"), Expr::u32(b'!' as u32)),
                                                        Expr::u32(OP_UN_NOT),
                                                        Expr::select(
                                                            Expr::eq(Expr::var("c"), Expr::u32(b'~' as u32)),
                                                            Expr::u32(OP_UN_BNOT),
                                                            Expr::select(
                                                                Expr::eq(Expr::var("c"), Expr::u32(b'-' as u32)),
                                                                Expr::u32(OP_UN_NEG),
                                                                Expr::u32(OP_UN_PLUS),
                                                            ),
                                                        ),
                                                    ),
                                                ));
                                                un.extend(push_stack("op_stack", "osp", Expr::var("un_op")));
                                                // Mark as handled so the binary classifier
                                                // doesn't try to interpret the same byte.
                                                un.push(Node::assign("op_picked", Expr::u32(OP_LPAREN)));
                                                // last_was_value stays 0 so the operand
                                                // following the unary still parses as a
                                                // value (not as an unconditional binary).
                                                un
                                            },
                                        ));
                                        // Two-char binary ops. Map ('=', '=') etc.
                                        let pairs: &[(u8, u8, u32)] = &[
                                            (b'|', b'|', OP_LOR),
                                            (b'&', b'&', OP_LAND),
                                            (b'=', b'=', OP_EQ),
                                            (b'!', b'=', OP_NE),
                                            (b'<', b'=', OP_LE),
                                            (b'>', b'=', OP_GE),
                                            (b'<', b'<', OP_SHL),
                                            (b'>', b'>', OP_SHR),
                                        ];
                                        for &(a, b, op) in pairs {
                                            op_nodes.push(Node::if_then(
                                                Expr::and(
                                                    Expr::eq(Expr::var("op_picked"), Expr::u32(0)),
                                                    Expr::and(
                                                        Expr::eq(Expr::var("c"), Expr::u32(a as u32)),
                                                        Expr::eq(Expr::var("c1"), Expr::u32(b as u32)),
                                                    ),
                                                ),
                                                vec![
                                                    Node::assign("op_picked", Expr::u32(op)),
                                                    Node::assign("op_skip", Expr::u32(2)),
                                                ],
                                            ));
                                        }
                                        // One-char binary ops (skip if op_picked already set above).
                                        let singles: &[(u8, u32)] = &[
                                            (b'|', OP_BOR),
                                            (b'&', OP_BAND),
                                            (b'^', OP_BXOR),
                                            (b'<', OP_LT),
                                            (b'>', OP_GT),
                                            (b'+', OP_ADD),
                                            (b'-', OP_SUB),
                                            (b'*', OP_MUL),
                                            (b'/', OP_DIV),
                                            (b'%', OP_MOD),
                                        ];
                                        for &(b, op) in singles {
                                            op_nodes.push(Node::if_then(
                                                Expr::and(
                                                    Expr::eq(Expr::var("op_picked"), Expr::u32(0)),
                                                    Expr::eq(Expr::var("c"), Expr::u32(b as u32)),
                                                ),
                                                vec![Node::assign("op_picked", Expr::u32(op))],
                                            ));
                                        }
                                        // Push binary op (everything except LPAREN and the close-paren marker).
                                        op_nodes.push(Node::if_then(
                                            Expr::and(
                                                Expr::ne(Expr::var("op_picked"), Expr::u32(0)),
                                                Expr::ne(Expr::var("op_picked"), Expr::u32(OP_LPAREN)),
                                            ),
                                            {
                                                let mut push_bin: Vec<Node> = Vec::new();
                                                // Pop while stack top has >= precedence.
                                                push_bin.push(Node::let_bind("pop_done", Expr::u32(0)));
                                                push_bin.push(Node::let_bind("new_prec", precedence_of(Expr::var("op_picked"))));
                                                push_bin.push(Node::loop_for(
                                                    "pop_while",
                                                    Expr::u32(0),
                                                    Expr::u32(STACK_DEPTH),
                                                    vec![Node::if_then(
                                                        Expr::and(
                                                            Expr::eq(Expr::var("pop_done"), Expr::u32(0)),
                                                            Expr::gt(Expr::var("osp"), Expr::u32(0)),
                                                        ),
                                                        {
                                                            let mut iter: Vec<Node> = Vec::new();
                                                            iter.push(Node::let_bind("top_op_apply", Expr::u32(0)));
                                                            iter.extend(peek_stack("op_stack", "osp", "top_op_apply"));
                                                            iter.push(Node::let_bind("top_prec", precedence_of(Expr::var("top_op_apply"))));
                                                            iter.push(Node::if_then_else(
                                                                Expr::ge(Expr::var("top_prec"), Expr::var("new_prec")),
                                                                apply_top_op(),
                                                                vec![Node::assign("pop_done", Expr::u32(1))],
                                                            ));
                                                            iter
                                                        },
                                                    )],
                                                ));
                                                push_bin.extend(push_stack("op_stack", "osp", Expr::var("op_picked")));
                                                push_bin.push(Node::assign("last_was_value", Expr::u32(0)));
                                                push_bin
                                            },
                                        ));
                                        // Pure LPAREN push.
                                        op_nodes.push(Node::if_then(
                                            Expr::eq(Expr::var("c"), Expr::u32(b'(' as u32)),
                                            {
                                                let mut nodes: Vec<Node> = Vec::new();
                                                nodes.extend(push_stack("op_stack", "osp", Expr::u32(OP_LPAREN)));
                                                nodes.push(Node::assign("last_was_value", Expr::u32(0)));
                                                nodes
                                            },
                                        ));
                                        // Unknown character: terminate scan to avoid infinite loop.
                                        op_nodes.push(Node::if_then(
                                            Expr::eq(Expr::var("op_picked"), Expr::u32(0)),
                                            vec![Node::assign("scan_done", Expr::u32(1))],
                                        ));
                                        op_nodes.push(Node::assign(
                                            "scan_pos",
                                            Expr::add(Expr::var("scan_pos"), Expr::var("op_skip")),
                                        ));
                                        op_nodes
                                    },
                                ));
                                classify
                            },
                        ));
                        iter_body
                    },
                )],
            ));

            // ---------- Drain remaining operators ----------
            evaluate.push(Node::let_bind("drain_done", Expr::u32(0)));
            evaluate.push(Node::loop_for(
                "drain",
                Expr::u32(0),
                Expr::u32(STACK_DEPTH),
                vec![Node::if_then(
                    Expr::and(
                        Expr::eq(Expr::var("drain_done"), Expr::u32(0)),
                        Expr::gt(Expr::var("osp"), Expr::u32(0)),
                    ),
                    {
                        let mut iter: Vec<Node> = Vec::new();
                        iter.push(Node::let_bind("top_op_drain", Expr::u32(0)));
                        iter.extend(peek_stack("op_stack", "osp", "top_op_drain"));
                        iter.push(Node::if_then_else(
                            Expr::or(
                                Expr::eq(Expr::var("top_op_drain"), Expr::u32(OP_LPAREN)),
                                Expr::eq(Expr::var("top_op_drain"), Expr::u32(OP_TERNARY_Q)),
                            ),
                            vec![
                                Node::assign("osp", Expr::sub(Expr::var("osp"), Expr::u32(1))),
                            ],
                            apply_top_op(),
                        ));
                        iter
                    },
                )],
            ));

            // ---------- Final value: top of val_stack, mapped to bool ----------
            evaluate.push(Node::let_bind("final_val", Expr::u32(0)));
            evaluate.extend(peek_stack("val_stack", "vsp", "final_val"));
            evaluate.push(Node::assign(
                "expr_value_out",
                Expr::select(
                    Expr::ne(Expr::var("expr_invalid"), Expr::u32(0)),
                    Expr::u32(INVALID_EXPR_VALUE),
                    Expr::select(
                        Expr::ne(Expr::var("final_val"), Expr::u32(0)),
                        Expr::u32(1),
                        Expr::u32(0),
                    ),
                ),
            ));

            // Gate the directive_values store on directive_kind so
            // this kernel only writes to if/elif rows. This makes
            // it safe to fuse with `gpu_ifdef_value` (which writes
            // ifdef/ifndef rows)  -  disjoint cells, no clobbering
            // even with a barrier between fused arms.
            inner.push(Node::if_then(
                Expr::or(
                    Expr::eq(Expr::var("kind"), Expr::u32(TOK_PP_IF)),
                    Expr::eq(Expr::var("kind"), Expr::u32(TOK_PP_ELIF)),
                ),
                {
                    let mut gated = evaluate;
                    gated.push(Node::store(
                        "directive_values",
                        t.clone(),
                        Expr::var("expr_value_out"),
                    ));
                    gated
                },
            ));
            inner
        },
    ));

    Program::wrapped(
        vec![
            BufferDecl::storage(
                "tok_starts",
                BINDING_TOK_STARTS,
                BufferAccess::ReadOnly,
                DataType::U32,
            )
            .with_count(num_tokens.max(1)),
            BufferDecl::storage(
                "tok_lens",
                BINDING_TOK_LENS,
                BufferAccess::ReadOnly,
                DataType::U32,
            )
            .with_count(num_tokens.max(1)),
            BufferDecl::storage(
                "directive_kinds",
                BINDING_DIRECTIVE_KINDS,
                BufferAccess::ReadOnly,
                DataType::U32,
            )
            .with_count(num_tokens.max(1)),
            BufferDecl::storage(
                "source",
                BINDING_SOURCE,
                BufferAccess::ReadOnly,
                source_buffer_element(source_layout),
            )
            .with_count(0),
            // Runtime-sized: count=0 marks the buffer as runtime-bound,
            // so the program structure stays independent of the host's
            // macro-table size.
            BufferDecl::storage(
                "macro_names_packed",
                BINDING_MACRO_NAMES_PACKED,
                BufferAccess::ReadOnly,
                source_buffer_element(macro_names_layout),
            )
            .with_count(0),
            BufferDecl::storage(
                "macro_offsets",
                BINDING_MACRO_OFFSETS,
                BufferAccess::ReadOnly,
                DataType::U32,
            )
            .with_count(0),
            BufferDecl::storage(
                "macro_values",
                BINDING_MACRO_VALUES,
                BufferAccess::ReadOnly,
                DataType::U32,
            )
            .with_count(0),
            BufferDecl::storage(
                "directive_values",
                BINDING_DIRECTIVE_VALUES,
                BufferAccess::ReadWrite,
                DataType::U32,
            )
            .with_count(num_tokens.max(1)),
        ],
        [256, 1, 1],
        body,
    )
    .with_entry_op_id(OP_ID)
}