parseman 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/combinators/expect.d.ts +2 -2
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts +14 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/token.d.ts +10 -0
- package/dist/combinators/token.d.ts.map +1 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts +28 -0
- package/dist/compiler/ir-serialize.d.ts.map +1 -0
- package/dist/compiler/linker.d.ts +9 -0
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/index.cjs +517 -126
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +517 -126
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +838 -121
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +838 -121
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/plugin/index.js
CHANGED
|
@@ -65,6 +65,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
65
65
|
case "transform":
|
|
66
66
|
case "label":
|
|
67
67
|
case "trivia":
|
|
68
|
+
case "token":
|
|
68
69
|
case "expect":
|
|
69
70
|
case "withCtx":
|
|
70
71
|
case "node":
|
|
@@ -91,6 +92,55 @@ function sequenceFirstSet(parsers) {
|
|
|
91
92
|
}
|
|
92
93
|
return fs2;
|
|
93
94
|
}
|
|
95
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
96
|
+
if (seen.has(p)) return any();
|
|
97
|
+
seen.add(p);
|
|
98
|
+
const fs2 = (c) => firstSetOf(c, seen);
|
|
99
|
+
const d = p._def;
|
|
100
|
+
switch (d.tag) {
|
|
101
|
+
case "literal":
|
|
102
|
+
case "regex":
|
|
103
|
+
case "keywords":
|
|
104
|
+
return p._meta.firstSet;
|
|
105
|
+
// terminals: no refs, cached set is exact
|
|
106
|
+
case "lazy":
|
|
107
|
+
try {
|
|
108
|
+
return fs2(d.thunk());
|
|
109
|
+
} catch {
|
|
110
|
+
return any();
|
|
111
|
+
}
|
|
112
|
+
case "choice": {
|
|
113
|
+
let out = empty();
|
|
114
|
+
for (const arm of d.parsers) out = union(out, fs2(arm));
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
case "sequence": {
|
|
118
|
+
let out = empty();
|
|
119
|
+
for (const term of d.parsers) {
|
|
120
|
+
out = union(out, fs2(term));
|
|
121
|
+
if (!matchesEmpty(term)) return out;
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
case "oneOrMore":
|
|
126
|
+
case "many":
|
|
127
|
+
case "optional":
|
|
128
|
+
case "transform":
|
|
129
|
+
case "label":
|
|
130
|
+
case "trivia":
|
|
131
|
+
case "token":
|
|
132
|
+
case "node":
|
|
133
|
+
case "grammar":
|
|
134
|
+
case "expect":
|
|
135
|
+
return fs2(d.parser);
|
|
136
|
+
case "sepBy":
|
|
137
|
+
return fs2(d.parser);
|
|
138
|
+
case "skip":
|
|
139
|
+
return fs2(d.main);
|
|
140
|
+
default:
|
|
141
|
+
return p._meta.firstSet;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
94
144
|
function mergeRanges(ranges) {
|
|
95
145
|
if (ranges.length === 0) return [];
|
|
96
146
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -1202,6 +1252,7 @@ function markUnusedValues(root) {
|
|
|
1202
1252
|
return;
|
|
1203
1253
|
case "label":
|
|
1204
1254
|
case "trivia":
|
|
1255
|
+
case "token":
|
|
1205
1256
|
case "withCtx":
|
|
1206
1257
|
case "expect":
|
|
1207
1258
|
visit(def.parser, consumed);
|
|
@@ -1230,6 +1281,41 @@ function markUnusedValues(root) {
|
|
|
1230
1281
|
visit(resolveRoot(root), true);
|
|
1231
1282
|
}
|
|
1232
1283
|
|
|
1284
|
+
// src/combinators/parser.ts
|
|
1285
|
+
function rules(factory) {
|
|
1286
|
+
const cache = {};
|
|
1287
|
+
const proxy = new Proxy(cache, {
|
|
1288
|
+
get(target, key) {
|
|
1289
|
+
if (typeof key !== "string") return void 0;
|
|
1290
|
+
const record = target;
|
|
1291
|
+
if (!(key in record)) {
|
|
1292
|
+
const r = ref();
|
|
1293
|
+
r._ruleName = key;
|
|
1294
|
+
record[key] = r;
|
|
1295
|
+
}
|
|
1296
|
+
return record[key];
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
const definitions = factory(proxy);
|
|
1300
|
+
for (const key of Object.keys(definitions)) {
|
|
1301
|
+
const placeholder = cache[key];
|
|
1302
|
+
const parser2 = definitions[key];
|
|
1303
|
+
if (placeholder !== void 0 && typeof placeholder.define === "function") {
|
|
1304
|
+
;
|
|
1305
|
+
placeholder.define(parser2);
|
|
1306
|
+
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
1307
|
+
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
1308
|
+
} else {
|
|
1309
|
+
;
|
|
1310
|
+
cache[key] = parser2;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
for (const key of Object.keys(definitions)) {
|
|
1314
|
+
markUnusedValues(cache[key]);
|
|
1315
|
+
}
|
|
1316
|
+
return cache;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1233
1319
|
// src/combinators/not.ts
|
|
1234
1320
|
function not(combinator) {
|
|
1235
1321
|
const meta = {
|
|
@@ -1336,6 +1422,20 @@ function transform(combinator, fn) {
|
|
|
1336
1422
|
}
|
|
1337
1423
|
};
|
|
1338
1424
|
}
|
|
1425
|
+
function skip(main, skipped) {
|
|
1426
|
+
return {
|
|
1427
|
+
_tag: "skip",
|
|
1428
|
+
_meta: main._meta,
|
|
1429
|
+
_def: { tag: "skip", main, skipped },
|
|
1430
|
+
parse(input, pos, ctx) {
|
|
1431
|
+
const result = main.parse(input, pos, ctx);
|
|
1432
|
+
if (!result.ok) return result;
|
|
1433
|
+
const s = skipped.parse(input, result.span.end, ctx);
|
|
1434
|
+
if (!s.ok) return result;
|
|
1435
|
+
return { ...result, span: { start: result.span.start, end: s.span.end } };
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1339
1439
|
function trivia(combinator) {
|
|
1340
1440
|
const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
|
|
1341
1441
|
return {
|
|
@@ -1430,8 +1530,60 @@ function noTrivia(root) {
|
|
|
1430
1530
|
return parser({ trivia: null }, root);
|
|
1431
1531
|
}
|
|
1432
1532
|
|
|
1533
|
+
// src/combinators/token.ts
|
|
1534
|
+
function token(root) {
|
|
1535
|
+
const meta = {
|
|
1536
|
+
firstSet: root._meta.firstSet,
|
|
1537
|
+
canMatchNewline: root._meta.canMatchNewline,
|
|
1538
|
+
isTrivia: false
|
|
1539
|
+
};
|
|
1540
|
+
return {
|
|
1541
|
+
_tag: "token",
|
|
1542
|
+
_meta: meta,
|
|
1543
|
+
_def: { tag: "token", parser: root },
|
|
1544
|
+
parse(input, pos, ctx) {
|
|
1545
|
+
const savedTrivia = ctx.trivia;
|
|
1546
|
+
const savedKinds = ctx.triviaKindLabels;
|
|
1547
|
+
const savedBuf = ctx._cstBuf;
|
|
1548
|
+
const savedChildren = ctx._cstChildren;
|
|
1549
|
+
const savedLeaves = ctx._cstLeaves;
|
|
1550
|
+
const savedRaw = ctx._cstRawChildren;
|
|
1551
|
+
const savedTriviaLog = ctx._cstTriviaLog;
|
|
1552
|
+
const savedOuterTriviaLog = ctx._triviaLog;
|
|
1553
|
+
const wasCapturing = cstCaptureActive(ctx);
|
|
1554
|
+
ctx.trivia = void 0;
|
|
1555
|
+
ctx.triviaKindLabels = void 0;
|
|
1556
|
+
ctx._cstBuf = void 0;
|
|
1557
|
+
ctx._cstChildren = void 0;
|
|
1558
|
+
ctx._cstLeaves = void 0;
|
|
1559
|
+
ctx._cstRawChildren = void 0;
|
|
1560
|
+
ctx._cstTriviaLog = void 0;
|
|
1561
|
+
delete ctx._triviaLog;
|
|
1562
|
+
let result;
|
|
1563
|
+
try {
|
|
1564
|
+
result = root.parse(input, pos, ctx);
|
|
1565
|
+
} finally {
|
|
1566
|
+
ctx.trivia = savedTrivia;
|
|
1567
|
+
ctx.triviaKindLabels = savedKinds;
|
|
1568
|
+
ctx._cstBuf = savedBuf;
|
|
1569
|
+
ctx._cstChildren = savedChildren;
|
|
1570
|
+
ctx._cstLeaves = savedLeaves;
|
|
1571
|
+
ctx._cstRawChildren = savedRaw;
|
|
1572
|
+
ctx._cstTriviaLog = savedTriviaLog;
|
|
1573
|
+
if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
|
|
1574
|
+
else ctx._triviaLog = savedOuterTriviaLog;
|
|
1575
|
+
}
|
|
1576
|
+
if (!result.ok) return result;
|
|
1577
|
+
const span = result.span;
|
|
1578
|
+
const value = input.slice(pos, span.end);
|
|
1579
|
+
if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span });
|
|
1580
|
+
return { ok: true, value, span };
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1433
1585
|
// src/combinators/expect.ts
|
|
1434
|
-
function
|
|
1586
|
+
function deriveExpected(c) {
|
|
1435
1587
|
const def = c._def;
|
|
1436
1588
|
switch (def.tag) {
|
|
1437
1589
|
case "literal":
|
|
@@ -1443,22 +1595,23 @@ function staticExpected(c) {
|
|
|
1443
1595
|
case "label":
|
|
1444
1596
|
return [def.label];
|
|
1445
1597
|
case "choice":
|
|
1446
|
-
return def.parsers.flatMap(
|
|
1598
|
+
return def.parsers.flatMap(deriveExpected);
|
|
1447
1599
|
case "sequence":
|
|
1448
|
-
return def.parsers.length > 0 ?
|
|
1600
|
+
return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
|
|
1449
1601
|
case "node":
|
|
1450
1602
|
case "grammar":
|
|
1451
1603
|
case "trivia":
|
|
1604
|
+
case "token":
|
|
1452
1605
|
case "optional":
|
|
1453
1606
|
case "many":
|
|
1454
1607
|
case "oneOrMore":
|
|
1455
1608
|
case "transform":
|
|
1456
1609
|
case "not":
|
|
1457
|
-
return
|
|
1610
|
+
return deriveExpected(def.parser);
|
|
1458
1611
|
case "lazy": {
|
|
1459
1612
|
const name = c._ruleName;
|
|
1460
1613
|
try {
|
|
1461
|
-
return
|
|
1614
|
+
return deriveExpected(def.thunk());
|
|
1462
1615
|
} catch {
|
|
1463
1616
|
return name ? [name] : [];
|
|
1464
1617
|
}
|
|
@@ -1468,7 +1621,7 @@ function staticExpected(c) {
|
|
|
1468
1621
|
}
|
|
1469
1622
|
}
|
|
1470
1623
|
function expect(combinator, label2) {
|
|
1471
|
-
const expected = label2 !== void 0 ? [label2] :
|
|
1624
|
+
const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
|
|
1472
1625
|
const meta = {
|
|
1473
1626
|
firstSet: combinator._meta.firstSet,
|
|
1474
1627
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
@@ -2964,16 +3117,12 @@ function namedFnPrelude() {
|
|
|
2964
3117
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
2965
3118
|
ctx.namedFnDecls.push([
|
|
2966
3119
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
2967
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
2968
3120
|
` _pfail: {`,
|
|
2969
3121
|
...reindentStmts(bodyStmts, 2),
|
|
2970
|
-
`
|
|
2971
|
-
`
|
|
2972
|
-
` _pfok = true`,
|
|
3122
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3123
|
+
` return ${valueVar}`,
|
|
2973
3124
|
` }`,
|
|
2974
|
-
`
|
|
2975
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
2976
|
-
` return _pfv`,
|
|
3125
|
+
` return ${NAMED_FN_FAIL}`,
|
|
2977
3126
|
`}`
|
|
2978
3127
|
].join("\n"));
|
|
2979
3128
|
}
|
|
@@ -3132,6 +3281,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
3132
3281
|
case "guard":
|
|
3133
3282
|
case "not":
|
|
3134
3283
|
case "trivia":
|
|
3284
|
+
case "token":
|
|
3135
3285
|
case "scanTo":
|
|
3136
3286
|
case "unknown":
|
|
3137
3287
|
return false;
|
|
@@ -3193,6 +3343,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
3193
3343
|
case "regex":
|
|
3194
3344
|
case "keywords":
|
|
3195
3345
|
case "node":
|
|
3346
|
+
case "token":
|
|
3196
3347
|
return true;
|
|
3197
3348
|
case "not":
|
|
3198
3349
|
case "guard":
|
|
@@ -3471,9 +3622,9 @@ function emitSeq(def, ctx, pos) {
|
|
|
3471
3622
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3472
3623
|
return { stmts, valueVar: arrV, endVar };
|
|
3473
3624
|
}
|
|
3474
|
-
function
|
|
3625
|
+
function deriveExpectedArr(parsers) {
|
|
3475
3626
|
return JSON.stringify(parsers.flatMap((p) => {
|
|
3476
|
-
const e =
|
|
3627
|
+
const e = deriveExpected(p);
|
|
3477
3628
|
return e.length > 0 ? e : [p._tag];
|
|
3478
3629
|
}));
|
|
3479
3630
|
}
|
|
@@ -3494,7 +3645,7 @@ function failsAtStart(p) {
|
|
|
3494
3645
|
}
|
|
3495
3646
|
}
|
|
3496
3647
|
function armStaticExpected(ctx, p) {
|
|
3497
|
-
return hoistExpected(ctx,
|
|
3648
|
+
return hoistExpected(ctx, deriveExpectedArr([p]));
|
|
3498
3649
|
}
|
|
3499
3650
|
function canMatchEmptyAtStart(p) {
|
|
3500
3651
|
const d = p._def;
|
|
@@ -3532,7 +3683,7 @@ function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
|
3532
3683
|
];
|
|
3533
3684
|
}
|
|
3534
3685
|
function emitChoice(def, ctx, pos) {
|
|
3535
|
-
const allExpected =
|
|
3686
|
+
const allExpected = deriveExpectedArr(def.parsers);
|
|
3536
3687
|
if (def.disjoint) {
|
|
3537
3688
|
const codeV = v(ctx, "_code");
|
|
3538
3689
|
const valV = v(ctx, "_chv");
|
|
@@ -3593,7 +3744,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
3593
3744
|
const wordV = v(ctx, "_gw");
|
|
3594
3745
|
const endV = v(ctx, "_ge");
|
|
3595
3746
|
const valV = v(ctx, "_gcv");
|
|
3596
|
-
const regexExpected = JSON.stringify(
|
|
3747
|
+
const regexExpected = JSON.stringify(deriveExpected(superParser));
|
|
3597
3748
|
const stmts = [
|
|
3598
3749
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
3599
3750
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
@@ -3674,11 +3825,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3674
3825
|
const atStart = failsAtStart(p);
|
|
3675
3826
|
const staticFx = armStaticExpected(ctx, p);
|
|
3676
3827
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3677
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3828
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3829
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3830
|
+
const deep = firstSetOf(p);
|
|
3831
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3832
|
+
})();
|
|
3678
3833
|
let gateCond = null;
|
|
3679
3834
|
if (gate) {
|
|
3680
3835
|
const gateIdx = ctx.mapFns.length;
|
|
3681
3836
|
ctx.mapFns.push(gate);
|
|
3837
|
+
ctx.mapFnSrcs.push(null);
|
|
3682
3838
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3683
3839
|
}
|
|
3684
3840
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3737,13 +3893,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3737
3893
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3738
3894
|
return emitFirstMatch(def, ctx, pos);
|
|
3739
3895
|
}
|
|
3896
|
+
function pushMapFn(ctx, fn, src) {
|
|
3897
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3898
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3899
|
+
if (hit !== -1) return hit;
|
|
3900
|
+
}
|
|
3901
|
+
const idx = ctx.mapFns.length;
|
|
3902
|
+
ctx.mapFns.push(fn);
|
|
3903
|
+
ctx.mapFnSrcs.push(src);
|
|
3904
|
+
return idx;
|
|
3905
|
+
}
|
|
3740
3906
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3741
3907
|
const def = p._def;
|
|
3742
3908
|
if (def.tag === "transform") {
|
|
3743
3909
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3744
|
-
const fnIdx = ctx.
|
|
3745
|
-
ctx.mapFns.push(def.fn);
|
|
3746
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3910
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3747
3911
|
const vv = v(ctx);
|
|
3748
3912
|
return {
|
|
3749
3913
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3926,7 +4090,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3926
4090
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3927
4091
|
];
|
|
3928
4092
|
ctx.indent++;
|
|
3929
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4093
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3930
4094
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3931
4095
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3932
4096
|
if (def.skip.length > 0) {
|
|
@@ -3972,6 +4136,116 @@ function emitNot(def, ctx, pos) {
|
|
|
3972
4136
|
endVar: pos
|
|
3973
4137
|
};
|
|
3974
4138
|
}
|
|
4139
|
+
function escapeTokenLiteral(value) {
|
|
4140
|
+
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
4141
|
+
}
|
|
4142
|
+
function regexMatchesEmpty(source, flags) {
|
|
4143
|
+
try {
|
|
4144
|
+
return new RegExp(`^(?:${source})$`, flags).test("");
|
|
4145
|
+
} catch {
|
|
4146
|
+
return true;
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
function tokenTerminalSource(p) {
|
|
4150
|
+
const def = p._def;
|
|
4151
|
+
switch (def.tag) {
|
|
4152
|
+
case "literal":
|
|
4153
|
+
if (def.value.length === 0) return null;
|
|
4154
|
+
return {
|
|
4155
|
+
source: escapeTokenLiteral(def.value),
|
|
4156
|
+
caseInsensitive: def.caseInsensitive,
|
|
4157
|
+
rawCaseSensitive: def.caseInsensitive ? "" : def.value
|
|
4158
|
+
};
|
|
4159
|
+
case "regex": {
|
|
4160
|
+
if (def.flags.replace(/i/g, "") !== "") return null;
|
|
4161
|
+
if (regexMatchesEmpty(def.source, def.flags)) return null;
|
|
4162
|
+
return {
|
|
4163
|
+
source: `(?:${def.source})`,
|
|
4164
|
+
caseInsensitive: def.flags.includes("i"),
|
|
4165
|
+
rawCaseSensitive: def.flags.includes("i") ? "" : def.source
|
|
4166
|
+
};
|
|
4167
|
+
}
|
|
4168
|
+
default:
|
|
4169
|
+
return null;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
function tokenSequenceSource(p) {
|
|
4173
|
+
const def = p._def;
|
|
4174
|
+
const parts = def.tag === "sequence" ? def.parsers.map(tokenTerminalSource) : [tokenTerminalSource(p)];
|
|
4175
|
+
if (parts.some((part) => part === null)) return null;
|
|
4176
|
+
const typed = parts;
|
|
4177
|
+
const flags = typed.some((part) => part.caseInsensitive) ? "i" : "";
|
|
4178
|
+
if (flags.includes("i") && typed.some((part) => /[A-Za-z]/.test(part.rawCaseSensitive))) return null;
|
|
4179
|
+
return {
|
|
4180
|
+
source: typed.map((part) => part.source).join(""),
|
|
4181
|
+
flags,
|
|
4182
|
+
hasCaseSensitiveLetters: typed.some((part) => /[A-Za-z]/.test(part.rawCaseSensitive))
|
|
4183
|
+
};
|
|
4184
|
+
}
|
|
4185
|
+
function combineTokenRegexSources(parts) {
|
|
4186
|
+
const flags = parts.some((part) => part.flags.includes("i")) ? "i" : "";
|
|
4187
|
+
if (flags.includes("i") && parts.some((part) => part.flags === "" && part.hasCaseSensitiveLetters)) return null;
|
|
4188
|
+
return { source: parts.map((part) => part.source).join(""), flags };
|
|
4189
|
+
}
|
|
4190
|
+
function tokenNullableRegex(def) {
|
|
4191
|
+
const inner = def.parser._def;
|
|
4192
|
+
if (inner.tag === "many") {
|
|
4193
|
+
const seq = tokenSequenceSource(inner.parser);
|
|
4194
|
+
if (!seq) return null;
|
|
4195
|
+
return { tag: "regex", source: `(?:${seq.source})*`, flags: seq.flags };
|
|
4196
|
+
}
|
|
4197
|
+
if (inner.tag === "optional") {
|
|
4198
|
+
const seq = tokenSequenceSource(inner.parser);
|
|
4199
|
+
if (!seq) return null;
|
|
4200
|
+
return { tag: "regex", source: `(?:${seq.source})?`, flags: seq.flags };
|
|
4201
|
+
}
|
|
4202
|
+
if (inner.tag === "sepBy") {
|
|
4203
|
+
const item = tokenSequenceSource(inner.parser);
|
|
4204
|
+
const sep = tokenSequenceSource(inner.separator);
|
|
4205
|
+
if (!item || !sep) return null;
|
|
4206
|
+
const combined = combineTokenRegexSources([item, sep]);
|
|
4207
|
+
if (!combined) return null;
|
|
4208
|
+
return { tag: "regex", source: `(?:${item.source}(?:${sep.source}${item.source})*)?`, flags: combined.flags };
|
|
4209
|
+
}
|
|
4210
|
+
return null;
|
|
4211
|
+
}
|
|
4212
|
+
function emitToken(def, ctx, pos) {
|
|
4213
|
+
const lowered = tokenNullableRegex(def);
|
|
4214
|
+
if (lowered) return emitRegex(lowered, ctx, pos);
|
|
4215
|
+
const savedTrivia = ctx.activeTrivia;
|
|
4216
|
+
const savedKindLabels = ctx.triviaKindLabels;
|
|
4217
|
+
ctx.activeTrivia = void 0;
|
|
4218
|
+
ctx.triviaKindLabels = void 0;
|
|
4219
|
+
const inner = emitFallible(def.parser, ctx, pos);
|
|
4220
|
+
ctx.activeTrivia = savedTrivia;
|
|
4221
|
+
ctx.triviaKindLabels = savedKindLabels;
|
|
4222
|
+
const i = ind(ctx);
|
|
4223
|
+
const sc = v(ctx, "_tokCh");
|
|
4224
|
+
const sl = v(ctx, "_tokLv");
|
|
4225
|
+
const sr = v(ctx, "_tokRaw");
|
|
4226
|
+
const sv = v(ctx, "_tokTr");
|
|
4227
|
+
const sk = v(ctx, "_tokKinds");
|
|
4228
|
+
const stl = v(ctx, "_tokTl");
|
|
4229
|
+
const sol = v(ctx, "_tokLog");
|
|
4230
|
+
const sb = v(ctx, "_tokBuf");
|
|
4231
|
+
const valV = v(ctx, "_tok");
|
|
4232
|
+
return {
|
|
4233
|
+
stmts: [
|
|
4234
|
+
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${sv} = _ctx.trivia, ${sk} = _ctx.triviaKindLabels, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
|
|
4235
|
+
`${i}_ctx.trivia = undefined; _ctx.triviaKindLabels = undefined; _ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
|
|
4236
|
+
`${i}try {`,
|
|
4237
|
+
...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
|
|
4238
|
+
`${i}} finally {`,
|
|
4239
|
+
`${i} _ctx.trivia = ${sv}; _ctx.triviaKindLabels = ${sk}; _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
|
|
4240
|
+
`${i}}`,
|
|
4241
|
+
...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
|
|
4242
|
+
`${i}const ${valV} = input.slice(${pos}, ${inner.endVar})`,
|
|
4243
|
+
...emitLeafCapture(ctx, valV, pos, inner.endVar)
|
|
4244
|
+
],
|
|
4245
|
+
valueVar: valV,
|
|
4246
|
+
endVar: inner.endVar
|
|
4247
|
+
};
|
|
4248
|
+
}
|
|
3975
4249
|
function emitNode(def, ctx, pos) {
|
|
3976
4250
|
const structural = def.build === void 0;
|
|
3977
4251
|
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
@@ -4091,7 +4365,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
4091
4365
|
`${ind0} if (${sentOk}) break`,
|
|
4092
4366
|
`${ind0} ${scanV}++`,
|
|
4093
4367
|
`${ind0} }`,
|
|
4094
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(
|
|
4368
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
|
|
4095
4369
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
4096
4370
|
`${ind0} ${valVar} = ${errV}`,
|
|
4097
4371
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -4117,6 +4391,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4117
4391
|
return { stmts, valueVar: valVar, endVar };
|
|
4118
4392
|
}
|
|
4119
4393
|
function emit(p, ctx, pos) {
|
|
4394
|
+
const usage = ctx.lazyUsage;
|
|
4395
|
+
if (usage && !ctx.noHoist && !ctx.capAsTrivia && !ctx.ruleNames?.has(p) && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && // Only hoist subtrees big enough that de-duplicating them beats the call
|
|
4396
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4397
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4398
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4399
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4400
|
+
const existing = ctx.namedParsers.get(p);
|
|
4401
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4402
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4403
|
+
ctx.namedParsers.set(p, fnName);
|
|
4404
|
+
const savedIndent = ctx.indent;
|
|
4405
|
+
const savedFailLabel = ctx.failLabel;
|
|
4406
|
+
const savedRecord = ctx.recordFail;
|
|
4407
|
+
ctx.indent = 1;
|
|
4408
|
+
ctx.failLabel = "_pfail";
|
|
4409
|
+
ctx.recordFail = true;
|
|
4410
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4411
|
+
ctx.indent = savedIndent;
|
|
4412
|
+
ctx.failLabel = savedFailLabel;
|
|
4413
|
+
ctx.recordFail = savedRecord;
|
|
4414
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4415
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4416
|
+
}
|
|
4417
|
+
return emitDispatch(p, ctx, pos);
|
|
4418
|
+
}
|
|
4419
|
+
function emitDispatch(p, ctx, pos) {
|
|
4120
4420
|
const def = p._def;
|
|
4121
4421
|
switch (def.tag) {
|
|
4122
4422
|
case "literal":
|
|
@@ -4162,9 +4462,7 @@ function emit(p, ctx, pos) {
|
|
|
4162
4462
|
};
|
|
4163
4463
|
}
|
|
4164
4464
|
}
|
|
4165
|
-
const fnIdx = ctx.
|
|
4166
|
-
ctx.mapFns.push(def.fn);
|
|
4167
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4465
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4168
4466
|
const mv = v(ctx, "_mapped");
|
|
4169
4467
|
return {
|
|
4170
4468
|
stmts: [
|
|
@@ -4197,6 +4495,8 @@ function emit(p, ctx, pos) {
|
|
|
4197
4495
|
return emitLazy(p, def, ctx, pos);
|
|
4198
4496
|
case "trivia":
|
|
4199
4497
|
return emit(def.parser, ctx, pos);
|
|
4498
|
+
case "token":
|
|
4499
|
+
return emitToken(def, ctx, pos);
|
|
4200
4500
|
case "label":
|
|
4201
4501
|
return emit(def.parser, ctx, pos);
|
|
4202
4502
|
case "grammar": {
|
|
@@ -4229,6 +4529,7 @@ function emit(p, ctx, pos) {
|
|
|
4229
4529
|
case "guard": {
|
|
4230
4530
|
const fnIdx = ctx.mapFns.length;
|
|
4231
4531
|
ctx.mapFns.push(def.predicate);
|
|
4532
|
+
ctx.mapFnSrcs.push(null);
|
|
4232
4533
|
const vv = v(ctx);
|
|
4233
4534
|
return {
|
|
4234
4535
|
stmts: [
|
|
@@ -4243,6 +4544,7 @@ function emit(p, ctx, pos) {
|
|
|
4243
4544
|
const evIdx = ctx.mapFns.length;
|
|
4244
4545
|
const extra = def.extra;
|
|
4245
4546
|
ctx.mapFns.push((() => extra));
|
|
4547
|
+
ctx.mapFnSrcs.push(null);
|
|
4246
4548
|
const innerParser = def.parser;
|
|
4247
4549
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4248
4550
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4296,6 +4598,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4296
4598
|
}
|
|
4297
4599
|
case "grammar":
|
|
4298
4600
|
case "trivia":
|
|
4601
|
+
case "token":
|
|
4299
4602
|
case "label":
|
|
4300
4603
|
case "optional":
|
|
4301
4604
|
case "many":
|
|
@@ -4331,6 +4634,7 @@ function childrenOf(def) {
|
|
|
4331
4634
|
case "optional":
|
|
4332
4635
|
case "transform":
|
|
4333
4636
|
case "trivia":
|
|
4637
|
+
case "token":
|
|
4334
4638
|
case "label":
|
|
4335
4639
|
case "grammar":
|
|
4336
4640
|
case "not":
|
|
@@ -4362,9 +4666,58 @@ function childrenOf(def) {
|
|
|
4362
4666
|
return [];
|
|
4363
4667
|
}
|
|
4364
4668
|
}
|
|
4669
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4670
|
+
"sequence",
|
|
4671
|
+
"choice",
|
|
4672
|
+
"many",
|
|
4673
|
+
"oneOrMore",
|
|
4674
|
+
"optional",
|
|
4675
|
+
"sepBy",
|
|
4676
|
+
"transform",
|
|
4677
|
+
"node",
|
|
4678
|
+
"not"
|
|
4679
|
+
]);
|
|
4680
|
+
function isHoistableTag(tag) {
|
|
4681
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4682
|
+
}
|
|
4683
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4365
4684
|
function analyzeLazyUsage(root) {
|
|
4366
4685
|
return analyzeLazyUsageMulti([root]);
|
|
4367
4686
|
}
|
|
4687
|
+
function subtreeSizes(roots) {
|
|
4688
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4689
|
+
const sz = (p) => {
|
|
4690
|
+
const cached = sizes.get(p);
|
|
4691
|
+
if (cached !== void 0) return cached;
|
|
4692
|
+
const def = p._def;
|
|
4693
|
+
let s = 1;
|
|
4694
|
+
if (def.tag !== "lazy") {
|
|
4695
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4696
|
+
}
|
|
4697
|
+
sizes.set(p, s);
|
|
4698
|
+
return s;
|
|
4699
|
+
};
|
|
4700
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4701
|
+
const visit = (p) => {
|
|
4702
|
+
const def = p._def;
|
|
4703
|
+
if (def.tag === "lazy") {
|
|
4704
|
+
if (visitedLazy.has(p)) return;
|
|
4705
|
+
visitedLazy.add(p);
|
|
4706
|
+
let resolved;
|
|
4707
|
+
try {
|
|
4708
|
+
resolved = def.thunk();
|
|
4709
|
+
} catch {
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
visit(resolved);
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
sz(p);
|
|
4716
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4717
|
+
};
|
|
4718
|
+
for (const root of roots) visit(root);
|
|
4719
|
+
return sizes;
|
|
4720
|
+
}
|
|
4368
4721
|
function analyzeLazyUsageMulti(roots) {
|
|
4369
4722
|
const counts = /* @__PURE__ */ new Map();
|
|
4370
4723
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4391,10 +4744,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4391
4744
|
active.delete(p);
|
|
4392
4745
|
return;
|
|
4393
4746
|
}
|
|
4747
|
+
if (isHoistableTag(def.tag)) {
|
|
4748
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4749
|
+
}
|
|
4394
4750
|
for (const child of childrenOf(def)) walk2(child);
|
|
4395
4751
|
}
|
|
4396
|
-
|
|
4397
|
-
|
|
4752
|
+
const rootsArr = [...roots];
|
|
4753
|
+
for (const root of rootsArr) walk2(root);
|
|
4754
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4398
4755
|
}
|
|
4399
4756
|
function ruleDependencies(ruleMap) {
|
|
4400
4757
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4793,64 +5150,6 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
|
4793
5150
|
].join("\n");
|
|
4794
5151
|
}
|
|
4795
5152
|
|
|
4796
|
-
// src/compiler/linker.ts
|
|
4797
|
-
function fusedBody(pieces) {
|
|
4798
|
-
const winner = /* @__PURE__ */ new Map();
|
|
4799
|
-
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4800
|
-
for (const [k, p] of winner) {
|
|
4801
|
-
for (const d of p.deps.get(k) ?? []) {
|
|
4802
|
-
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4803
|
-
}
|
|
4804
|
-
}
|
|
4805
|
-
const contributing = new Set(winner.values());
|
|
4806
|
-
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4807
|
-
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4808
|
-
const lines = [
|
|
4809
|
-
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4810
|
-
"const _pfFail = {}",
|
|
4811
|
-
"let _pfEnd",
|
|
4812
|
-
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4813
|
-
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4814
|
-
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4815
|
-
...[...contributing].flatMap((p) => p.prelude),
|
|
4816
|
-
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4817
|
-
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4818
|
-
];
|
|
4819
|
-
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4820
|
-
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4821
|
-
const finalFS = /* @__PURE__ */ new Map();
|
|
4822
|
-
for (const [k, p] of winner) {
|
|
4823
|
-
const fs2 = p.firstSets?.get(k);
|
|
4824
|
-
if (fs2) finalFS.set(k, fs2);
|
|
4825
|
-
}
|
|
4826
|
-
const body = rawBody.replace(
|
|
4827
|
-
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4828
|
-
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4829
|
-
// matches every well-formed placeholder.
|
|
4830
|
-
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4831
|
-
(_m, name, codevar) => {
|
|
4832
|
-
const fs2 = finalFS.get(name);
|
|
4833
|
-
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4834
|
-
return `(${firstSetCond(codevar, fs2)})`;
|
|
4835
|
-
}
|
|
4836
|
-
);
|
|
4837
|
-
const env = {};
|
|
4838
|
-
for (const p of contributing) {
|
|
4839
|
-
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4840
|
-
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4841
|
-
}
|
|
4842
|
-
return { body, env };
|
|
4843
|
-
}
|
|
4844
|
-
function emitFusedSource(pieces) {
|
|
4845
|
-
const { body, env } = fusedBody(pieces);
|
|
4846
|
-
if (Object.keys(env).length > 0) {
|
|
4847
|
-
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4848
|
-
}
|
|
4849
|
-
return `/* @__PURE__ */ (() => {
|
|
4850
|
-
${body}
|
|
4851
|
-
})()`;
|
|
4852
|
-
}
|
|
4853
|
-
|
|
4854
5153
|
// src/combinators/scanTo.ts
|
|
4855
5154
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4856
5155
|
const meta = {
|
|
@@ -4919,7 +5218,7 @@ function balanced(open, close, options = {}) {
|
|
|
4919
5218
|
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
4920
5219
|
const inner = many(choice(self, ...skips, content));
|
|
4921
5220
|
const combi = transform(
|
|
4922
|
-
sequence(literal(open), inner, expect(literal(close)
|
|
5221
|
+
sequence(literal(open), inner, expect(literal(close))),
|
|
4923
5222
|
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
4924
5223
|
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
4925
5224
|
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
@@ -4945,6 +5244,359 @@ function escapeClassChar(ch) {
|
|
|
4945
5244
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4946
5245
|
}
|
|
4947
5246
|
|
|
5247
|
+
// src/compiler/ir-serialize.ts
|
|
5248
|
+
function childrenOf2(def) {
|
|
5249
|
+
switch (def.tag) {
|
|
5250
|
+
case "sequence":
|
|
5251
|
+
case "choice":
|
|
5252
|
+
return def.parsers;
|
|
5253
|
+
case "many":
|
|
5254
|
+
case "oneOrMore":
|
|
5255
|
+
case "optional":
|
|
5256
|
+
case "transform":
|
|
5257
|
+
case "trivia":
|
|
5258
|
+
case "token":
|
|
5259
|
+
case "label":
|
|
5260
|
+
case "not":
|
|
5261
|
+
case "node":
|
|
5262
|
+
case "expect":
|
|
5263
|
+
return [def.parser];
|
|
5264
|
+
case "grammar":
|
|
5265
|
+
return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
|
|
5266
|
+
case "sepBy":
|
|
5267
|
+
return [def.parser, def.separator];
|
|
5268
|
+
case "skip":
|
|
5269
|
+
return [def.main, def.skipped];
|
|
5270
|
+
case "scanTo":
|
|
5271
|
+
return [def.sentinel, ...def.skip];
|
|
5272
|
+
case "lazy":
|
|
5273
|
+
case "literal":
|
|
5274
|
+
case "regex":
|
|
5275
|
+
case "keywords":
|
|
5276
|
+
case "guard":
|
|
5277
|
+
case "withCtx":
|
|
5278
|
+
case "recover":
|
|
5279
|
+
case "unknown":
|
|
5280
|
+
return [];
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
var ruleNameOf = (c) => c._ruleName;
|
|
5284
|
+
function lazyTarget(c) {
|
|
5285
|
+
if (c._def.tag !== "lazy") return null;
|
|
5286
|
+
try {
|
|
5287
|
+
return c._def.thunk();
|
|
5288
|
+
} catch {
|
|
5289
|
+
return null;
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
var Unserializable = class extends Error {
|
|
5293
|
+
};
|
|
5294
|
+
function serializeRuleMap(ruleMap) {
|
|
5295
|
+
try {
|
|
5296
|
+
return new Serializer(ruleMap).run();
|
|
5297
|
+
} catch (e) {
|
|
5298
|
+
if (e instanceof Unserializable) {
|
|
5299
|
+
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
5300
|
+
return null;
|
|
5301
|
+
}
|
|
5302
|
+
throw e;
|
|
5303
|
+
}
|
|
5304
|
+
}
|
|
5305
|
+
function evalRuleMapIR(ir) {
|
|
5306
|
+
const _tf = (child, src) => {
|
|
5307
|
+
let fn2;
|
|
5308
|
+
try {
|
|
5309
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5310
|
+
} catch {
|
|
5311
|
+
fn2 = () => {
|
|
5312
|
+
throw new Error("IR transform fn not materialized");
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5315
|
+
const t = transform(child, fn2);
|
|
5316
|
+
t._def.fnSrc = src;
|
|
5317
|
+
return t;
|
|
5318
|
+
};
|
|
5319
|
+
const _nd = (type, child, src, opts) => {
|
|
5320
|
+
const n = node(type, child, void 0, opts);
|
|
5321
|
+
n._def.buildSrc = src;
|
|
5322
|
+
return n;
|
|
5323
|
+
};
|
|
5324
|
+
const fn = new Function(
|
|
5325
|
+
"rules",
|
|
5326
|
+
"ref",
|
|
5327
|
+
"regex",
|
|
5328
|
+
"literal",
|
|
5329
|
+
"keywords",
|
|
5330
|
+
"sequence",
|
|
5331
|
+
"choice",
|
|
5332
|
+
"many",
|
|
5333
|
+
"oneOrMore",
|
|
5334
|
+
"optional",
|
|
5335
|
+
"sepBy",
|
|
5336
|
+
"not",
|
|
5337
|
+
"node",
|
|
5338
|
+
"parser",
|
|
5339
|
+
"scanTo",
|
|
5340
|
+
"token",
|
|
5341
|
+
"transform",
|
|
5342
|
+
"skip",
|
|
5343
|
+
"trivia",
|
|
5344
|
+
"label",
|
|
5345
|
+
"expect",
|
|
5346
|
+
"_tf",
|
|
5347
|
+
"_nd",
|
|
5348
|
+
`return (${ir})`
|
|
5349
|
+
);
|
|
5350
|
+
const map = fn(
|
|
5351
|
+
rules,
|
|
5352
|
+
ref,
|
|
5353
|
+
regex,
|
|
5354
|
+
literal,
|
|
5355
|
+
keywords,
|
|
5356
|
+
sequence,
|
|
5357
|
+
choice,
|
|
5358
|
+
many,
|
|
5359
|
+
oneOrMore,
|
|
5360
|
+
optional,
|
|
5361
|
+
sepBy,
|
|
5362
|
+
not,
|
|
5363
|
+
node,
|
|
5364
|
+
parser,
|
|
5365
|
+
scanTo,
|
|
5366
|
+
token,
|
|
5367
|
+
transform,
|
|
5368
|
+
skip,
|
|
5369
|
+
trivia,
|
|
5370
|
+
label,
|
|
5371
|
+
expect,
|
|
5372
|
+
_tf,
|
|
5373
|
+
_nd
|
|
5374
|
+
);
|
|
5375
|
+
return Object.entries(map);
|
|
5376
|
+
}
|
|
5377
|
+
var Serializer = class {
|
|
5378
|
+
counts = /* @__PURE__ */ new Map();
|
|
5379
|
+
selfRef = /* @__PURE__ */ new Set();
|
|
5380
|
+
// combinators reached by an unnamed lazy pointing back into their own subtree
|
|
5381
|
+
constName = /* @__PURE__ */ new Map();
|
|
5382
|
+
emitted = /* @__PURE__ */ new Set();
|
|
5383
|
+
decls = [];
|
|
5384
|
+
ruleValues = /* @__PURE__ */ new Set();
|
|
5385
|
+
ruleMap;
|
|
5386
|
+
constructor(ruleMap) {
|
|
5387
|
+
this.ruleMap = ruleMap;
|
|
5388
|
+
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
5389
|
+
}
|
|
5390
|
+
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
5391
|
+
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
5392
|
+
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
5393
|
+
body(c) {
|
|
5394
|
+
return c._def.tag === "lazy" && ruleNameOf(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
5395
|
+
}
|
|
5396
|
+
run() {
|
|
5397
|
+
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
5398
|
+
let n = 0;
|
|
5399
|
+
for (const [c, count] of this.counts) {
|
|
5400
|
+
if (this.ruleValues.has(c)) continue;
|
|
5401
|
+
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
5402
|
+
}
|
|
5403
|
+
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
5404
|
+
if (this.decls.length === 0) return `rules((g) => ({
|
|
5405
|
+
${body}
|
|
5406
|
+
}))`;
|
|
5407
|
+
return `rules((g) => {
|
|
5408
|
+
${this.decls.map((d) => " " + d).join("\n")}
|
|
5409
|
+
return ({
|
|
5410
|
+
${body}
|
|
5411
|
+
})
|
|
5412
|
+
})`;
|
|
5413
|
+
}
|
|
5414
|
+
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
5415
|
+
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
5416
|
+
analyze(c, active) {
|
|
5417
|
+
const def = c._def;
|
|
5418
|
+
if (def.tag === "lazy") {
|
|
5419
|
+
if (ruleNameOf(c) !== void 0) return;
|
|
5420
|
+
const target = lazyTarget(c);
|
|
5421
|
+
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
5422
|
+
if (active.has(target)) {
|
|
5423
|
+
this.selfRef.add(target);
|
|
5424
|
+
return;
|
|
5425
|
+
}
|
|
5426
|
+
this.analyze(target, active);
|
|
5427
|
+
return;
|
|
5428
|
+
}
|
|
5429
|
+
if (def.tag === "guard" || def.tag === "withCtx" || def.tag === "recover" || def.tag === "unknown") {
|
|
5430
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5431
|
+
}
|
|
5432
|
+
this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
|
|
5433
|
+
if ((this.counts.get(c) ?? 0) > 1) return;
|
|
5434
|
+
active.add(c);
|
|
5435
|
+
for (const child of childrenOf2(def)) this.analyze(child, active);
|
|
5436
|
+
active.delete(c);
|
|
5437
|
+
}
|
|
5438
|
+
/** Reference `c`: a const var if it has one (emitting its decl first), else inline.
|
|
5439
|
+
* `selfOf` is the const currently being wrapped in a ref()/define closure — an
|
|
5440
|
+
* unnamed lazy back into it emits the local ref var instead of recursing. */
|
|
5441
|
+
ref(c, selfOf) {
|
|
5442
|
+
const cn = this.constName.get(c);
|
|
5443
|
+
if (cn) {
|
|
5444
|
+
this.emitDecl(c);
|
|
5445
|
+
return cn;
|
|
5446
|
+
}
|
|
5447
|
+
return this.expr(c, selfOf);
|
|
5448
|
+
}
|
|
5449
|
+
emitDecl(c) {
|
|
5450
|
+
const name = this.constName.get(c);
|
|
5451
|
+
if (this.emitted.has(name)) return;
|
|
5452
|
+
this.emitted.add(name);
|
|
5453
|
+
this.decls.push(`const ${name} = ${this.wrap(c)}`);
|
|
5454
|
+
}
|
|
5455
|
+
selfN = 0;
|
|
5456
|
+
/** Emit `c`'s own constructor call, wrapping it in a `ref()`+`.define()` closure
|
|
5457
|
+
* if it is self-referential (an unnamed lazy in its subtree points back to it) —
|
|
5458
|
+
* the shape `balanced()` and other recursive library combinators build. */
|
|
5459
|
+
wrap(c) {
|
|
5460
|
+
if (!this.selfRef.has(c)) return this.expr(c, null);
|
|
5461
|
+
const rv = `_rr${this.selfN++}`;
|
|
5462
|
+
const bodyExpr = this.expr(c, { comb: c, var: rv });
|
|
5463
|
+
return `(() => { const ${rv} = ref(); const _b = ${bodyExpr}; ${rv}.define(_b); return _b })()`;
|
|
5464
|
+
}
|
|
5465
|
+
/** Emit the constructor-call source for `c` (never a const shortcut for `c` itself). */
|
|
5466
|
+
expr(c, selfOf) {
|
|
5467
|
+
const def = c._def;
|
|
5468
|
+
const kid = (x) => this.ref(x, selfOf);
|
|
5469
|
+
switch (def.tag) {
|
|
5470
|
+
case "lazy": {
|
|
5471
|
+
if (ruleNameOf(c) !== void 0) return `g[${JSON.stringify(ruleNameOf(c))}]`;
|
|
5472
|
+
const target = lazyTarget(c);
|
|
5473
|
+
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
5474
|
+
return this.ref(target, selfOf);
|
|
5475
|
+
}
|
|
5476
|
+
case "literal":
|
|
5477
|
+
return `literal(${JSON.stringify(def.value)}${def.caseInsensitive ? ", { caseInsensitive: true }" : ""})`;
|
|
5478
|
+
case "regex":
|
|
5479
|
+
return `regex(${JSON.stringify(def.source)}, ${JSON.stringify(def.flags)})`;
|
|
5480
|
+
case "keywords":
|
|
5481
|
+
return `keywords(${JSON.stringify(def.words)}, { caseInsensitive: ${def.caseInsensitive}${def.boundary !== void 0 ? `, boundary: ${JSON.stringify(def.boundary)}` : ""} })`;
|
|
5482
|
+
case "sequence":
|
|
5483
|
+
return `sequence(${def.parsers.map(kid).join(", ")})`;
|
|
5484
|
+
case "choice": {
|
|
5485
|
+
if (def.gates.some((g) => g !== null)) throw new Unserializable("choice with a gate() has no source");
|
|
5486
|
+
return `choice(${def.parsers.map(kid).join(", ")})`;
|
|
5487
|
+
}
|
|
5488
|
+
case "many":
|
|
5489
|
+
return `many(${kid(def.parser)})`;
|
|
5490
|
+
case "oneOrMore":
|
|
5491
|
+
return `oneOrMore(${kid(def.parser)})`;
|
|
5492
|
+
case "optional":
|
|
5493
|
+
return `optional(${kid(def.parser)})`;
|
|
5494
|
+
case "sepBy":
|
|
5495
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
5496
|
+
case "not":
|
|
5497
|
+
return `not(${kid(def.parser)})`;
|
|
5498
|
+
case "trivia":
|
|
5499
|
+
return `trivia(${kid(def.parser)})`;
|
|
5500
|
+
case "token":
|
|
5501
|
+
return `token(${kid(def.parser)})`;
|
|
5502
|
+
case "label":
|
|
5503
|
+
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
5504
|
+
case "expect":
|
|
5505
|
+
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
5506
|
+
case "skip":
|
|
5507
|
+
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
5508
|
+
case "scanTo":
|
|
5509
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
5510
|
+
case "transform": {
|
|
5511
|
+
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
5512
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
5513
|
+
}
|
|
5514
|
+
case "node": {
|
|
5515
|
+
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
5516
|
+
const opts = def.collapse ? `, { collapse: true }` : "";
|
|
5517
|
+
if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
|
|
5518
|
+
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
5519
|
+
}
|
|
5520
|
+
case "grammar": {
|
|
5521
|
+
const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
|
|
5522
|
+
return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
|
|
5523
|
+
}
|
|
5524
|
+
case "guard":
|
|
5525
|
+
case "withCtx":
|
|
5526
|
+
case "recover":
|
|
5527
|
+
case "unknown":
|
|
5528
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
};
|
|
5532
|
+
|
|
5533
|
+
// src/compiler/linker.ts
|
|
5534
|
+
function fusedBody(pieces) {
|
|
5535
|
+
const winner = /* @__PURE__ */ new Map();
|
|
5536
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
5537
|
+
for (const [k, p] of winner) {
|
|
5538
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
5539
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5542
|
+
const contributing = new Set(winner.values());
|
|
5543
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
5544
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
5545
|
+
const lines = [
|
|
5546
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
5547
|
+
"const _pfFail = {}",
|
|
5548
|
+
"let _pfEnd",
|
|
5549
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
5550
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
5551
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
5552
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
5553
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
5554
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
5555
|
+
];
|
|
5556
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
5557
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
5558
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
5559
|
+
for (const [k, p] of winner) {
|
|
5560
|
+
const fs2 = p.firstSets?.get(k);
|
|
5561
|
+
if (fs2) finalFS.set(k, fs2);
|
|
5562
|
+
}
|
|
5563
|
+
const body = rawBody.replace(
|
|
5564
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
5565
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
5566
|
+
// matches every well-formed placeholder.
|
|
5567
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
5568
|
+
(_m, name, codevar) => {
|
|
5569
|
+
const fs2 = finalFS.get(name);
|
|
5570
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
5571
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
5572
|
+
}
|
|
5573
|
+
);
|
|
5574
|
+
const env = {};
|
|
5575
|
+
for (const p of contributing) {
|
|
5576
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
5577
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
5578
|
+
}
|
|
5579
|
+
return { body, env };
|
|
5580
|
+
}
|
|
5581
|
+
function emitFusedSource(pieces) {
|
|
5582
|
+
const { body, env } = fusedBody(pieces);
|
|
5583
|
+
if (Object.keys(env).length > 0) {
|
|
5584
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
5585
|
+
}
|
|
5586
|
+
return `/* @__PURE__ */ (() => {
|
|
5587
|
+
${body}
|
|
5588
|
+
})()`;
|
|
5589
|
+
}
|
|
5590
|
+
function isIRPiece(p) {
|
|
5591
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5592
|
+
}
|
|
5593
|
+
function materializePiece(p) {
|
|
5594
|
+
if (!isIRPiece(p)) return p;
|
|
5595
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5596
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5597
|
+
return pieces;
|
|
5598
|
+
}
|
|
5599
|
+
|
|
4948
5600
|
// src/cst/offset-model.ts
|
|
4949
5601
|
var { isFinite } = Number;
|
|
4950
5602
|
|
|
@@ -4969,6 +5621,7 @@ var SUPPORTED = {
|
|
|
4969
5621
|
trivia: (...a) => trivia(a[0]),
|
|
4970
5622
|
label: (...a) => label(a[0], a[1]),
|
|
4971
5623
|
noTrivia: (...a) => noTrivia(a[0]),
|
|
5624
|
+
token: (...a) => token(a[0]),
|
|
4972
5625
|
expect: (...a) => expect(a[0], a[1])
|
|
4973
5626
|
};
|
|
4974
5627
|
function isScopeEntry(v2) {
|
|
@@ -5518,6 +6171,14 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5518
6171
|
const obj = callee?.object?.name;
|
|
5519
6172
|
const prop = callee?.property?.name;
|
|
5520
6173
|
const args = node2.arguments;
|
|
6174
|
+
if (obj === "Object" && prop === "assign" && args && args[1]?.type === "ObjectExpression") {
|
|
6175
|
+
for (const p of args[1].properties ?? []) {
|
|
6176
|
+
if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
|
|
6177
|
+
found = { start: p.value.start, end: p.value.end };
|
|
6178
|
+
return;
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
5521
6182
|
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5522
6183
|
const descriptor = args[2];
|
|
5523
6184
|
for (const p of descriptor?.properties ?? []) {
|
|
@@ -5538,6 +6199,20 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5538
6199
|
visit(root);
|
|
5539
6200
|
return found;
|
|
5540
6201
|
}
|
|
6202
|
+
function extractImportBindings(body) {
|
|
6203
|
+
const out = /* @__PURE__ */ new Map();
|
|
6204
|
+
for (const stmt of body) {
|
|
6205
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
6206
|
+
const s = stmt;
|
|
6207
|
+
for (const spec of s.specifiers) {
|
|
6208
|
+
if (spec.type === "ImportSpecifier") {
|
|
6209
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
6210
|
+
out.set(spec.local.name, { source: s.source.value, imported });
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
return out;
|
|
6215
|
+
}
|
|
5541
6216
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5542
6217
|
let result;
|
|
5543
6218
|
try {
|
|
@@ -5612,14 +6287,57 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5612
6287
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5613
6288
|
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5614
6289
|
const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
6290
|
+
const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
|
|
5615
6291
|
const serializePieces = (p) => {
|
|
5616
|
-
const mapLit = (m) => `new Map([${[...m].map(
|
|
5617
|
-
|
|
6292
|
+
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
6293
|
+
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
6294
|
+
).join(", ")}])`;
|
|
6295
|
+
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, mfFns: [], buildFns: [] }`;
|
|
5618
6296
|
};
|
|
5619
|
-
const
|
|
5620
|
-
const
|
|
6297
|
+
const isSpread = (it) => "__spreadLocal" in it;
|
|
6298
|
+
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
6299
|
+
const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
|
|
6300
|
+
const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
|
|
6301
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
|
|
5621
6302
|
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5622
6303
|
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
6304
|
+
const resolveModulePieces = (file, exportName, seen) => {
|
|
6305
|
+
const mod = parseModuleCached(file);
|
|
6306
|
+
if (!mod) return null;
|
|
6307
|
+
const localFor = exportLocalName(mod.body, exportName);
|
|
6308
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
6309
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
6310
|
+
if (!literalRange) return null;
|
|
6311
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
6312
|
+
const imports = extractImportBindings(mod.body);
|
|
6313
|
+
const stubNames = [];
|
|
6314
|
+
const stubVals = [];
|
|
6315
|
+
const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
|
|
6316
|
+
const done = /* @__PURE__ */ new Set();
|
|
6317
|
+
for (let m; m = spreadRe.exec(literal2); ) {
|
|
6318
|
+
const local = m[1];
|
|
6319
|
+
if (done.has(local)) continue;
|
|
6320
|
+
done.add(local);
|
|
6321
|
+
const b = imports.get(local);
|
|
6322
|
+
let subPieces = [];
|
|
6323
|
+
if (b) {
|
|
6324
|
+
let subFile = null;
|
|
6325
|
+
try {
|
|
6326
|
+
subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
|
|
6327
|
+
} catch {
|
|
6328
|
+
}
|
|
6329
|
+
if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
|
|
6330
|
+
}
|
|
6331
|
+
stubNames.push(local);
|
|
6332
|
+
stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
|
|
6333
|
+
}
|
|
6334
|
+
try {
|
|
6335
|
+
const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
|
|
6336
|
+
return list.map(materializePiece);
|
|
6337
|
+
} catch {
|
|
6338
|
+
return null;
|
|
6339
|
+
}
|
|
6340
|
+
};
|
|
5623
6341
|
const importedPieces = (localName) => {
|
|
5624
6342
|
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5625
6343
|
let result2 = null;
|
|
@@ -5630,41 +6348,34 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5630
6348
|
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5631
6349
|
} catch {
|
|
5632
6350
|
}
|
|
5633
|
-
|
|
5634
|
-
if (mod) {
|
|
5635
|
-
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5636
|
-
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5637
|
-
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5638
|
-
if (literalRange) {
|
|
5639
|
-
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5640
|
-
try {
|
|
5641
|
-
result2 = new Function(`return (${literal2})`)();
|
|
5642
|
-
} catch {
|
|
5643
|
-
result2 = null;
|
|
5644
|
-
}
|
|
5645
|
-
}
|
|
5646
|
-
}
|
|
6351
|
+
result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
|
|
5647
6352
|
}
|
|
5648
6353
|
importedPiecesCache.set(localName, result2);
|
|
5649
6354
|
return result2;
|
|
5650
6355
|
};
|
|
6356
|
+
const localArg = (rm, label2) => {
|
|
6357
|
+
const entries = [...rm];
|
|
6358
|
+
const ns = nsFor(label2);
|
|
6359
|
+
const p = compileLinkable(entries, ns);
|
|
6360
|
+
if (!p) return null;
|
|
6361
|
+
const ir = serializeRuleMap(entries);
|
|
6362
|
+
return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
|
|
6363
|
+
};
|
|
5651
6364
|
const argPieces = (arg, label2) => {
|
|
5652
6365
|
if (isRulesCall(arg)) {
|
|
5653
6366
|
const factory = arg.arguments[0];
|
|
5654
6367
|
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5655
|
-
|
|
5656
|
-
return p ? [p] : null;
|
|
6368
|
+
return rm ? localArg(rm, label2) : null;
|
|
5657
6369
|
}
|
|
5658
6370
|
if (arg.type === "Identifier") {
|
|
5659
6371
|
const name = arg.name;
|
|
5660
6372
|
const rm = localRuleMaps.get(name);
|
|
5661
|
-
if (rm)
|
|
5662
|
-
const p = compileLinkable([...rm], nsFor(label2));
|
|
5663
|
-
return p ? [p] : null;
|
|
5664
|
-
}
|
|
6373
|
+
if (rm) return localArg(rm, label2);
|
|
5665
6374
|
const composed = localComposedPieces.get(name);
|
|
5666
|
-
if (composed) return composed;
|
|
5667
|
-
|
|
6375
|
+
if (composed) return { pieces: composed };
|
|
6376
|
+
const pieces = importedPieces(name);
|
|
6377
|
+
if (!pieces) return null;
|
|
6378
|
+
return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
|
|
5668
6379
|
}
|
|
5669
6380
|
return null;
|
|
5670
6381
|
};
|
|
@@ -5677,16 +6388,20 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5677
6388
|
}
|
|
5678
6389
|
const elements = arr.elements;
|
|
5679
6390
|
const pieces = [];
|
|
6391
|
+
const carried = [];
|
|
5680
6392
|
for (let i = 0; i < elements.length; i++) {
|
|
5681
|
-
const
|
|
5682
|
-
if (!
|
|
6393
|
+
const r = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
6394
|
+
if (!r) {
|
|
5683
6395
|
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5684
6396
|
return null;
|
|
5685
6397
|
}
|
|
5686
|
-
pieces.push(...
|
|
6398
|
+
pieces.push(...r.pieces);
|
|
6399
|
+
if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
|
|
6400
|
+
else if (r.ir) carried.push(r.ir);
|
|
6401
|
+
else carried.push(...r.pieces);
|
|
5687
6402
|
}
|
|
5688
6403
|
try {
|
|
5689
|
-
return { replacement: emitFusedSource(pieces), pieces };
|
|
6404
|
+
return { replacement: emitFusedSource(pieces), pieces, carried };
|
|
5690
6405
|
} catch (e) {
|
|
5691
6406
|
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5692
6407
|
return null;
|
|
@@ -5773,9 +6488,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5773
6488
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5774
6489
|
let replacement = compiledRules.replacement;
|
|
5775
6490
|
if (exportPrefix) {
|
|
5776
|
-
const
|
|
6491
|
+
const ns = nsFor(varName);
|
|
6492
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], ns);
|
|
5777
6493
|
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5778
|
-
|
|
6494
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
6495
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
5779
6496
|
}
|
|
5780
6497
|
}
|
|
5781
6498
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
@@ -5785,7 +6502,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5785
6502
|
const fused = compileComposeCall(init);
|
|
5786
6503
|
if (!fused) continue;
|
|
5787
6504
|
localComposedPieces.set(varName, fused.pieces);
|
|
5788
|
-
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.
|
|
6505
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
5789
6506
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5790
6507
|
continue;
|
|
5791
6508
|
}
|