parseman 0.61.0 → 0.61.2
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/dist/analysis/backtrack.d.ts +50 -0
- package/dist/analysis/backtrack.d.ts.map +1 -0
- package/dist/analysis/diagnostics.d.ts +3 -0
- package/dist/analysis/diagnostics.d.ts.map +1 -1
- package/dist/analysis/diagnostics.js +1116 -123
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/analysis/rebuild.d.ts +17 -1
- package/dist/analysis/rebuild.d.ts.map +1 -1
- package/dist/cli/index.js +307 -34
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts +2 -0
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/index.js +31 -25
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.d.ts +3 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +53 -25
- package/dist/plugin/index.js.map +2 -2
- package/dist/table/backtrack-probe.d.ts +41 -0
- package/dist/table/backtrack-probe.d.ts.map +1 -0
- package/dist/table/child-slots.d.ts +10 -0
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/index.js +21 -15
- package/dist/table/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +5 -1
- package/src/analysis/backtrack.ts +249 -0
- package/src/analysis/diagnostics.ts +4 -0
- package/src/analysis/rebuild.ts +63 -11
- package/src/combinators/dispatch.ts +9 -0
- package/src/coverage.ts +2 -14
- package/src/plugin/index.ts +30 -13
- package/src/table/backtrack-probe.ts +326 -0
- package/src/table/child-slots.ts +30 -14
- package/src/version.ts +1 -1
|
@@ -53,9 +53,25 @@ export type RebuildResult = {
|
|
|
53
53
|
*/
|
|
54
54
|
unapplied: Combinator<unknown>[];
|
|
55
55
|
};
|
|
56
|
+
export type RebuildOptions = {
|
|
57
|
+
/**
|
|
58
|
+
* Also rebuild `dispatch`, a gated `choice`, `withCtx` and `routed` instead of
|
|
59
|
+
* freezing them. Each is reconstructed from its own def fields — the arms, gates,
|
|
60
|
+
* matchers and `extra` the grammar built it with. Off for `proposeFixes()`, whose
|
|
61
|
+
* reports name the frozen kinds; on for an instrumented rebuild, which must reach
|
|
62
|
+
* every terminal (`backtrack.ts`).
|
|
63
|
+
*/
|
|
64
|
+
readonly thaw?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Called with each node and what it was rebuilt to (itself, for a frozen or
|
|
67
|
+
* childless one), and its result is used in its place. Not called for a
|
|
68
|
+
* replacement or a `lazy` slot.
|
|
69
|
+
*/
|
|
70
|
+
readonly after?: (original: Combinator<unknown>, rebuilt: Combinator<unknown>) => Combinator<unknown>;
|
|
71
|
+
};
|
|
56
72
|
/**
|
|
57
73
|
* Rebuild `root`, replacing every combinator that is a key of `replacements` with its
|
|
58
74
|
* value. The replacement is spliced WITHOUT descending into the original target.
|
|
59
75
|
*/
|
|
60
|
-
export declare function rebuildCombinator(root: Combinator<unknown>, replacements: ReadonlyMap<Combinator<unknown>, Combinator<unknown
|
|
76
|
+
export declare function rebuildCombinator(root: Combinator<unknown>, replacements: ReadonlyMap<Combinator<unknown>, Combinator<unknown>>, options?: RebuildOptions): RebuildResult;
|
|
61
77
|
//# sourceMappingURL=rebuild.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rebuild.d.ts","sourceRoot":"","sources":["../../src/analysis/rebuild.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"rebuild.d.ts","sourceRoot":"","sources":["../../src/analysis/rebuild.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,aAAa,CAAA;AA4BxD,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;IACzB,wEAAwE;IACxE,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB;;;OAGG;IACH,SAAS,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAA;CACjC,CAAA;AA0CD,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,UAAU,CAAC,OAAO,CAAC,CAAA;CACtG,CAAA;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,EACnE,OAAO,GAAE,cAAmB,GAC3B,aAAa,CA8Lf"}
|
package/dist/cli/index.js
CHANGED
|
@@ -2552,6 +2552,206 @@ function asciiFoldKey(key) {
|
|
|
2552
2552
|
}
|
|
2553
2553
|
return out;
|
|
2554
2554
|
}
|
|
2555
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2556
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2557
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2558
|
+
switch (matcher.kind) {
|
|
2559
|
+
case "startsWith":
|
|
2560
|
+
return candidate.startsWith(expected);
|
|
2561
|
+
case "endsWith":
|
|
2562
|
+
return candidate.endsWith(expected);
|
|
2563
|
+
case "matches": {
|
|
2564
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2565
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
function isStringMatcher(value) {
|
|
2570
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2571
|
+
const kind = value.kind;
|
|
2572
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2573
|
+
}
|
|
2574
|
+
function fixedMatcher(kind, value) {
|
|
2575
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2576
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2577
|
+
return { kind, value };
|
|
2578
|
+
}
|
|
2579
|
+
function startsWith(prefix) {
|
|
2580
|
+
return fixedMatcher("startsWith", prefix);
|
|
2581
|
+
}
|
|
2582
|
+
function endsWith(suffix) {
|
|
2583
|
+
return fixedMatcher("endsWith", suffix);
|
|
2584
|
+
}
|
|
2585
|
+
function matches(pattern) {
|
|
2586
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2587
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2588
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2589
|
+
}
|
|
2590
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2591
|
+
}
|
|
2592
|
+
function dispatchMatcher(entry) {
|
|
2593
|
+
switch (entry.kind) {
|
|
2594
|
+
case "startsWith":
|
|
2595
|
+
return startsWith(entry.value);
|
|
2596
|
+
case "endsWith":
|
|
2597
|
+
return endsWith(entry.value);
|
|
2598
|
+
case "matches":
|
|
2599
|
+
return matches(new RegExp(entry.value, entry.flags));
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
function when(key, parser2, opts = {}) {
|
|
2603
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2604
|
+
if (isStringMatcher(key)) {
|
|
2605
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2606
|
+
}
|
|
2607
|
+
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2608
|
+
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2609
|
+
for (const item of keys) {
|
|
2610
|
+
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2611
|
+
}
|
|
2612
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2613
|
+
}
|
|
2614
|
+
function otherwise(parser2, opts = {}) {
|
|
2615
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2616
|
+
return opts.commit === false ? { kind: "otherwise", parser: parser2, usesRouted, commit: false } : { kind: "otherwise", parser: parser2, usesRouted };
|
|
2617
|
+
}
|
|
2618
|
+
function routed(fallback) {
|
|
2619
|
+
return {
|
|
2620
|
+
_tag: "routed",
|
|
2621
|
+
_meta: {
|
|
2622
|
+
// The routed token is whatever the selector matched, so the first set stays
|
|
2623
|
+
// `any` even with a fallback — `any` already subsumes the fallback's.
|
|
2624
|
+
firstSet: { kind: "any" },
|
|
2625
|
+
canMatchNewline: fallback?._meta.canMatchNewline ?? false,
|
|
2626
|
+
isTrivia: false
|
|
2627
|
+
},
|
|
2628
|
+
_def: fallback === void 0 ? { tag: "routed" } : { tag: "routed", fallback },
|
|
2629
|
+
parse(_input, pos, ctx) {
|
|
2630
|
+
const item = ctx._routed;
|
|
2631
|
+
if (item === void 0 || pos !== item.span.start) {
|
|
2632
|
+
if (fallback !== void 0) return fallback.parse(_input, pos, ctx);
|
|
2633
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2634
|
+
}
|
|
2635
|
+
if (cstCaptureActive(ctx)) {
|
|
2636
|
+
pushCstLeaf(ctx, new PlainLeaf2(item.value, item.span));
|
|
2637
|
+
}
|
|
2638
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2639
|
+
}
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
function dispatch(selector, ...arms) {
|
|
2643
|
+
if (parserUsesRouted(selector)) {
|
|
2644
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2645
|
+
}
|
|
2646
|
+
let fallback;
|
|
2647
|
+
let fallbackUsesRouted = false;
|
|
2648
|
+
let fallbackCommits = true;
|
|
2649
|
+
const matchers = [];
|
|
2650
|
+
const cases = [];
|
|
2651
|
+
const seen = [];
|
|
2652
|
+
for (let i = 0; i < arms.length; i++) {
|
|
2653
|
+
const arm = arms[i];
|
|
2654
|
+
if (arm.kind === "otherwise") {
|
|
2655
|
+
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2656
|
+
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2657
|
+
fallback = arm.parser;
|
|
2658
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
2659
|
+
fallbackCommits = arm.commit !== false;
|
|
2660
|
+
continue;
|
|
2661
|
+
}
|
|
2662
|
+
if (arm.kind === "whenMatcher") {
|
|
2663
|
+
const matcher = arm.matcher;
|
|
2664
|
+
matchers.push({
|
|
2665
|
+
kind: matcher.kind,
|
|
2666
|
+
value: matcher.value,
|
|
2667
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2668
|
+
parser: arm.parser,
|
|
2669
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2670
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2671
|
+
});
|
|
2672
|
+
continue;
|
|
2673
|
+
}
|
|
2674
|
+
for (const key of arm.keys) {
|
|
2675
|
+
const folded = asciiFoldKey(key);
|
|
2676
|
+
for (const prior of seen) {
|
|
2677
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2678
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2679
|
+
}
|
|
2680
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2681
|
+
}
|
|
2682
|
+
cases.push({
|
|
2683
|
+
keys: arm.keys,
|
|
2684
|
+
parser: arm.parser,
|
|
2685
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2686
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
const meta = {
|
|
2690
|
+
firstSet: selector._meta.firstSet,
|
|
2691
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2692
|
+
isTrivia: false
|
|
2693
|
+
};
|
|
2694
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2695
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2696
|
+
for (const entry of cases) {
|
|
2697
|
+
for (const key of entry.keys) {
|
|
2698
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2699
|
+
else byKey.set(key, entry);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
return {
|
|
2703
|
+
_tag: "dispatch",
|
|
2704
|
+
_meta: meta,
|
|
2705
|
+
_def: {
|
|
2706
|
+
tag: "dispatch",
|
|
2707
|
+
selector,
|
|
2708
|
+
cases,
|
|
2709
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2710
|
+
...fallback === void 0 ? {} : {
|
|
2711
|
+
otherwise: fallback,
|
|
2712
|
+
...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {},
|
|
2713
|
+
...fallbackCommits ? {} : { otherwiseCommit: false }
|
|
2714
|
+
}
|
|
2715
|
+
},
|
|
2716
|
+
parse(input, pos, ctx) {
|
|
2717
|
+
if (parserUsesRouted(selector)) {
|
|
2718
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2719
|
+
}
|
|
2720
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
2721
|
+
const selected = selector.parse(input, pos, ctx);
|
|
2722
|
+
if (!selected.ok) return selected;
|
|
2723
|
+
const chosen = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher));
|
|
2724
|
+
const branch = chosen ?? (fallback === void 0 ? void 0 : { keys: [], parser: fallback, caseInsensitive: false, usesRouted: fallbackUsesRouted });
|
|
2725
|
+
if (branch === void 0) {
|
|
2726
|
+
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2727
|
+
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2728
|
+
}
|
|
2729
|
+
const savedRouted = ctx._routed;
|
|
2730
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2731
|
+
let mark = saveTriviaMark(ctx);
|
|
2732
|
+
if (usesRouted) {
|
|
2733
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2734
|
+
mark = saveTriviaMark(ctx);
|
|
2735
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2736
|
+
}
|
|
2737
|
+
let result;
|
|
2738
|
+
try {
|
|
2739
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2740
|
+
} finally {
|
|
2741
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2742
|
+
}
|
|
2743
|
+
if (!result.ok) {
|
|
2744
|
+
rollbackTrivia(ctx, mark);
|
|
2745
|
+
return chosen === void 0 && !fallbackCommits ? result : { ...result, committed: true };
|
|
2746
|
+
}
|
|
2747
|
+
return {
|
|
2748
|
+
ok: true,
|
|
2749
|
+
value: [selected.value, result.value],
|
|
2750
|
+
span: { start: pos, end: result.span.end }
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2555
2755
|
|
|
2556
2756
|
// src/recovery/scan.ts
|
|
2557
2757
|
import { PlainSpan as PlainSpan2 } from "../cst/plain.js";
|
|
@@ -5500,7 +5700,7 @@ function regex(pattern, flags = "") {
|
|
|
5500
5700
|
}
|
|
5501
5701
|
|
|
5502
5702
|
// src/version.ts
|
|
5503
|
-
var PARSEMAN_VERSION = "0.61.
|
|
5703
|
+
var PARSEMAN_VERSION = "0.61.2";
|
|
5504
5704
|
|
|
5505
5705
|
// src/table/lex-body.ts
|
|
5506
5706
|
function buildLexBodyRecognizer(spec, constants) {
|
|
@@ -8856,7 +9056,7 @@ var Encoder = class {
|
|
|
8856
9056
|
const dispCovIds = this.plan?.dispatches.get(p);
|
|
8857
9057
|
let dispCov = 0;
|
|
8858
9058
|
const arms = [];
|
|
8859
|
-
const
|
|
9059
|
+
const routed2 = [];
|
|
8860
9060
|
const key = [], keyArm = [];
|
|
8861
9061
|
const fold = [], foldArm = [];
|
|
8862
9062
|
const match = [];
|
|
@@ -8864,7 +9064,7 @@ var Encoder = class {
|
|
|
8864
9064
|
for (const c of d.cases) {
|
|
8865
9065
|
const arm = arms.length;
|
|
8866
9066
|
arms.push(this.covWrap(this.node(c.parser).ip, dispCovIds?.[dispCov++], 1));
|
|
8867
|
-
|
|
9067
|
+
routed2.push(branchUsesRouted(c) ? 1 : 0);
|
|
8868
9068
|
for (const kk of c.keys) {
|
|
8869
9069
|
expected.push(JSON.stringify(kk));
|
|
8870
9070
|
if (c.caseInsensitive) {
|
|
@@ -8880,7 +9080,7 @@ var Encoder = class {
|
|
|
8880
9080
|
for (const m of d.matchers ?? []) {
|
|
8881
9081
|
const arm = arms.length;
|
|
8882
9082
|
arms.push(this.covWrap(this.node(m.parser).ip, dispCovIds?.[dispCov++], 1));
|
|
8883
|
-
|
|
9083
|
+
routed2.push(branchUsesRouted(m) ? 1 : 0);
|
|
8884
9084
|
if (!m.caseInsensitive) {
|
|
8885
9085
|
match.push([KIND[m.kind], m.value, m.flags ?? "", arm]);
|
|
8886
9086
|
continue;
|
|
@@ -8894,7 +9094,7 @@ var Encoder = class {
|
|
|
8894
9094
|
}
|
|
8895
9095
|
const other = d.otherwise === void 0 ? -1 : this.covWrap(this.node(d.otherwise).ip, dispCovIds?.[dispCov++], 1);
|
|
8896
9096
|
const dspIdx = this.dsp.length;
|
|
8897
|
-
this.dsp.push({ key, keyArm, fold, foldArm, match, routed, expected });
|
|
9097
|
+
this.dsp.push({ key, keyArm, fold, foldArm, match, routed: routed2, expected });
|
|
8898
9098
|
const head = this.emitHead(OP_DISPATCH, 5 + arms.length);
|
|
8899
9099
|
this.code[head + 1] = sel;
|
|
8900
9100
|
this.code[head + 2] = dspIdx;
|
|
@@ -10210,6 +10410,12 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
|
|
|
10210
10410
|
|
|
10211
10411
|
// src/table/child-slots.ts
|
|
10212
10412
|
function childSlots(code, ip, out) {
|
|
10413
|
+
const from = out.length;
|
|
10414
|
+
if (!childSlotOffsets(code, ip, out)) return false;
|
|
10415
|
+
for (let i = from; i < out.length; i++) out[i] = code[out[i]];
|
|
10416
|
+
return true;
|
|
10417
|
+
}
|
|
10418
|
+
function childSlotOffsets(code, ip, out) {
|
|
10213
10419
|
switch (code[ip]) {
|
|
10214
10420
|
// ZERO-WIDTH / TERMINAL ROWS — no successor. `OP_GUARD` and `OP_ADJ` are both
|
|
10215
10421
|
// childless tests. `OP_LIVE` holds a hand-written combinator in `prog.fns`; its
|
|
@@ -10242,7 +10448,7 @@ function childSlots(code, ip, out) {
|
|
|
10242
10448
|
case OP_COV:
|
|
10243
10449
|
case OP_REJECT:
|
|
10244
10450
|
case OP_GAPGATE:
|
|
10245
|
-
out.push(
|
|
10451
|
+
out.push(ip + 1);
|
|
10246
10452
|
return true;
|
|
10247
10453
|
// Single child at `ip + 2`.
|
|
10248
10454
|
case OP_GATE:
|
|
@@ -10259,55 +10465,55 @@ function childSlots(code, ip, out) {
|
|
|
10259
10465
|
case OP_ARMGATE:
|
|
10260
10466
|
case OP_RGATE:
|
|
10261
10467
|
case OP_RGATE2:
|
|
10262
|
-
out.push(
|
|
10468
|
+
out.push(ip + 2);
|
|
10263
10469
|
return true;
|
|
10264
10470
|
// `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
|
|
10265
10471
|
case OP_STRICT:
|
|
10266
|
-
out.push(
|
|
10472
|
+
out.push(ip + 1, ip + 2);
|
|
10267
10473
|
return true;
|
|
10268
10474
|
// `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
|
|
10269
10475
|
// data the enclosing `dispatch()` already consumed, so the only edge is the
|
|
10270
10476
|
// fallback.
|
|
10271
10477
|
case OP_ROUTED:
|
|
10272
|
-
if (code[ip + 1] >= 0) out.push(
|
|
10478
|
+
if (code[ip + 1] >= 0) out.push(ip + 1);
|
|
10273
10479
|
return true;
|
|
10274
10480
|
// `DISPATCH sel d other otherRouted n a1 … an` — the SELECTOR is a child on
|
|
10275
10481
|
// exactly the same footing as the arms: it runs inside whatever node and scope
|
|
10276
10482
|
// this site sits in.
|
|
10277
10483
|
case OP_DISPATCH: {
|
|
10278
|
-
out.push(
|
|
10279
|
-
if (code[ip + 3] >= 0) out.push(
|
|
10484
|
+
out.push(ip + 1);
|
|
10485
|
+
if (code[ip + 3] >= 0) out.push(ip + 3);
|
|
10280
10486
|
const n = code[ip + 5];
|
|
10281
|
-
for (let i = 0; i < n; i++) out.push(
|
|
10487
|
+
for (let i = 0; i < n; i++) out.push(ip + 6 + i);
|
|
10282
10488
|
return true;
|
|
10283
10489
|
}
|
|
10284
10490
|
case OP_SEQ:
|
|
10285
10491
|
case OP_SEQV: {
|
|
10286
10492
|
const n = code[ip + 1];
|
|
10287
|
-
for (let i = 0; i < n; i++) out.push(
|
|
10493
|
+
for (let i = 0; i < n; i++) out.push(ip + 2 + i);
|
|
10288
10494
|
return true;
|
|
10289
10495
|
}
|
|
10290
10496
|
case OP_SEQX: {
|
|
10291
10497
|
const n = code[ip + 2];
|
|
10292
|
-
for (let i = 0; i < n; i++) out.push(
|
|
10498
|
+
for (let i = 0; i < n; i++) out.push(ip + 3 + i);
|
|
10293
10499
|
return true;
|
|
10294
10500
|
}
|
|
10295
10501
|
case OP_CHOICE: {
|
|
10296
10502
|
const n = code[ip + 2];
|
|
10297
|
-
for (let i = 0; i < n; i++) out.push(
|
|
10503
|
+
for (let i = 0; i < n; i++) out.push(ip + 4 + i);
|
|
10298
10504
|
return true;
|
|
10299
10505
|
}
|
|
10300
10506
|
// Arms are stride-2 from `ip + 4` — the gate word sits beside each arm offset.
|
|
10301
10507
|
case OP_GREEDY: {
|
|
10302
|
-
out.push(
|
|
10508
|
+
out.push(ip + 1);
|
|
10303
10509
|
const n = code[ip + 2];
|
|
10304
|
-
for (let i = 0; i < n; i++) out.push(
|
|
10510
|
+
for (let i = 0; i < n; i++) out.push(ip + 4 + 2 * i);
|
|
10305
10511
|
return true;
|
|
10306
10512
|
}
|
|
10307
10513
|
case OP_REP:
|
|
10308
10514
|
case OP_REPV:
|
|
10309
|
-
out.push(
|
|
10310
|
-
if (code[ip + 4] >= 0) out.push(
|
|
10515
|
+
out.push(ip + 1);
|
|
10516
|
+
if (code[ip + 4] >= 0) out.push(ip + 4);
|
|
10311
10517
|
return true;
|
|
10312
10518
|
default:
|
|
10313
10519
|
return false;
|
|
@@ -16618,10 +16824,10 @@ function assemble(t2, prog, cfg) {
|
|
|
16618
16824
|
const hasFold = spec.byFold.size > 0;
|
|
16619
16825
|
const match = spec.match;
|
|
16620
16826
|
const matchN = match.length;
|
|
16621
|
-
const
|
|
16827
|
+
const routed2 = spec.routed;
|
|
16622
16828
|
const expected = spec.expected;
|
|
16623
16829
|
const validArm = (arm) => Number.isInteger(arm) && arm >= 0 && arm < n;
|
|
16624
|
-
if (
|
|
16830
|
+
if (routed2.length !== n) throw new TypeError("table assembler: malformed dispatch routed arity");
|
|
16625
16831
|
for (const arm of byKey.values()) if (!validArm(arm)) throw new TypeError("table assembler: malformed dispatch exact arm");
|
|
16626
16832
|
for (const arm of byFold.values()) if (!validArm(arm)) throw new TypeError("table assembler: malformed dispatch folded arm");
|
|
16627
16833
|
for (const m of match) if (!validArm(m[3])) throw new TypeError("table assembler: malformed dispatch matcher arm");
|
|
@@ -16646,7 +16852,7 @@ function assemble(t2, prog, cfg) {
|
|
|
16646
16852
|
const classify = hasFold ? matchBlock === void 0 ? (key) => byKey.get(key) ?? byFold.get(asciiFoldKey(key)) : (key) => byKey.get(key) ?? byFold.get(asciiFoldKey(key)) ?? matchBlock(key) : matchBlock === void 0 ? (key) => byKey.get(key) : (key) => byKey.get(key) ?? matchBlock(key);
|
|
16647
16853
|
const neverBranch = () => FAIL;
|
|
16648
16854
|
const bindArms = (start) => {
|
|
16649
|
-
const branch = (i) => i < n ? dispatchBranch(link(code[armBase + i]),
|
|
16855
|
+
const branch = (i) => i < n ? dispatchBranch(link(code[armBase + i]), routed2[i] === 1) : neverBranch;
|
|
16650
16856
|
const next = start + 4 < n ? bindArms(start + 4) : void 0;
|
|
16651
16857
|
return dispatchArmBlock(
|
|
16652
16858
|
start,
|
|
@@ -20261,6 +20467,38 @@ function not(combinator) {
|
|
|
20261
20467
|
};
|
|
20262
20468
|
}
|
|
20263
20469
|
|
|
20470
|
+
// src/combinators/withCtx.ts
|
|
20471
|
+
function withCtx(extra, combinator) {
|
|
20472
|
+
const meta = {
|
|
20473
|
+
firstSet: combinator._meta.firstSet,
|
|
20474
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
20475
|
+
isTrivia: false
|
|
20476
|
+
};
|
|
20477
|
+
const parse2 = typeof extra === "function" ? (input, pos, ctx) => {
|
|
20478
|
+
const saved = ctx.state;
|
|
20479
|
+
ctx.state = extra(saved);
|
|
20480
|
+
try {
|
|
20481
|
+
return combinator.parse(input, pos, ctx);
|
|
20482
|
+
} finally {
|
|
20483
|
+
ctx.state = saved;
|
|
20484
|
+
}
|
|
20485
|
+
} : (input, pos, ctx) => {
|
|
20486
|
+
const saved = ctx.state;
|
|
20487
|
+
ctx.state = extra;
|
|
20488
|
+
try {
|
|
20489
|
+
return combinator.parse(input, pos, ctx);
|
|
20490
|
+
} finally {
|
|
20491
|
+
ctx.state = saved;
|
|
20492
|
+
}
|
|
20493
|
+
};
|
|
20494
|
+
return {
|
|
20495
|
+
_tag: "withCtx",
|
|
20496
|
+
_meta: meta,
|
|
20497
|
+
_def: { tag: "withCtx", extra, parser: combinator },
|
|
20498
|
+
parse: parse2
|
|
20499
|
+
};
|
|
20500
|
+
}
|
|
20501
|
+
|
|
20264
20502
|
// src/analysis/rebuild.ts
|
|
20265
20503
|
var FROZEN_TAGS = /* @__PURE__ */ new Set([
|
|
20266
20504
|
"dispatch",
|
|
@@ -20274,6 +20512,7 @@ var FROZEN_TAGS = /* @__PURE__ */ new Set([
|
|
|
20274
20512
|
"lines",
|
|
20275
20513
|
"columns"
|
|
20276
20514
|
]);
|
|
20515
|
+
var THAWED_TAGS = /* @__PURE__ */ new Set(["dispatch", "withCtx", "routed", "guard"]);
|
|
20277
20516
|
var CARRIED_META = [
|
|
20278
20517
|
"grammarTrivia",
|
|
20279
20518
|
"grammarScanSkip",
|
|
@@ -20312,7 +20551,9 @@ function subtreeContains(p, targets) {
|
|
|
20312
20551
|
walk(p);
|
|
20313
20552
|
return hit;
|
|
20314
20553
|
}
|
|
20315
|
-
function rebuildCombinator(root, replacements) {
|
|
20554
|
+
function rebuildCombinator(root, replacements, options = {}) {
|
|
20555
|
+
const thaw = options.thaw === true;
|
|
20556
|
+
const after = options.after ?? ((_p, out) => out);
|
|
20316
20557
|
const memo = /* @__PURE__ */ new Map();
|
|
20317
20558
|
const frozenKeys = /* @__PURE__ */ new Set();
|
|
20318
20559
|
const frozen = [];
|
|
@@ -20332,15 +20573,16 @@ function rebuildCombinator(root, replacements) {
|
|
|
20332
20573
|
return p;
|
|
20333
20574
|
}
|
|
20334
20575
|
const here = p._ruleName ?? rule2;
|
|
20335
|
-
if (FROZEN_TAGS.has(d.tag)) {
|
|
20576
|
+
if (FROZEN_TAGS.has(d.tag) && !(thaw && THAWED_TAGS.has(d.tag))) {
|
|
20336
20577
|
const key = JSON.stringify([here, d.tag]);
|
|
20337
20578
|
if (!frozenKeys.has(key)) {
|
|
20338
20579
|
frozenKeys.add(key);
|
|
20339
20580
|
frozen.push({ tag: d.tag, rule: here });
|
|
20340
20581
|
}
|
|
20341
20582
|
for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
|
|
20342
|
-
|
|
20343
|
-
|
|
20583
|
+
const kept = after(p, p);
|
|
20584
|
+
memo.set(p, kept);
|
|
20585
|
+
return kept;
|
|
20344
20586
|
}
|
|
20345
20587
|
if (d.tag === "lazy") {
|
|
20346
20588
|
let target;
|
|
@@ -20377,6 +20619,14 @@ function rebuildCombinator(root, replacements) {
|
|
|
20377
20619
|
break;
|
|
20378
20620
|
}
|
|
20379
20621
|
case "choice": {
|
|
20622
|
+
if (d.gates.some((g) => g !== null) && thaw) {
|
|
20623
|
+
const unsafeChoice = choice;
|
|
20624
|
+
out = unsafeChoice(...d.parsers.map((arm, i) => {
|
|
20625
|
+
const gate = d.gates[i];
|
|
20626
|
+
return gate ? { combinator: one(arm), gate } : one(arm);
|
|
20627
|
+
}));
|
|
20628
|
+
break;
|
|
20629
|
+
}
|
|
20380
20630
|
if (d.gates.some((g) => g !== null)) {
|
|
20381
20631
|
const key = JSON.stringify([here, "choice(gated)"]);
|
|
20382
20632
|
if (!frozenKeys.has(key)) {
|
|
@@ -20384,8 +20634,9 @@ function rebuildCombinator(root, replacements) {
|
|
|
20384
20634
|
frozen.push({ tag: "choice(gated)", rule: here });
|
|
20385
20635
|
}
|
|
20386
20636
|
for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
|
|
20387
|
-
|
|
20388
|
-
|
|
20637
|
+
const kept = after(p, p);
|
|
20638
|
+
memo.set(p, kept);
|
|
20639
|
+
return kept;
|
|
20389
20640
|
}
|
|
20390
20641
|
const kids = d.parsers.map(one);
|
|
20391
20642
|
out = choice(...kids);
|
|
@@ -20463,13 +20714,33 @@ function rebuildCombinator(root, replacements) {
|
|
|
20463
20714
|
frozen.push({ tag: "node(untyped+build+opts)", rule: here });
|
|
20464
20715
|
}
|
|
20465
20716
|
for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
|
|
20466
|
-
|
|
20467
|
-
|
|
20717
|
+
const kept = after(p, p);
|
|
20718
|
+
memo.set(p, kept);
|
|
20719
|
+
return kept;
|
|
20468
20720
|
}
|
|
20469
20721
|
out = d.type === void 0 ? d.build === void 0 ? node(one(d.parser), opts) : node(one(d.parser), d.build) : node(d.type, one(d.parser), d.build, opts);
|
|
20470
20722
|
if (d.buildRawUnused === true && out._def.tag === "node") out._def.buildRawUnused = true;
|
|
20471
20723
|
break;
|
|
20472
20724
|
}
|
|
20725
|
+
case "dispatch": {
|
|
20726
|
+
const arms = d.cases.map((e) => when(e.keys, one(e.parser), e.caseInsensitive ? { caseInsensitive: true } : void 0));
|
|
20727
|
+
for (const e of d.matchers ?? []) {
|
|
20728
|
+
arms.push(when(dispatchMatcher(e), one(e.parser), e.caseInsensitive ? { caseInsensitive: true } : void 0));
|
|
20729
|
+
}
|
|
20730
|
+
if (d.otherwise) arms.push(otherwise(one(d.otherwise), d.otherwiseCommit === false ? { commit: false } : void 0));
|
|
20731
|
+
const unsafeDispatch = dispatch;
|
|
20732
|
+
out = unsafeDispatch(one(d.selector), ...arms);
|
|
20733
|
+
break;
|
|
20734
|
+
}
|
|
20735
|
+
case "withCtx":
|
|
20736
|
+
out = withCtx(d.extra, one(d.parser));
|
|
20737
|
+
break;
|
|
20738
|
+
case "routed":
|
|
20739
|
+
out = d.fallback === void 0 ? routed() : routed(one(d.fallback));
|
|
20740
|
+
break;
|
|
20741
|
+
case "guard":
|
|
20742
|
+
out = p;
|
|
20743
|
+
break;
|
|
20473
20744
|
case "grammar": {
|
|
20474
20745
|
const inner = one(d.parser);
|
|
20475
20746
|
out = parser({
|
|
@@ -20487,13 +20758,15 @@ function rebuildCombinator(root, replacements) {
|
|
|
20487
20758
|
frozen.push({ tag: d.tag, rule: here });
|
|
20488
20759
|
}
|
|
20489
20760
|
for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
|
|
20490
|
-
|
|
20491
|
-
|
|
20761
|
+
const kept = after(p, p);
|
|
20762
|
+
memo.set(p, kept);
|
|
20763
|
+
return kept;
|
|
20492
20764
|
}
|
|
20493
20765
|
}
|
|
20494
20766
|
carryStamps(p, out);
|
|
20495
|
-
|
|
20496
|
-
|
|
20767
|
+
const done = after(p, out);
|
|
20768
|
+
memo.set(p, done);
|
|
20769
|
+
return done;
|
|
20497
20770
|
};
|
|
20498
20771
|
const rebuilt = go(root, root._ruleName ?? "<entry>");
|
|
20499
20772
|
frozen.sort((a, b) => a.rule < b.rule ? -1 : a.rule > b.rule ? 1 : a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0);
|