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.cjs
CHANGED
|
@@ -100,6 +100,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
100
100
|
case "transform":
|
|
101
101
|
case "label":
|
|
102
102
|
case "trivia":
|
|
103
|
+
case "token":
|
|
103
104
|
case "expect":
|
|
104
105
|
case "withCtx":
|
|
105
106
|
case "node":
|
|
@@ -126,6 +127,55 @@ function sequenceFirstSet(parsers) {
|
|
|
126
127
|
}
|
|
127
128
|
return fs2;
|
|
128
129
|
}
|
|
130
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
131
|
+
if (seen.has(p)) return any();
|
|
132
|
+
seen.add(p);
|
|
133
|
+
const fs2 = (c) => firstSetOf(c, seen);
|
|
134
|
+
const d = p._def;
|
|
135
|
+
switch (d.tag) {
|
|
136
|
+
case "literal":
|
|
137
|
+
case "regex":
|
|
138
|
+
case "keywords":
|
|
139
|
+
return p._meta.firstSet;
|
|
140
|
+
// terminals: no refs, cached set is exact
|
|
141
|
+
case "lazy":
|
|
142
|
+
try {
|
|
143
|
+
return fs2(d.thunk());
|
|
144
|
+
} catch {
|
|
145
|
+
return any();
|
|
146
|
+
}
|
|
147
|
+
case "choice": {
|
|
148
|
+
let out = empty();
|
|
149
|
+
for (const arm of d.parsers) out = union(out, fs2(arm));
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
case "sequence": {
|
|
153
|
+
let out = empty();
|
|
154
|
+
for (const term of d.parsers) {
|
|
155
|
+
out = union(out, fs2(term));
|
|
156
|
+
if (!matchesEmpty(term)) return out;
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
case "oneOrMore":
|
|
161
|
+
case "many":
|
|
162
|
+
case "optional":
|
|
163
|
+
case "transform":
|
|
164
|
+
case "label":
|
|
165
|
+
case "trivia":
|
|
166
|
+
case "token":
|
|
167
|
+
case "node":
|
|
168
|
+
case "grammar":
|
|
169
|
+
case "expect":
|
|
170
|
+
return fs2(d.parser);
|
|
171
|
+
case "sepBy":
|
|
172
|
+
return fs2(d.parser);
|
|
173
|
+
case "skip":
|
|
174
|
+
return fs2(d.main);
|
|
175
|
+
default:
|
|
176
|
+
return p._meta.firstSet;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
129
179
|
function mergeRanges(ranges) {
|
|
130
180
|
if (ranges.length === 0) return [];
|
|
131
181
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -1237,6 +1287,7 @@ function markUnusedValues(root) {
|
|
|
1237
1287
|
return;
|
|
1238
1288
|
case "label":
|
|
1239
1289
|
case "trivia":
|
|
1290
|
+
case "token":
|
|
1240
1291
|
case "withCtx":
|
|
1241
1292
|
case "expect":
|
|
1242
1293
|
visit(def.parser, consumed);
|
|
@@ -1265,6 +1316,41 @@ function markUnusedValues(root) {
|
|
|
1265
1316
|
visit(resolveRoot(root), true);
|
|
1266
1317
|
}
|
|
1267
1318
|
|
|
1319
|
+
// src/combinators/parser.ts
|
|
1320
|
+
function rules(factory) {
|
|
1321
|
+
const cache = {};
|
|
1322
|
+
const proxy = new Proxy(cache, {
|
|
1323
|
+
get(target, key) {
|
|
1324
|
+
if (typeof key !== "string") return void 0;
|
|
1325
|
+
const record = target;
|
|
1326
|
+
if (!(key in record)) {
|
|
1327
|
+
const r = ref();
|
|
1328
|
+
r._ruleName = key;
|
|
1329
|
+
record[key] = r;
|
|
1330
|
+
}
|
|
1331
|
+
return record[key];
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
const definitions = factory(proxy);
|
|
1335
|
+
for (const key of Object.keys(definitions)) {
|
|
1336
|
+
const placeholder = cache[key];
|
|
1337
|
+
const parser2 = definitions[key];
|
|
1338
|
+
if (placeholder !== void 0 && typeof placeholder.define === "function") {
|
|
1339
|
+
;
|
|
1340
|
+
placeholder.define(parser2);
|
|
1341
|
+
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
1342
|
+
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
1343
|
+
} else {
|
|
1344
|
+
;
|
|
1345
|
+
cache[key] = parser2;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
for (const key of Object.keys(definitions)) {
|
|
1349
|
+
markUnusedValues(cache[key]);
|
|
1350
|
+
}
|
|
1351
|
+
return cache;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1268
1354
|
// src/combinators/not.ts
|
|
1269
1355
|
function not(combinator) {
|
|
1270
1356
|
const meta = {
|
|
@@ -1371,6 +1457,20 @@ function transform(combinator, fn) {
|
|
|
1371
1457
|
}
|
|
1372
1458
|
};
|
|
1373
1459
|
}
|
|
1460
|
+
function skip(main, skipped) {
|
|
1461
|
+
return {
|
|
1462
|
+
_tag: "skip",
|
|
1463
|
+
_meta: main._meta,
|
|
1464
|
+
_def: { tag: "skip", main, skipped },
|
|
1465
|
+
parse(input, pos, ctx) {
|
|
1466
|
+
const result = main.parse(input, pos, ctx);
|
|
1467
|
+
if (!result.ok) return result;
|
|
1468
|
+
const s = skipped.parse(input, result.span.end, ctx);
|
|
1469
|
+
if (!s.ok) return result;
|
|
1470
|
+
return { ...result, span: { start: result.span.start, end: s.span.end } };
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1374
1474
|
function trivia(combinator) {
|
|
1375
1475
|
const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
|
|
1376
1476
|
return {
|
|
@@ -1465,8 +1565,60 @@ function noTrivia(root) {
|
|
|
1465
1565
|
return parser({ trivia: null }, root);
|
|
1466
1566
|
}
|
|
1467
1567
|
|
|
1568
|
+
// src/combinators/token.ts
|
|
1569
|
+
function token(root) {
|
|
1570
|
+
const meta = {
|
|
1571
|
+
firstSet: root._meta.firstSet,
|
|
1572
|
+
canMatchNewline: root._meta.canMatchNewline,
|
|
1573
|
+
isTrivia: false
|
|
1574
|
+
};
|
|
1575
|
+
return {
|
|
1576
|
+
_tag: "token",
|
|
1577
|
+
_meta: meta,
|
|
1578
|
+
_def: { tag: "token", parser: root },
|
|
1579
|
+
parse(input, pos, ctx) {
|
|
1580
|
+
const savedTrivia = ctx.trivia;
|
|
1581
|
+
const savedKinds = ctx.triviaKindLabels;
|
|
1582
|
+
const savedBuf = ctx._cstBuf;
|
|
1583
|
+
const savedChildren = ctx._cstChildren;
|
|
1584
|
+
const savedLeaves = ctx._cstLeaves;
|
|
1585
|
+
const savedRaw = ctx._cstRawChildren;
|
|
1586
|
+
const savedTriviaLog = ctx._cstTriviaLog;
|
|
1587
|
+
const savedOuterTriviaLog = ctx._triviaLog;
|
|
1588
|
+
const wasCapturing = cstCaptureActive(ctx);
|
|
1589
|
+
ctx.trivia = void 0;
|
|
1590
|
+
ctx.triviaKindLabels = void 0;
|
|
1591
|
+
ctx._cstBuf = void 0;
|
|
1592
|
+
ctx._cstChildren = void 0;
|
|
1593
|
+
ctx._cstLeaves = void 0;
|
|
1594
|
+
ctx._cstRawChildren = void 0;
|
|
1595
|
+
ctx._cstTriviaLog = void 0;
|
|
1596
|
+
delete ctx._triviaLog;
|
|
1597
|
+
let result;
|
|
1598
|
+
try {
|
|
1599
|
+
result = root.parse(input, pos, ctx);
|
|
1600
|
+
} finally {
|
|
1601
|
+
ctx.trivia = savedTrivia;
|
|
1602
|
+
ctx.triviaKindLabels = savedKinds;
|
|
1603
|
+
ctx._cstBuf = savedBuf;
|
|
1604
|
+
ctx._cstChildren = savedChildren;
|
|
1605
|
+
ctx._cstLeaves = savedLeaves;
|
|
1606
|
+
ctx._cstRawChildren = savedRaw;
|
|
1607
|
+
ctx._cstTriviaLog = savedTriviaLog;
|
|
1608
|
+
if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
|
|
1609
|
+
else ctx._triviaLog = savedOuterTriviaLog;
|
|
1610
|
+
}
|
|
1611
|
+
if (!result.ok) return result;
|
|
1612
|
+
const span = result.span;
|
|
1613
|
+
const value = input.slice(pos, span.end);
|
|
1614
|
+
if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span });
|
|
1615
|
+
return { ok: true, value, span };
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1468
1620
|
// src/combinators/expect.ts
|
|
1469
|
-
function
|
|
1621
|
+
function deriveExpected(c) {
|
|
1470
1622
|
const def = c._def;
|
|
1471
1623
|
switch (def.tag) {
|
|
1472
1624
|
case "literal":
|
|
@@ -1478,22 +1630,23 @@ function staticExpected(c) {
|
|
|
1478
1630
|
case "label":
|
|
1479
1631
|
return [def.label];
|
|
1480
1632
|
case "choice":
|
|
1481
|
-
return def.parsers.flatMap(
|
|
1633
|
+
return def.parsers.flatMap(deriveExpected);
|
|
1482
1634
|
case "sequence":
|
|
1483
|
-
return def.parsers.length > 0 ?
|
|
1635
|
+
return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
|
|
1484
1636
|
case "node":
|
|
1485
1637
|
case "grammar":
|
|
1486
1638
|
case "trivia":
|
|
1639
|
+
case "token":
|
|
1487
1640
|
case "optional":
|
|
1488
1641
|
case "many":
|
|
1489
1642
|
case "oneOrMore":
|
|
1490
1643
|
case "transform":
|
|
1491
1644
|
case "not":
|
|
1492
|
-
return
|
|
1645
|
+
return deriveExpected(def.parser);
|
|
1493
1646
|
case "lazy": {
|
|
1494
1647
|
const name = c._ruleName;
|
|
1495
1648
|
try {
|
|
1496
|
-
return
|
|
1649
|
+
return deriveExpected(def.thunk());
|
|
1497
1650
|
} catch {
|
|
1498
1651
|
return name ? [name] : [];
|
|
1499
1652
|
}
|
|
@@ -1503,7 +1656,7 @@ function staticExpected(c) {
|
|
|
1503
1656
|
}
|
|
1504
1657
|
}
|
|
1505
1658
|
function expect(combinator, label2) {
|
|
1506
|
-
const expected = label2 !== void 0 ? [label2] :
|
|
1659
|
+
const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
|
|
1507
1660
|
const meta = {
|
|
1508
1661
|
firstSet: combinator._meta.firstSet,
|
|
1509
1662
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
@@ -2999,16 +3152,12 @@ function namedFnPrelude() {
|
|
|
2999
3152
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
3000
3153
|
ctx.namedFnDecls.push([
|
|
3001
3154
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
3002
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
3003
3155
|
` _pfail: {`,
|
|
3004
3156
|
...reindentStmts(bodyStmts, 2),
|
|
3005
|
-
`
|
|
3006
|
-
`
|
|
3007
|
-
` _pfok = true`,
|
|
3157
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3158
|
+
` return ${valueVar}`,
|
|
3008
3159
|
` }`,
|
|
3009
|
-
`
|
|
3010
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
3011
|
-
` return _pfv`,
|
|
3160
|
+
` return ${NAMED_FN_FAIL}`,
|
|
3012
3161
|
`}`
|
|
3013
3162
|
].join("\n"));
|
|
3014
3163
|
}
|
|
@@ -3167,6 +3316,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
3167
3316
|
case "guard":
|
|
3168
3317
|
case "not":
|
|
3169
3318
|
case "trivia":
|
|
3319
|
+
case "token":
|
|
3170
3320
|
case "scanTo":
|
|
3171
3321
|
case "unknown":
|
|
3172
3322
|
return false;
|
|
@@ -3228,6 +3378,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
3228
3378
|
case "regex":
|
|
3229
3379
|
case "keywords":
|
|
3230
3380
|
case "node":
|
|
3381
|
+
case "token":
|
|
3231
3382
|
return true;
|
|
3232
3383
|
case "not":
|
|
3233
3384
|
case "guard":
|
|
@@ -3506,9 +3657,9 @@ function emitSeq(def, ctx, pos) {
|
|
|
3506
3657
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3507
3658
|
return { stmts, valueVar: arrV, endVar };
|
|
3508
3659
|
}
|
|
3509
|
-
function
|
|
3660
|
+
function deriveExpectedArr(parsers) {
|
|
3510
3661
|
return JSON.stringify(parsers.flatMap((p) => {
|
|
3511
|
-
const e =
|
|
3662
|
+
const e = deriveExpected(p);
|
|
3512
3663
|
return e.length > 0 ? e : [p._tag];
|
|
3513
3664
|
}));
|
|
3514
3665
|
}
|
|
@@ -3529,7 +3680,7 @@ function failsAtStart(p) {
|
|
|
3529
3680
|
}
|
|
3530
3681
|
}
|
|
3531
3682
|
function armStaticExpected(ctx, p) {
|
|
3532
|
-
return hoistExpected(ctx,
|
|
3683
|
+
return hoistExpected(ctx, deriveExpectedArr([p]));
|
|
3533
3684
|
}
|
|
3534
3685
|
function canMatchEmptyAtStart(p) {
|
|
3535
3686
|
const d = p._def;
|
|
@@ -3567,7 +3718,7 @@ function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
|
3567
3718
|
];
|
|
3568
3719
|
}
|
|
3569
3720
|
function emitChoice(def, ctx, pos) {
|
|
3570
|
-
const allExpected =
|
|
3721
|
+
const allExpected = deriveExpectedArr(def.parsers);
|
|
3571
3722
|
if (def.disjoint) {
|
|
3572
3723
|
const codeV = v(ctx, "_code");
|
|
3573
3724
|
const valV = v(ctx, "_chv");
|
|
@@ -3628,7 +3779,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
3628
3779
|
const wordV = v(ctx, "_gw");
|
|
3629
3780
|
const endV = v(ctx, "_ge");
|
|
3630
3781
|
const valV = v(ctx, "_gcv");
|
|
3631
|
-
const regexExpected = JSON.stringify(
|
|
3782
|
+
const regexExpected = JSON.stringify(deriveExpected(superParser));
|
|
3632
3783
|
const stmts = [
|
|
3633
3784
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
3634
3785
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
@@ -3709,11 +3860,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3709
3860
|
const atStart = failsAtStart(p);
|
|
3710
3861
|
const staticFx = armStaticExpected(ctx, p);
|
|
3711
3862
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3712
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3863
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3864
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3865
|
+
const deep = firstSetOf(p);
|
|
3866
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3867
|
+
})();
|
|
3713
3868
|
let gateCond = null;
|
|
3714
3869
|
if (gate) {
|
|
3715
3870
|
const gateIdx = ctx.mapFns.length;
|
|
3716
3871
|
ctx.mapFns.push(gate);
|
|
3872
|
+
ctx.mapFnSrcs.push(null);
|
|
3717
3873
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3718
3874
|
}
|
|
3719
3875
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3772,13 +3928,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3772
3928
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3773
3929
|
return emitFirstMatch(def, ctx, pos);
|
|
3774
3930
|
}
|
|
3931
|
+
function pushMapFn(ctx, fn, src) {
|
|
3932
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3933
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3934
|
+
if (hit !== -1) return hit;
|
|
3935
|
+
}
|
|
3936
|
+
const idx = ctx.mapFns.length;
|
|
3937
|
+
ctx.mapFns.push(fn);
|
|
3938
|
+
ctx.mapFnSrcs.push(src);
|
|
3939
|
+
return idx;
|
|
3940
|
+
}
|
|
3775
3941
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3776
3942
|
const def = p._def;
|
|
3777
3943
|
if (def.tag === "transform") {
|
|
3778
3944
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3779
|
-
const fnIdx = ctx.
|
|
3780
|
-
ctx.mapFns.push(def.fn);
|
|
3781
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3945
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3782
3946
|
const vv = v(ctx);
|
|
3783
3947
|
return {
|
|
3784
3948
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3961,7 +4125,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3961
4125
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3962
4126
|
];
|
|
3963
4127
|
ctx.indent++;
|
|
3964
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4128
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3965
4129
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3966
4130
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3967
4131
|
if (def.skip.length > 0) {
|
|
@@ -4007,6 +4171,116 @@ function emitNot(def, ctx, pos) {
|
|
|
4007
4171
|
endVar: pos
|
|
4008
4172
|
};
|
|
4009
4173
|
}
|
|
4174
|
+
function escapeTokenLiteral(value) {
|
|
4175
|
+
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
4176
|
+
}
|
|
4177
|
+
function regexMatchesEmpty(source, flags) {
|
|
4178
|
+
try {
|
|
4179
|
+
return new RegExp(`^(?:${source})$`, flags).test("");
|
|
4180
|
+
} catch {
|
|
4181
|
+
return true;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
function tokenTerminalSource(p) {
|
|
4185
|
+
const def = p._def;
|
|
4186
|
+
switch (def.tag) {
|
|
4187
|
+
case "literal":
|
|
4188
|
+
if (def.value.length === 0) return null;
|
|
4189
|
+
return {
|
|
4190
|
+
source: escapeTokenLiteral(def.value),
|
|
4191
|
+
caseInsensitive: def.caseInsensitive,
|
|
4192
|
+
rawCaseSensitive: def.caseInsensitive ? "" : def.value
|
|
4193
|
+
};
|
|
4194
|
+
case "regex": {
|
|
4195
|
+
if (def.flags.replace(/i/g, "") !== "") return null;
|
|
4196
|
+
if (regexMatchesEmpty(def.source, def.flags)) return null;
|
|
4197
|
+
return {
|
|
4198
|
+
source: `(?:${def.source})`,
|
|
4199
|
+
caseInsensitive: def.flags.includes("i"),
|
|
4200
|
+
rawCaseSensitive: def.flags.includes("i") ? "" : def.source
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
default:
|
|
4204
|
+
return null;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
function tokenSequenceSource(p) {
|
|
4208
|
+
const def = p._def;
|
|
4209
|
+
const parts = def.tag === "sequence" ? def.parsers.map(tokenTerminalSource) : [tokenTerminalSource(p)];
|
|
4210
|
+
if (parts.some((part) => part === null)) return null;
|
|
4211
|
+
const typed = parts;
|
|
4212
|
+
const flags = typed.some((part) => part.caseInsensitive) ? "i" : "";
|
|
4213
|
+
if (flags.includes("i") && typed.some((part) => /[A-Za-z]/.test(part.rawCaseSensitive))) return null;
|
|
4214
|
+
return {
|
|
4215
|
+
source: typed.map((part) => part.source).join(""),
|
|
4216
|
+
flags,
|
|
4217
|
+
hasCaseSensitiveLetters: typed.some((part) => /[A-Za-z]/.test(part.rawCaseSensitive))
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
function combineTokenRegexSources(parts) {
|
|
4221
|
+
const flags = parts.some((part) => part.flags.includes("i")) ? "i" : "";
|
|
4222
|
+
if (flags.includes("i") && parts.some((part) => part.flags === "" && part.hasCaseSensitiveLetters)) return null;
|
|
4223
|
+
return { source: parts.map((part) => part.source).join(""), flags };
|
|
4224
|
+
}
|
|
4225
|
+
function tokenNullableRegex(def) {
|
|
4226
|
+
const inner = def.parser._def;
|
|
4227
|
+
if (inner.tag === "many") {
|
|
4228
|
+
const seq = tokenSequenceSource(inner.parser);
|
|
4229
|
+
if (!seq) return null;
|
|
4230
|
+
return { tag: "regex", source: `(?:${seq.source})*`, flags: seq.flags };
|
|
4231
|
+
}
|
|
4232
|
+
if (inner.tag === "optional") {
|
|
4233
|
+
const seq = tokenSequenceSource(inner.parser);
|
|
4234
|
+
if (!seq) return null;
|
|
4235
|
+
return { tag: "regex", source: `(?:${seq.source})?`, flags: seq.flags };
|
|
4236
|
+
}
|
|
4237
|
+
if (inner.tag === "sepBy") {
|
|
4238
|
+
const item = tokenSequenceSource(inner.parser);
|
|
4239
|
+
const sep = tokenSequenceSource(inner.separator);
|
|
4240
|
+
if (!item || !sep) return null;
|
|
4241
|
+
const combined = combineTokenRegexSources([item, sep]);
|
|
4242
|
+
if (!combined) return null;
|
|
4243
|
+
return { tag: "regex", source: `(?:${item.source}(?:${sep.source}${item.source})*)?`, flags: combined.flags };
|
|
4244
|
+
}
|
|
4245
|
+
return null;
|
|
4246
|
+
}
|
|
4247
|
+
function emitToken(def, ctx, pos) {
|
|
4248
|
+
const lowered = tokenNullableRegex(def);
|
|
4249
|
+
if (lowered) return emitRegex(lowered, ctx, pos);
|
|
4250
|
+
const savedTrivia = ctx.activeTrivia;
|
|
4251
|
+
const savedKindLabels = ctx.triviaKindLabels;
|
|
4252
|
+
ctx.activeTrivia = void 0;
|
|
4253
|
+
ctx.triviaKindLabels = void 0;
|
|
4254
|
+
const inner = emitFallible(def.parser, ctx, pos);
|
|
4255
|
+
ctx.activeTrivia = savedTrivia;
|
|
4256
|
+
ctx.triviaKindLabels = savedKindLabels;
|
|
4257
|
+
const i = ind(ctx);
|
|
4258
|
+
const sc = v(ctx, "_tokCh");
|
|
4259
|
+
const sl = v(ctx, "_tokLv");
|
|
4260
|
+
const sr = v(ctx, "_tokRaw");
|
|
4261
|
+
const sv = v(ctx, "_tokTr");
|
|
4262
|
+
const sk = v(ctx, "_tokKinds");
|
|
4263
|
+
const stl = v(ctx, "_tokTl");
|
|
4264
|
+
const sol = v(ctx, "_tokLog");
|
|
4265
|
+
const sb = v(ctx, "_tokBuf");
|
|
4266
|
+
const valV = v(ctx, "_tok");
|
|
4267
|
+
return {
|
|
4268
|
+
stmts: [
|
|
4269
|
+
`${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`,
|
|
4270
|
+
`${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`,
|
|
4271
|
+
`${i}try {`,
|
|
4272
|
+
...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
|
|
4273
|
+
`${i}} finally {`,
|
|
4274
|
+
`${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}`,
|
|
4275
|
+
`${i}}`,
|
|
4276
|
+
...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
|
|
4277
|
+
`${i}const ${valV} = input.slice(${pos}, ${inner.endVar})`,
|
|
4278
|
+
...emitLeafCapture(ctx, valV, pos, inner.endVar)
|
|
4279
|
+
],
|
|
4280
|
+
valueVar: valV,
|
|
4281
|
+
endVar: inner.endVar
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4010
4284
|
function emitNode(def, ctx, pos) {
|
|
4011
4285
|
const structural = def.build === void 0;
|
|
4012
4286
|
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
@@ -4126,7 +4400,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
4126
4400
|
`${ind0} if (${sentOk}) break`,
|
|
4127
4401
|
`${ind0} ${scanV}++`,
|
|
4128
4402
|
`${ind0} }`,
|
|
4129
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(
|
|
4403
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
|
|
4130
4404
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
4131
4405
|
`${ind0} ${valVar} = ${errV}`,
|
|
4132
4406
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -4152,6 +4426,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4152
4426
|
return { stmts, valueVar: valVar, endVar };
|
|
4153
4427
|
}
|
|
4154
4428
|
function emit(p, ctx, pos) {
|
|
4429
|
+
const usage = ctx.lazyUsage;
|
|
4430
|
+
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
|
|
4431
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4432
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4433
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4434
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4435
|
+
const existing = ctx.namedParsers.get(p);
|
|
4436
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4437
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4438
|
+
ctx.namedParsers.set(p, fnName);
|
|
4439
|
+
const savedIndent = ctx.indent;
|
|
4440
|
+
const savedFailLabel = ctx.failLabel;
|
|
4441
|
+
const savedRecord = ctx.recordFail;
|
|
4442
|
+
ctx.indent = 1;
|
|
4443
|
+
ctx.failLabel = "_pfail";
|
|
4444
|
+
ctx.recordFail = true;
|
|
4445
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4446
|
+
ctx.indent = savedIndent;
|
|
4447
|
+
ctx.failLabel = savedFailLabel;
|
|
4448
|
+
ctx.recordFail = savedRecord;
|
|
4449
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4450
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4451
|
+
}
|
|
4452
|
+
return emitDispatch(p, ctx, pos);
|
|
4453
|
+
}
|
|
4454
|
+
function emitDispatch(p, ctx, pos) {
|
|
4155
4455
|
const def = p._def;
|
|
4156
4456
|
switch (def.tag) {
|
|
4157
4457
|
case "literal":
|
|
@@ -4197,9 +4497,7 @@ function emit(p, ctx, pos) {
|
|
|
4197
4497
|
};
|
|
4198
4498
|
}
|
|
4199
4499
|
}
|
|
4200
|
-
const fnIdx = ctx.
|
|
4201
|
-
ctx.mapFns.push(def.fn);
|
|
4202
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4500
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4203
4501
|
const mv = v(ctx, "_mapped");
|
|
4204
4502
|
return {
|
|
4205
4503
|
stmts: [
|
|
@@ -4232,6 +4530,8 @@ function emit(p, ctx, pos) {
|
|
|
4232
4530
|
return emitLazy(p, def, ctx, pos);
|
|
4233
4531
|
case "trivia":
|
|
4234
4532
|
return emit(def.parser, ctx, pos);
|
|
4533
|
+
case "token":
|
|
4534
|
+
return emitToken(def, ctx, pos);
|
|
4235
4535
|
case "label":
|
|
4236
4536
|
return emit(def.parser, ctx, pos);
|
|
4237
4537
|
case "grammar": {
|
|
@@ -4264,6 +4564,7 @@ function emit(p, ctx, pos) {
|
|
|
4264
4564
|
case "guard": {
|
|
4265
4565
|
const fnIdx = ctx.mapFns.length;
|
|
4266
4566
|
ctx.mapFns.push(def.predicate);
|
|
4567
|
+
ctx.mapFnSrcs.push(null);
|
|
4267
4568
|
const vv = v(ctx);
|
|
4268
4569
|
return {
|
|
4269
4570
|
stmts: [
|
|
@@ -4278,6 +4579,7 @@ function emit(p, ctx, pos) {
|
|
|
4278
4579
|
const evIdx = ctx.mapFns.length;
|
|
4279
4580
|
const extra = def.extra;
|
|
4280
4581
|
ctx.mapFns.push((() => extra));
|
|
4582
|
+
ctx.mapFnSrcs.push(null);
|
|
4281
4583
|
const innerParser = def.parser;
|
|
4282
4584
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4283
4585
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4331,6 +4633,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4331
4633
|
}
|
|
4332
4634
|
case "grammar":
|
|
4333
4635
|
case "trivia":
|
|
4636
|
+
case "token":
|
|
4334
4637
|
case "label":
|
|
4335
4638
|
case "optional":
|
|
4336
4639
|
case "many":
|
|
@@ -4366,6 +4669,7 @@ function childrenOf(def) {
|
|
|
4366
4669
|
case "optional":
|
|
4367
4670
|
case "transform":
|
|
4368
4671
|
case "trivia":
|
|
4672
|
+
case "token":
|
|
4369
4673
|
case "label":
|
|
4370
4674
|
case "grammar":
|
|
4371
4675
|
case "not":
|
|
@@ -4397,9 +4701,58 @@ function childrenOf(def) {
|
|
|
4397
4701
|
return [];
|
|
4398
4702
|
}
|
|
4399
4703
|
}
|
|
4704
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4705
|
+
"sequence",
|
|
4706
|
+
"choice",
|
|
4707
|
+
"many",
|
|
4708
|
+
"oneOrMore",
|
|
4709
|
+
"optional",
|
|
4710
|
+
"sepBy",
|
|
4711
|
+
"transform",
|
|
4712
|
+
"node",
|
|
4713
|
+
"not"
|
|
4714
|
+
]);
|
|
4715
|
+
function isHoistableTag(tag) {
|
|
4716
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4717
|
+
}
|
|
4718
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4400
4719
|
function analyzeLazyUsage(root) {
|
|
4401
4720
|
return analyzeLazyUsageMulti([root]);
|
|
4402
4721
|
}
|
|
4722
|
+
function subtreeSizes(roots) {
|
|
4723
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4724
|
+
const sz = (p) => {
|
|
4725
|
+
const cached = sizes.get(p);
|
|
4726
|
+
if (cached !== void 0) return cached;
|
|
4727
|
+
const def = p._def;
|
|
4728
|
+
let s = 1;
|
|
4729
|
+
if (def.tag !== "lazy") {
|
|
4730
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4731
|
+
}
|
|
4732
|
+
sizes.set(p, s);
|
|
4733
|
+
return s;
|
|
4734
|
+
};
|
|
4735
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4736
|
+
const visit = (p) => {
|
|
4737
|
+
const def = p._def;
|
|
4738
|
+
if (def.tag === "lazy") {
|
|
4739
|
+
if (visitedLazy.has(p)) return;
|
|
4740
|
+
visitedLazy.add(p);
|
|
4741
|
+
let resolved;
|
|
4742
|
+
try {
|
|
4743
|
+
resolved = def.thunk();
|
|
4744
|
+
} catch {
|
|
4745
|
+
return;
|
|
4746
|
+
}
|
|
4747
|
+
visit(resolved);
|
|
4748
|
+
return;
|
|
4749
|
+
}
|
|
4750
|
+
sz(p);
|
|
4751
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4752
|
+
};
|
|
4753
|
+
for (const root of roots) visit(root);
|
|
4754
|
+
return sizes;
|
|
4755
|
+
}
|
|
4403
4756
|
function analyzeLazyUsageMulti(roots) {
|
|
4404
4757
|
const counts = /* @__PURE__ */ new Map();
|
|
4405
4758
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4426,10 +4779,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4426
4779
|
active.delete(p);
|
|
4427
4780
|
return;
|
|
4428
4781
|
}
|
|
4782
|
+
if (isHoistableTag(def.tag)) {
|
|
4783
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4784
|
+
}
|
|
4429
4785
|
for (const child of childrenOf(def)) walk2(child);
|
|
4430
4786
|
}
|
|
4431
|
-
|
|
4432
|
-
|
|
4787
|
+
const rootsArr = [...roots];
|
|
4788
|
+
for (const root of rootsArr) walk2(root);
|
|
4789
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4433
4790
|
}
|
|
4434
4791
|
function ruleDependencies(ruleMap) {
|
|
4435
4792
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4828,64 +5185,6 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
|
4828
5185
|
].join("\n");
|
|
4829
5186
|
}
|
|
4830
5187
|
|
|
4831
|
-
// src/compiler/linker.ts
|
|
4832
|
-
function fusedBody(pieces) {
|
|
4833
|
-
const winner = /* @__PURE__ */ new Map();
|
|
4834
|
-
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4835
|
-
for (const [k, p] of winner) {
|
|
4836
|
-
for (const d of p.deps.get(k) ?? []) {
|
|
4837
|
-
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
const contributing = new Set(winner.values());
|
|
4841
|
-
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4842
|
-
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4843
|
-
const lines = [
|
|
4844
|
-
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4845
|
-
"const _pfFail = {}",
|
|
4846
|
-
"let _pfEnd",
|
|
4847
|
-
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4848
|
-
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4849
|
-
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4850
|
-
...[...contributing].flatMap((p) => p.prelude),
|
|
4851
|
-
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4852
|
-
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4853
|
-
];
|
|
4854
|
-
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4855
|
-
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4856
|
-
const finalFS = /* @__PURE__ */ new Map();
|
|
4857
|
-
for (const [k, p] of winner) {
|
|
4858
|
-
const fs2 = p.firstSets?.get(k);
|
|
4859
|
-
if (fs2) finalFS.set(k, fs2);
|
|
4860
|
-
}
|
|
4861
|
-
const body = rawBody.replace(
|
|
4862
|
-
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4863
|
-
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4864
|
-
// matches every well-formed placeholder.
|
|
4865
|
-
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4866
|
-
(_m, name, codevar) => {
|
|
4867
|
-
const fs2 = finalFS.get(name);
|
|
4868
|
-
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4869
|
-
return `(${firstSetCond(codevar, fs2)})`;
|
|
4870
|
-
}
|
|
4871
|
-
);
|
|
4872
|
-
const env = {};
|
|
4873
|
-
for (const p of contributing) {
|
|
4874
|
-
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4875
|
-
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4876
|
-
}
|
|
4877
|
-
return { body, env };
|
|
4878
|
-
}
|
|
4879
|
-
function emitFusedSource(pieces) {
|
|
4880
|
-
const { body, env } = fusedBody(pieces);
|
|
4881
|
-
if (Object.keys(env).length > 0) {
|
|
4882
|
-
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4883
|
-
}
|
|
4884
|
-
return `/* @__PURE__ */ (() => {
|
|
4885
|
-
${body}
|
|
4886
|
-
})()`;
|
|
4887
|
-
}
|
|
4888
|
-
|
|
4889
5188
|
// src/combinators/scanTo.ts
|
|
4890
5189
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4891
5190
|
const meta = {
|
|
@@ -4954,7 +5253,7 @@ function balanced(open, close, options = {}) {
|
|
|
4954
5253
|
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
4955
5254
|
const inner = many(choice(self, ...skips, content));
|
|
4956
5255
|
const combi = transform(
|
|
4957
|
-
sequence(literal(open), inner, expect(literal(close)
|
|
5256
|
+
sequence(literal(open), inner, expect(literal(close))),
|
|
4958
5257
|
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
4959
5258
|
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
4960
5259
|
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
@@ -4980,6 +5279,359 @@ function escapeClassChar(ch) {
|
|
|
4980
5279
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4981
5280
|
}
|
|
4982
5281
|
|
|
5282
|
+
// src/compiler/ir-serialize.ts
|
|
5283
|
+
function childrenOf2(def) {
|
|
5284
|
+
switch (def.tag) {
|
|
5285
|
+
case "sequence":
|
|
5286
|
+
case "choice":
|
|
5287
|
+
return def.parsers;
|
|
5288
|
+
case "many":
|
|
5289
|
+
case "oneOrMore":
|
|
5290
|
+
case "optional":
|
|
5291
|
+
case "transform":
|
|
5292
|
+
case "trivia":
|
|
5293
|
+
case "token":
|
|
5294
|
+
case "label":
|
|
5295
|
+
case "not":
|
|
5296
|
+
case "node":
|
|
5297
|
+
case "expect":
|
|
5298
|
+
return [def.parser];
|
|
5299
|
+
case "grammar":
|
|
5300
|
+
return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
|
|
5301
|
+
case "sepBy":
|
|
5302
|
+
return [def.parser, def.separator];
|
|
5303
|
+
case "skip":
|
|
5304
|
+
return [def.main, def.skipped];
|
|
5305
|
+
case "scanTo":
|
|
5306
|
+
return [def.sentinel, ...def.skip];
|
|
5307
|
+
case "lazy":
|
|
5308
|
+
case "literal":
|
|
5309
|
+
case "regex":
|
|
5310
|
+
case "keywords":
|
|
5311
|
+
case "guard":
|
|
5312
|
+
case "withCtx":
|
|
5313
|
+
case "recover":
|
|
5314
|
+
case "unknown":
|
|
5315
|
+
return [];
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
var ruleNameOf = (c) => c._ruleName;
|
|
5319
|
+
function lazyTarget(c) {
|
|
5320
|
+
if (c._def.tag !== "lazy") return null;
|
|
5321
|
+
try {
|
|
5322
|
+
return c._def.thunk();
|
|
5323
|
+
} catch {
|
|
5324
|
+
return null;
|
|
5325
|
+
}
|
|
5326
|
+
}
|
|
5327
|
+
var Unserializable = class extends Error {
|
|
5328
|
+
};
|
|
5329
|
+
function serializeRuleMap(ruleMap) {
|
|
5330
|
+
try {
|
|
5331
|
+
return new Serializer(ruleMap).run();
|
|
5332
|
+
} catch (e) {
|
|
5333
|
+
if (e instanceof Unserializable) {
|
|
5334
|
+
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
5335
|
+
return null;
|
|
5336
|
+
}
|
|
5337
|
+
throw e;
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
function evalRuleMapIR(ir) {
|
|
5341
|
+
const _tf = (child, src) => {
|
|
5342
|
+
let fn2;
|
|
5343
|
+
try {
|
|
5344
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5345
|
+
} catch {
|
|
5346
|
+
fn2 = () => {
|
|
5347
|
+
throw new Error("IR transform fn not materialized");
|
|
5348
|
+
};
|
|
5349
|
+
}
|
|
5350
|
+
const t = transform(child, fn2);
|
|
5351
|
+
t._def.fnSrc = src;
|
|
5352
|
+
return t;
|
|
5353
|
+
};
|
|
5354
|
+
const _nd = (type, child, src, opts) => {
|
|
5355
|
+
const n = node(type, child, void 0, opts);
|
|
5356
|
+
n._def.buildSrc = src;
|
|
5357
|
+
return n;
|
|
5358
|
+
};
|
|
5359
|
+
const fn = new Function(
|
|
5360
|
+
"rules",
|
|
5361
|
+
"ref",
|
|
5362
|
+
"regex",
|
|
5363
|
+
"literal",
|
|
5364
|
+
"keywords",
|
|
5365
|
+
"sequence",
|
|
5366
|
+
"choice",
|
|
5367
|
+
"many",
|
|
5368
|
+
"oneOrMore",
|
|
5369
|
+
"optional",
|
|
5370
|
+
"sepBy",
|
|
5371
|
+
"not",
|
|
5372
|
+
"node",
|
|
5373
|
+
"parser",
|
|
5374
|
+
"scanTo",
|
|
5375
|
+
"token",
|
|
5376
|
+
"transform",
|
|
5377
|
+
"skip",
|
|
5378
|
+
"trivia",
|
|
5379
|
+
"label",
|
|
5380
|
+
"expect",
|
|
5381
|
+
"_tf",
|
|
5382
|
+
"_nd",
|
|
5383
|
+
`return (${ir})`
|
|
5384
|
+
);
|
|
5385
|
+
const map = fn(
|
|
5386
|
+
rules,
|
|
5387
|
+
ref,
|
|
5388
|
+
regex,
|
|
5389
|
+
literal,
|
|
5390
|
+
keywords,
|
|
5391
|
+
sequence,
|
|
5392
|
+
choice,
|
|
5393
|
+
many,
|
|
5394
|
+
oneOrMore,
|
|
5395
|
+
optional,
|
|
5396
|
+
sepBy,
|
|
5397
|
+
not,
|
|
5398
|
+
node,
|
|
5399
|
+
parser,
|
|
5400
|
+
scanTo,
|
|
5401
|
+
token,
|
|
5402
|
+
transform,
|
|
5403
|
+
skip,
|
|
5404
|
+
trivia,
|
|
5405
|
+
label,
|
|
5406
|
+
expect,
|
|
5407
|
+
_tf,
|
|
5408
|
+
_nd
|
|
5409
|
+
);
|
|
5410
|
+
return Object.entries(map);
|
|
5411
|
+
}
|
|
5412
|
+
var Serializer = class {
|
|
5413
|
+
counts = /* @__PURE__ */ new Map();
|
|
5414
|
+
selfRef = /* @__PURE__ */ new Set();
|
|
5415
|
+
// combinators reached by an unnamed lazy pointing back into their own subtree
|
|
5416
|
+
constName = /* @__PURE__ */ new Map();
|
|
5417
|
+
emitted = /* @__PURE__ */ new Set();
|
|
5418
|
+
decls = [];
|
|
5419
|
+
ruleValues = /* @__PURE__ */ new Set();
|
|
5420
|
+
ruleMap;
|
|
5421
|
+
constructor(ruleMap) {
|
|
5422
|
+
this.ruleMap = ruleMap;
|
|
5423
|
+
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
5424
|
+
}
|
|
5425
|
+
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
5426
|
+
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
5427
|
+
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
5428
|
+
body(c) {
|
|
5429
|
+
return c._def.tag === "lazy" && ruleNameOf(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
5430
|
+
}
|
|
5431
|
+
run() {
|
|
5432
|
+
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
5433
|
+
let n = 0;
|
|
5434
|
+
for (const [c, count] of this.counts) {
|
|
5435
|
+
if (this.ruleValues.has(c)) continue;
|
|
5436
|
+
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
5437
|
+
}
|
|
5438
|
+
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
5439
|
+
if (this.decls.length === 0) return `rules((g) => ({
|
|
5440
|
+
${body}
|
|
5441
|
+
}))`;
|
|
5442
|
+
return `rules((g) => {
|
|
5443
|
+
${this.decls.map((d) => " " + d).join("\n")}
|
|
5444
|
+
return ({
|
|
5445
|
+
${body}
|
|
5446
|
+
})
|
|
5447
|
+
})`;
|
|
5448
|
+
}
|
|
5449
|
+
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
5450
|
+
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
5451
|
+
analyze(c, active) {
|
|
5452
|
+
const def = c._def;
|
|
5453
|
+
if (def.tag === "lazy") {
|
|
5454
|
+
if (ruleNameOf(c) !== void 0) return;
|
|
5455
|
+
const target = lazyTarget(c);
|
|
5456
|
+
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
5457
|
+
if (active.has(target)) {
|
|
5458
|
+
this.selfRef.add(target);
|
|
5459
|
+
return;
|
|
5460
|
+
}
|
|
5461
|
+
this.analyze(target, active);
|
|
5462
|
+
return;
|
|
5463
|
+
}
|
|
5464
|
+
if (def.tag === "guard" || def.tag === "withCtx" || def.tag === "recover" || def.tag === "unknown") {
|
|
5465
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5466
|
+
}
|
|
5467
|
+
this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
|
|
5468
|
+
if ((this.counts.get(c) ?? 0) > 1) return;
|
|
5469
|
+
active.add(c);
|
|
5470
|
+
for (const child of childrenOf2(def)) this.analyze(child, active);
|
|
5471
|
+
active.delete(c);
|
|
5472
|
+
}
|
|
5473
|
+
/** Reference `c`: a const var if it has one (emitting its decl first), else inline.
|
|
5474
|
+
* `selfOf` is the const currently being wrapped in a ref()/define closure — an
|
|
5475
|
+
* unnamed lazy back into it emits the local ref var instead of recursing. */
|
|
5476
|
+
ref(c, selfOf) {
|
|
5477
|
+
const cn = this.constName.get(c);
|
|
5478
|
+
if (cn) {
|
|
5479
|
+
this.emitDecl(c);
|
|
5480
|
+
return cn;
|
|
5481
|
+
}
|
|
5482
|
+
return this.expr(c, selfOf);
|
|
5483
|
+
}
|
|
5484
|
+
emitDecl(c) {
|
|
5485
|
+
const name = this.constName.get(c);
|
|
5486
|
+
if (this.emitted.has(name)) return;
|
|
5487
|
+
this.emitted.add(name);
|
|
5488
|
+
this.decls.push(`const ${name} = ${this.wrap(c)}`);
|
|
5489
|
+
}
|
|
5490
|
+
selfN = 0;
|
|
5491
|
+
/** Emit `c`'s own constructor call, wrapping it in a `ref()`+`.define()` closure
|
|
5492
|
+
* if it is self-referential (an unnamed lazy in its subtree points back to it) —
|
|
5493
|
+
* the shape `balanced()` and other recursive library combinators build. */
|
|
5494
|
+
wrap(c) {
|
|
5495
|
+
if (!this.selfRef.has(c)) return this.expr(c, null);
|
|
5496
|
+
const rv = `_rr${this.selfN++}`;
|
|
5497
|
+
const bodyExpr = this.expr(c, { comb: c, var: rv });
|
|
5498
|
+
return `(() => { const ${rv} = ref(); const _b = ${bodyExpr}; ${rv}.define(_b); return _b })()`;
|
|
5499
|
+
}
|
|
5500
|
+
/** Emit the constructor-call source for `c` (never a const shortcut for `c` itself). */
|
|
5501
|
+
expr(c, selfOf) {
|
|
5502
|
+
const def = c._def;
|
|
5503
|
+
const kid = (x) => this.ref(x, selfOf);
|
|
5504
|
+
switch (def.tag) {
|
|
5505
|
+
case "lazy": {
|
|
5506
|
+
if (ruleNameOf(c) !== void 0) return `g[${JSON.stringify(ruleNameOf(c))}]`;
|
|
5507
|
+
const target = lazyTarget(c);
|
|
5508
|
+
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
5509
|
+
return this.ref(target, selfOf);
|
|
5510
|
+
}
|
|
5511
|
+
case "literal":
|
|
5512
|
+
return `literal(${JSON.stringify(def.value)}${def.caseInsensitive ? ", { caseInsensitive: true }" : ""})`;
|
|
5513
|
+
case "regex":
|
|
5514
|
+
return `regex(${JSON.stringify(def.source)}, ${JSON.stringify(def.flags)})`;
|
|
5515
|
+
case "keywords":
|
|
5516
|
+
return `keywords(${JSON.stringify(def.words)}, { caseInsensitive: ${def.caseInsensitive}${def.boundary !== void 0 ? `, boundary: ${JSON.stringify(def.boundary)}` : ""} })`;
|
|
5517
|
+
case "sequence":
|
|
5518
|
+
return `sequence(${def.parsers.map(kid).join(", ")})`;
|
|
5519
|
+
case "choice": {
|
|
5520
|
+
if (def.gates.some((g) => g !== null)) throw new Unserializable("choice with a gate() has no source");
|
|
5521
|
+
return `choice(${def.parsers.map(kid).join(", ")})`;
|
|
5522
|
+
}
|
|
5523
|
+
case "many":
|
|
5524
|
+
return `many(${kid(def.parser)})`;
|
|
5525
|
+
case "oneOrMore":
|
|
5526
|
+
return `oneOrMore(${kid(def.parser)})`;
|
|
5527
|
+
case "optional":
|
|
5528
|
+
return `optional(${kid(def.parser)})`;
|
|
5529
|
+
case "sepBy":
|
|
5530
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
5531
|
+
case "not":
|
|
5532
|
+
return `not(${kid(def.parser)})`;
|
|
5533
|
+
case "trivia":
|
|
5534
|
+
return `trivia(${kid(def.parser)})`;
|
|
5535
|
+
case "token":
|
|
5536
|
+
return `token(${kid(def.parser)})`;
|
|
5537
|
+
case "label":
|
|
5538
|
+
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
5539
|
+
case "expect":
|
|
5540
|
+
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
5541
|
+
case "skip":
|
|
5542
|
+
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
5543
|
+
case "scanTo":
|
|
5544
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
5545
|
+
case "transform": {
|
|
5546
|
+
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
5547
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
5548
|
+
}
|
|
5549
|
+
case "node": {
|
|
5550
|
+
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
5551
|
+
const opts = def.collapse ? `, { collapse: true }` : "";
|
|
5552
|
+
if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
|
|
5553
|
+
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
5554
|
+
}
|
|
5555
|
+
case "grammar": {
|
|
5556
|
+
const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
|
|
5557
|
+
return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
|
|
5558
|
+
}
|
|
5559
|
+
case "guard":
|
|
5560
|
+
case "withCtx":
|
|
5561
|
+
case "recover":
|
|
5562
|
+
case "unknown":
|
|
5563
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
};
|
|
5567
|
+
|
|
5568
|
+
// src/compiler/linker.ts
|
|
5569
|
+
function fusedBody(pieces) {
|
|
5570
|
+
const winner = /* @__PURE__ */ new Map();
|
|
5571
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
5572
|
+
for (const [k, p] of winner) {
|
|
5573
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
5574
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
const contributing = new Set(winner.values());
|
|
5578
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
5579
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
5580
|
+
const lines = [
|
|
5581
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
5582
|
+
"const _pfFail = {}",
|
|
5583
|
+
"let _pfEnd",
|
|
5584
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
5585
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
5586
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
5587
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
5588
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
5589
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
5590
|
+
];
|
|
5591
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
5592
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
5593
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
5594
|
+
for (const [k, p] of winner) {
|
|
5595
|
+
const fs2 = p.firstSets?.get(k);
|
|
5596
|
+
if (fs2) finalFS.set(k, fs2);
|
|
5597
|
+
}
|
|
5598
|
+
const body = rawBody.replace(
|
|
5599
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
5600
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
5601
|
+
// matches every well-formed placeholder.
|
|
5602
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
5603
|
+
(_m, name, codevar) => {
|
|
5604
|
+
const fs2 = finalFS.get(name);
|
|
5605
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
5606
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
5607
|
+
}
|
|
5608
|
+
);
|
|
5609
|
+
const env = {};
|
|
5610
|
+
for (const p of contributing) {
|
|
5611
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
5612
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
5613
|
+
}
|
|
5614
|
+
return { body, env };
|
|
5615
|
+
}
|
|
5616
|
+
function emitFusedSource(pieces) {
|
|
5617
|
+
const { body, env } = fusedBody(pieces);
|
|
5618
|
+
if (Object.keys(env).length > 0) {
|
|
5619
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
5620
|
+
}
|
|
5621
|
+
return `/* @__PURE__ */ (() => {
|
|
5622
|
+
${body}
|
|
5623
|
+
})()`;
|
|
5624
|
+
}
|
|
5625
|
+
function isIRPiece(p) {
|
|
5626
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5627
|
+
}
|
|
5628
|
+
function materializePiece(p) {
|
|
5629
|
+
if (!isIRPiece(p)) return p;
|
|
5630
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5631
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5632
|
+
return pieces;
|
|
5633
|
+
}
|
|
5634
|
+
|
|
4983
5635
|
// src/cst/offset-model.ts
|
|
4984
5636
|
var { isFinite } = Number;
|
|
4985
5637
|
|
|
@@ -5004,6 +5656,7 @@ var SUPPORTED = {
|
|
|
5004
5656
|
trivia: (...a) => trivia(a[0]),
|
|
5005
5657
|
label: (...a) => label(a[0], a[1]),
|
|
5006
5658
|
noTrivia: (...a) => noTrivia(a[0]),
|
|
5659
|
+
token: (...a) => token(a[0]),
|
|
5007
5660
|
expect: (...a) => expect(a[0], a[1])
|
|
5008
5661
|
};
|
|
5009
5662
|
function isScopeEntry(v2) {
|
|
@@ -5553,6 +6206,14 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5553
6206
|
const obj = callee?.object?.name;
|
|
5554
6207
|
const prop = callee?.property?.name;
|
|
5555
6208
|
const args = node2.arguments;
|
|
6209
|
+
if (obj === "Object" && prop === "assign" && args && args[1]?.type === "ObjectExpression") {
|
|
6210
|
+
for (const p of args[1].properties ?? []) {
|
|
6211
|
+
if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
|
|
6212
|
+
found = { start: p.value.start, end: p.value.end };
|
|
6213
|
+
return;
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
5556
6217
|
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5557
6218
|
const descriptor = args[2];
|
|
5558
6219
|
for (const p of descriptor?.properties ?? []) {
|
|
@@ -5573,6 +6234,20 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5573
6234
|
visit(root);
|
|
5574
6235
|
return found;
|
|
5575
6236
|
}
|
|
6237
|
+
function extractImportBindings(body) {
|
|
6238
|
+
const out = /* @__PURE__ */ new Map();
|
|
6239
|
+
for (const stmt of body) {
|
|
6240
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
6241
|
+
const s = stmt;
|
|
6242
|
+
for (const spec of s.specifiers) {
|
|
6243
|
+
if (spec.type === "ImportSpecifier") {
|
|
6244
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
6245
|
+
out.set(spec.local.name, { source: s.source.value, imported });
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
return out;
|
|
6250
|
+
}
|
|
5576
6251
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5577
6252
|
let result;
|
|
5578
6253
|
try {
|
|
@@ -5647,14 +6322,57 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5647
6322
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5648
6323
|
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5649
6324
|
const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
6325
|
+
const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
|
|
5650
6326
|
const serializePieces = (p) => {
|
|
5651
|
-
const mapLit = (m) => `new Map([${[...m].map(
|
|
5652
|
-
|
|
6327
|
+
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
6328
|
+
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
6329
|
+
).join(", ")}])`;
|
|
6330
|
+
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: [] }`;
|
|
5653
6331
|
};
|
|
5654
|
-
const
|
|
5655
|
-
const
|
|
6332
|
+
const isSpread = (it) => "__spreadLocal" in it;
|
|
6333
|
+
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
6334
|
+
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);
|
|
6335
|
+
const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
|
|
6336
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
|
|
5656
6337
|
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5657
6338
|
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
6339
|
+
const resolveModulePieces = (file, exportName, seen) => {
|
|
6340
|
+
const mod = parseModuleCached(file);
|
|
6341
|
+
if (!mod) return null;
|
|
6342
|
+
const localFor = exportLocalName(mod.body, exportName);
|
|
6343
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
6344
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
6345
|
+
if (!literalRange) return null;
|
|
6346
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
6347
|
+
const imports = extractImportBindings(mod.body);
|
|
6348
|
+
const stubNames = [];
|
|
6349
|
+
const stubVals = [];
|
|
6350
|
+
const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
|
|
6351
|
+
const done = /* @__PURE__ */ new Set();
|
|
6352
|
+
for (let m; m = spreadRe.exec(literal2); ) {
|
|
6353
|
+
const local = m[1];
|
|
6354
|
+
if (done.has(local)) continue;
|
|
6355
|
+
done.add(local);
|
|
6356
|
+
const b = imports.get(local);
|
|
6357
|
+
let subPieces = [];
|
|
6358
|
+
if (b) {
|
|
6359
|
+
let subFile = null;
|
|
6360
|
+
try {
|
|
6361
|
+
subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
|
|
6362
|
+
} catch {
|
|
6363
|
+
}
|
|
6364
|
+
if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
|
|
6365
|
+
}
|
|
6366
|
+
stubNames.push(local);
|
|
6367
|
+
stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
|
|
6368
|
+
}
|
|
6369
|
+
try {
|
|
6370
|
+
const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
|
|
6371
|
+
return list.map(materializePiece);
|
|
6372
|
+
} catch {
|
|
6373
|
+
return null;
|
|
6374
|
+
}
|
|
6375
|
+
};
|
|
5658
6376
|
const importedPieces = (localName) => {
|
|
5659
6377
|
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5660
6378
|
let result2 = null;
|
|
@@ -5665,41 +6383,34 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5665
6383
|
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5666
6384
|
} catch {
|
|
5667
6385
|
}
|
|
5668
|
-
|
|
5669
|
-
if (mod) {
|
|
5670
|
-
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5671
|
-
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5672
|
-
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5673
|
-
if (literalRange) {
|
|
5674
|
-
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5675
|
-
try {
|
|
5676
|
-
result2 = new Function(`return (${literal2})`)();
|
|
5677
|
-
} catch {
|
|
5678
|
-
result2 = null;
|
|
5679
|
-
}
|
|
5680
|
-
}
|
|
5681
|
-
}
|
|
6386
|
+
result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
|
|
5682
6387
|
}
|
|
5683
6388
|
importedPiecesCache.set(localName, result2);
|
|
5684
6389
|
return result2;
|
|
5685
6390
|
};
|
|
6391
|
+
const localArg = (rm, label2) => {
|
|
6392
|
+
const entries = [...rm];
|
|
6393
|
+
const ns = nsFor(label2);
|
|
6394
|
+
const p = compileLinkable(entries, ns);
|
|
6395
|
+
if (!p) return null;
|
|
6396
|
+
const ir = serializeRuleMap(entries);
|
|
6397
|
+
return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
|
|
6398
|
+
};
|
|
5686
6399
|
const argPieces = (arg, label2) => {
|
|
5687
6400
|
if (isRulesCall(arg)) {
|
|
5688
6401
|
const factory = arg.arguments[0];
|
|
5689
6402
|
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5690
|
-
|
|
5691
|
-
return p ? [p] : null;
|
|
6403
|
+
return rm ? localArg(rm, label2) : null;
|
|
5692
6404
|
}
|
|
5693
6405
|
if (arg.type === "Identifier") {
|
|
5694
6406
|
const name = arg.name;
|
|
5695
6407
|
const rm = localRuleMaps.get(name);
|
|
5696
|
-
if (rm)
|
|
5697
|
-
const p = compileLinkable([...rm], nsFor(label2));
|
|
5698
|
-
return p ? [p] : null;
|
|
5699
|
-
}
|
|
6408
|
+
if (rm) return localArg(rm, label2);
|
|
5700
6409
|
const composed = localComposedPieces.get(name);
|
|
5701
|
-
if (composed) return composed;
|
|
5702
|
-
|
|
6410
|
+
if (composed) return { pieces: composed };
|
|
6411
|
+
const pieces = importedPieces(name);
|
|
6412
|
+
if (!pieces) return null;
|
|
6413
|
+
return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
|
|
5703
6414
|
}
|
|
5704
6415
|
return null;
|
|
5705
6416
|
};
|
|
@@ -5712,16 +6423,20 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5712
6423
|
}
|
|
5713
6424
|
const elements = arr.elements;
|
|
5714
6425
|
const pieces = [];
|
|
6426
|
+
const carried = [];
|
|
5715
6427
|
for (let i = 0; i < elements.length; i++) {
|
|
5716
|
-
const
|
|
5717
|
-
if (!
|
|
6428
|
+
const r = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
6429
|
+
if (!r) {
|
|
5718
6430
|
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5719
6431
|
return null;
|
|
5720
6432
|
}
|
|
5721
|
-
pieces.push(...
|
|
6433
|
+
pieces.push(...r.pieces);
|
|
6434
|
+
if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
|
|
6435
|
+
else if (r.ir) carried.push(r.ir);
|
|
6436
|
+
else carried.push(...r.pieces);
|
|
5722
6437
|
}
|
|
5723
6438
|
try {
|
|
5724
|
-
return { replacement: emitFusedSource(pieces), pieces };
|
|
6439
|
+
return { replacement: emitFusedSource(pieces), pieces, carried };
|
|
5725
6440
|
} catch (e) {
|
|
5726
6441
|
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5727
6442
|
return null;
|
|
@@ -5808,9 +6523,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5808
6523
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5809
6524
|
let replacement = compiledRules.replacement;
|
|
5810
6525
|
if (exportPrefix) {
|
|
5811
|
-
const
|
|
6526
|
+
const ns = nsFor(varName);
|
|
6527
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], ns);
|
|
5812
6528
|
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5813
|
-
|
|
6529
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
6530
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
5814
6531
|
}
|
|
5815
6532
|
}
|
|
5816
6533
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
@@ -5820,7 +6537,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5820
6537
|
const fused = compileComposeCall(init);
|
|
5821
6538
|
if (!fused) continue;
|
|
5822
6539
|
localComposedPieces.set(varName, fused.pieces);
|
|
5823
|
-
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.
|
|
6540
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
5824
6541
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5825
6542
|
continue;
|
|
5826
6543
|
}
|