parseman 0.30.0 → 0.32.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/dist/analysis/gating.d.ts +115 -0
- package/dist/analysis/gating.d.ts.map +1 -0
- package/dist/combinators/first-set.d.ts +25 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/gate.d.ts +24 -0
- package/dist/combinators/gate.d.ts.map +1 -0
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/guard.d.ts +5 -12
- package/dist/combinators/guard.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +7 -0
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts +45 -3
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +78 -14
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/index.cjs +721 -241
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +721 -241
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs +3 -1
- package/dist/language-service/index.cjs.map +2 -2
- package/dist/language-service/index.js +3 -1
- package/dist/language-service/index.js.map +2 -2
- package/dist/plugin/index.cjs +747 -253
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +747 -253
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.js.map +1 -1
- package/dist/types.d.ts +32 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +17 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -87,10 +87,13 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
87
87
|
return true;
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
+
function isZeroWidthAssertion(p) {
|
|
91
|
+
return p._def.tag === "not";
|
|
92
|
+
}
|
|
90
93
|
function sequenceFirstSet(parsers) {
|
|
91
94
|
let fs = empty();
|
|
92
95
|
for (const p of parsers) {
|
|
93
|
-
fs = union(fs, p._meta.firstSet);
|
|
96
|
+
if (!isZeroWidthAssertion(p)) fs = union(fs, p._meta.firstSet);
|
|
94
97
|
if (!matchesEmpty(p)) return fs;
|
|
95
98
|
}
|
|
96
99
|
return fs;
|
|
@@ -120,7 +123,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
120
123
|
case "sequence": {
|
|
121
124
|
let out = empty();
|
|
122
125
|
for (const term of d.parsers) {
|
|
123
|
-
out = union(out, fs(term));
|
|
126
|
+
if (!isZeroWidthAssertion(term)) out = union(out, fs(term));
|
|
124
127
|
if (!matchesEmpty(term)) return out;
|
|
125
128
|
}
|
|
126
129
|
return out;
|
|
@@ -408,9 +411,9 @@ var SPACE_RANGES = [
|
|
|
408
411
|
[12288, 12288],
|
|
409
412
|
[65279, 65279]
|
|
410
413
|
];
|
|
411
|
-
function shorthandRanges(
|
|
412
|
-
if (
|
|
413
|
-
if (
|
|
414
|
+
function shorthandRanges(ch2) {
|
|
415
|
+
if (ch2 === "d") return [[48, 57]];
|
|
416
|
+
if (ch2 === "s") return SPACE_RANGES;
|
|
414
417
|
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
415
418
|
}
|
|
416
419
|
function readUnicodeEscape(body, i) {
|
|
@@ -423,9 +426,9 @@ function parseClassRanges(body) {
|
|
|
423
426
|
const ranges = [];
|
|
424
427
|
let i = 0;
|
|
425
428
|
const readAtom = () => {
|
|
426
|
-
const
|
|
427
|
-
if (
|
|
428
|
-
if (
|
|
429
|
+
const ch2 = body[i];
|
|
430
|
+
if (ch2 === void 0) return null;
|
|
431
|
+
if (ch2 === "\\") {
|
|
429
432
|
const uni = readUnicodeEscape(body, i);
|
|
430
433
|
if (uni) {
|
|
431
434
|
i = uni.next;
|
|
@@ -439,8 +442,8 @@ function parseClassRanges(body) {
|
|
|
439
442
|
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
440
443
|
return { cp: e.codePointAt(0) };
|
|
441
444
|
}
|
|
442
|
-
i +=
|
|
443
|
-
return { cp:
|
|
445
|
+
i += ch2.length;
|
|
446
|
+
return { cp: ch2.codePointAt(0) };
|
|
444
447
|
};
|
|
445
448
|
while (i < body.length) {
|
|
446
449
|
const lo = readAtom();
|
|
@@ -507,12 +510,35 @@ function shortScanner(source, flags) {
|
|
|
507
510
|
return minOne && end === pos ? null : end;
|
|
508
511
|
};
|
|
509
512
|
}
|
|
513
|
+
function asciiCaseFold(fs) {
|
|
514
|
+
if (fs.kind !== "ranges") return fs;
|
|
515
|
+
const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
|
|
516
|
+
const add = (lo, hi) => {
|
|
517
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
518
|
+
};
|
|
519
|
+
for (const r of fs.ranges) {
|
|
520
|
+
const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
|
|
521
|
+
add(uLo + 32, uHi + 32);
|
|
522
|
+
const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
|
|
523
|
+
add(lLo - 32, lHi - 32);
|
|
524
|
+
}
|
|
525
|
+
ranges.sort((a, b) => a.lo - b.lo);
|
|
526
|
+
const merged = [];
|
|
527
|
+
for (const r of ranges) {
|
|
528
|
+
const last = merged[merged.length - 1];
|
|
529
|
+
if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
|
|
530
|
+
else merged.push({ lo: r.lo, hi: r.hi });
|
|
531
|
+
}
|
|
532
|
+
return { kind: "ranges", ranges: merged };
|
|
533
|
+
}
|
|
510
534
|
function regex(pattern, flags = "") {
|
|
511
535
|
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
512
536
|
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
513
537
|
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
514
538
|
const scan = shortScanner(source, resolvedFlags);
|
|
515
|
-
const
|
|
539
|
+
const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
540
|
+
const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
|
|
541
|
+
const canMatchNewline = raw.canMatchNewline;
|
|
516
542
|
const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
|
|
517
543
|
return {
|
|
518
544
|
_tag: "regex",
|
|
@@ -567,8 +593,8 @@ function parseRegex(src) {
|
|
|
567
593
|
const parseSeq = () => {
|
|
568
594
|
const parts = [];
|
|
569
595
|
while (i < src.length) {
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
596
|
+
const ch2 = src[i];
|
|
597
|
+
if (ch2 === "|" || ch2 === ")") break;
|
|
572
598
|
parts.push(parseQuantified());
|
|
573
599
|
}
|
|
574
600
|
if (parts.length === 0) return EMPTY2;
|
|
@@ -576,18 +602,18 @@ function parseRegex(src) {
|
|
|
576
602
|
};
|
|
577
603
|
const parseQuantified = () => {
|
|
578
604
|
const atom = parseAtom();
|
|
579
|
-
const
|
|
605
|
+
const ch2 = src[i];
|
|
580
606
|
let min = null;
|
|
581
|
-
if (
|
|
607
|
+
if (ch2 === "*") {
|
|
582
608
|
min = 0;
|
|
583
609
|
i++;
|
|
584
|
-
} else if (
|
|
610
|
+
} else if (ch2 === "+") {
|
|
585
611
|
min = 1;
|
|
586
612
|
i++;
|
|
587
|
-
} else if (
|
|
613
|
+
} else if (ch2 === "?") {
|
|
588
614
|
min = 0;
|
|
589
615
|
i++;
|
|
590
|
-
} else if (
|
|
616
|
+
} else if (ch2 === "{") {
|
|
591
617
|
min = parseBraceQuantifier();
|
|
592
618
|
}
|
|
593
619
|
if (min === null) return atom;
|
|
@@ -603,8 +629,8 @@ function parseRegex(src) {
|
|
|
603
629
|
return Number.parseInt(m[1], 10);
|
|
604
630
|
};
|
|
605
631
|
const parseAtom = () => {
|
|
606
|
-
const
|
|
607
|
-
switch (
|
|
632
|
+
const ch2 = src[i];
|
|
633
|
+
switch (ch2) {
|
|
608
634
|
case "(":
|
|
609
635
|
return parseGroup();
|
|
610
636
|
case "[":
|
|
@@ -625,8 +651,8 @@ function parseRegex(src) {
|
|
|
625
651
|
return charNode(fromRange(123, 123), false);
|
|
626
652
|
// a `{` with nothing to quantify is a literal brace
|
|
627
653
|
default:
|
|
628
|
-
i +=
|
|
629
|
-
return charNode(fromRange(
|
|
654
|
+
i += ch2.length;
|
|
655
|
+
return charNode(fromRange(ch2.codePointAt(0), ch2.codePointAt(0)), ch2 === "\n");
|
|
630
656
|
}
|
|
631
657
|
};
|
|
632
658
|
const parseGroup = () => {
|
|
@@ -1066,8 +1092,8 @@ function choice(...args) {
|
|
|
1066
1092
|
}
|
|
1067
1093
|
}
|
|
1068
1094
|
if (idx >= 0) {
|
|
1069
|
-
const
|
|
1070
|
-
if (
|
|
1095
|
+
const gate2 = gates[idx];
|
|
1096
|
+
if (gate2 && !gate2(ctx.state)) {
|
|
1071
1097
|
return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
|
|
1072
1098
|
}
|
|
1073
1099
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
@@ -1413,11 +1439,11 @@ function tryFastLabeledScan(input, cur, trivia2) {
|
|
|
1413
1439
|
function recordTriviaChunks(ctx, chunks) {
|
|
1414
1440
|
const kinds = ctx.triviaKindLabels;
|
|
1415
1441
|
const mask = ctx._triviaCaptureMask;
|
|
1416
|
-
for (const
|
|
1417
|
-
pushTriviaLogEntry(ctx,
|
|
1442
|
+
for (const ch2 of chunks) {
|
|
1443
|
+
pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
|
|
1418
1444
|
if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
|
|
1419
|
-
if (mask === void 0 || kinds === void 0 || (mask & 1 <<
|
|
1420
|
-
pushCstTriviaEntry(ctx,
|
|
1445
|
+
if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
|
|
1446
|
+
pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
|
|
1421
1447
|
}
|
|
1422
1448
|
}
|
|
1423
1449
|
}
|
|
@@ -2193,6 +2219,12 @@ function rules(a, b) {
|
|
|
2193
2219
|
if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
|
|
2194
2220
|
}
|
|
2195
2221
|
}
|
|
2222
|
+
if (options?.scanSkip != null) {
|
|
2223
|
+
for (const key of Object.keys(definitions)) {
|
|
2224
|
+
const rule = cache[key];
|
|
2225
|
+
if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2196
2228
|
for (const key of Object.keys(definitions)) {
|
|
2197
2229
|
markUnusedValues(cache[key]);
|
|
2198
2230
|
}
|
|
@@ -2675,9 +2707,11 @@ function parse(combinator, input, opts = {}) {
|
|
|
2675
2707
|
const _errors = opts.recover ? [] : void 0;
|
|
2676
2708
|
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
2677
2709
|
const grammarTrivia = combinator._meta.grammarTrivia;
|
|
2710
|
+
const grammarScanSkip = combinator._meta.grammarScanSkip;
|
|
2678
2711
|
const ctx = {
|
|
2679
2712
|
trackLines,
|
|
2680
2713
|
...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
|
|
2714
|
+
...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
|
|
2681
2715
|
..._errors !== void 0 ? { _errors } : {},
|
|
2682
2716
|
..._probe !== void 0 ? { _probe } : {}
|
|
2683
2717
|
};
|
|
@@ -2786,6 +2820,413 @@ function leaf(root, fn) {
|
|
|
2786
2820
|
};
|
|
2787
2821
|
}
|
|
2788
2822
|
|
|
2823
|
+
// src/version.ts
|
|
2824
|
+
var PARSEMAN_VERSION = "0.32.0";
|
|
2825
|
+
|
|
2826
|
+
// src/analysis/gating.ts
|
|
2827
|
+
var isAny = (fs) => fs.kind === "any";
|
|
2828
|
+
function intersects2(a, b) {
|
|
2829
|
+
if (a.kind === "any" || b.kind === "any") return true;
|
|
2830
|
+
if (a.kind === "empty" || b.kind === "empty") return false;
|
|
2831
|
+
for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true;
|
|
2832
|
+
return false;
|
|
2833
|
+
}
|
|
2834
|
+
function intersection(a, b) {
|
|
2835
|
+
if (a.kind === "any") return b;
|
|
2836
|
+
if (b.kind === "any") return a;
|
|
2837
|
+
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
2838
|
+
const ranges = [];
|
|
2839
|
+
for (const ra of a.ranges) for (const rb of b.ranges) {
|
|
2840
|
+
const lo = Math.max(ra.lo, rb.lo);
|
|
2841
|
+
const hi = Math.min(ra.hi, rb.hi);
|
|
2842
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
2843
|
+
}
|
|
2844
|
+
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
2845
|
+
}
|
|
2846
|
+
function combine(sets) {
|
|
2847
|
+
let acc = { kind: "empty" };
|
|
2848
|
+
for (const s of sets) {
|
|
2849
|
+
if (s.kind === "any") return { kind: "any" };
|
|
2850
|
+
if (s.kind === "empty") continue;
|
|
2851
|
+
acc = acc.kind === "ranges" ? { kind: "ranges", ranges: [...acc.ranges, ...s.ranges] } : s;
|
|
2852
|
+
}
|
|
2853
|
+
return acc;
|
|
2854
|
+
}
|
|
2855
|
+
var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
|
|
2856
|
+
function firstSetToString(fs) {
|
|
2857
|
+
if (fs.kind === "any") return "ANY";
|
|
2858
|
+
if (fs.kind === "empty") return "(empty)";
|
|
2859
|
+
return fs.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
|
|
2860
|
+
}
|
|
2861
|
+
var ruleNameOf = (p) => p._ruleName;
|
|
2862
|
+
var SUGGESTIONS = {
|
|
2863
|
+
"broad-recognizer": "if this arm leads with a keyword regex, use word('kw', boundary) / keywords([...]) \u2014 they expose an EXACT, gating first-set and lower to the same charCodeAt scan. A genuine scanTo fallback is fine; accept it in the gating snapshot if intentional.",
|
|
2864
|
+
"leading-not": "let the arm lead with its actual consuming terminal (first-sets gate it automatically); keep not(...) only as a TRAILING boundary. Do not hand-roll not(not(...)) to fake gating.",
|
|
2865
|
+
"nullable-prefix": "a leading optional/many lets a later, broad term start the arm. Split the empty case into its own arm, or gate on the prefix first char.",
|
|
2866
|
+
"cross-artifact-ref": "parseman >=0.32.0 resolves a g.Foo ref first-set at fuse time; if still ANY the target rule is itself ungated \u2014 analyze it and give it a concrete non-nullable lead.",
|
|
2867
|
+
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
2868
|
+
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
2869
|
+
};
|
|
2870
|
+
function classifyBroadArm(arm) {
|
|
2871
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2872
|
+
const walk2 = (p) => {
|
|
2873
|
+
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
2874
|
+
seen.add(p);
|
|
2875
|
+
const d = p._def;
|
|
2876
|
+
switch (d.tag) {
|
|
2877
|
+
case "literal":
|
|
2878
|
+
case "regex":
|
|
2879
|
+
case "keywords":
|
|
2880
|
+
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
2881
|
+
case "not":
|
|
2882
|
+
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
2883
|
+
case "scanTo":
|
|
2884
|
+
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
2885
|
+
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
2886
|
+
// its own — it is the poison. `withCtx`/`recover` FORWARD their inner
|
|
2887
|
+
// first-set (see firstSetOf), so a broad result comes from the inner parser:
|
|
2888
|
+
// walk into it for a precise cause.
|
|
2889
|
+
case "guard":
|
|
2890
|
+
return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
|
|
2891
|
+
// attempt/withCtx/recover FORWARD their inner first-set (transparent for
|
|
2892
|
+
// dispatch), so a broad result comes from the inner parser — walk into it.
|
|
2893
|
+
case "attempt":
|
|
2894
|
+
case "withCtx":
|
|
2895
|
+
case "recover":
|
|
2896
|
+
return walk2(d.parser);
|
|
2897
|
+
case "lazy": {
|
|
2898
|
+
const name = p._ruleName;
|
|
2899
|
+
try {
|
|
2900
|
+
const inner = walk2(d.thunk());
|
|
2901
|
+
if (name !== void 0) return inner ? { cause: "cross-artifact-ref", detail: `via ref g.${name} \u2192 ${inner.detail}` } : null;
|
|
2902
|
+
return inner;
|
|
2903
|
+
} catch {
|
|
2904
|
+
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}` };
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
case "optional":
|
|
2908
|
+
case "many": {
|
|
2909
|
+
const inner = walk2(d.parser);
|
|
2910
|
+
return { cause: "nullable-prefix", detail: inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term" };
|
|
2911
|
+
}
|
|
2912
|
+
case "oneOrMore":
|
|
2913
|
+
case "transform":
|
|
2914
|
+
case "label":
|
|
2915
|
+
case "field":
|
|
2916
|
+
case "trivia":
|
|
2917
|
+
case "token":
|
|
2918
|
+
case "leaf":
|
|
2919
|
+
case "node":
|
|
2920
|
+
case "grammar":
|
|
2921
|
+
case "expect":
|
|
2922
|
+
return walk2(d.parser);
|
|
2923
|
+
case "skip":
|
|
2924
|
+
return walk2(d.main);
|
|
2925
|
+
case "sequence": {
|
|
2926
|
+
let sawNullablePrefix = false;
|
|
2927
|
+
for (const t of d.parsers) {
|
|
2928
|
+
if (t._def.tag === "not") {
|
|
2929
|
+
sawNullablePrefix = true;
|
|
2930
|
+
continue;
|
|
2931
|
+
}
|
|
2932
|
+
if (isAny(firstSetOf(t))) {
|
|
2933
|
+
const inner = walk2(t);
|
|
2934
|
+
if (sawNullablePrefix)
|
|
2935
|
+
return { cause: "nullable-prefix", detail: inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term" };
|
|
2936
|
+
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
2937
|
+
}
|
|
2938
|
+
if (matchesEmpty(t)) {
|
|
2939
|
+
sawNullablePrefix = true;
|
|
2940
|
+
continue;
|
|
2941
|
+
}
|
|
2942
|
+
return null;
|
|
2943
|
+
}
|
|
2944
|
+
return { cause: "broad-recognizer", detail: "sequence of only nullable/zero-width terms" };
|
|
2945
|
+
}
|
|
2946
|
+
case "choice": {
|
|
2947
|
+
for (const a of d.parsers) {
|
|
2948
|
+
const r = walk2(a);
|
|
2949
|
+
if (r) return { cause: r.cause, detail: `choice arm \u2192 ${r.detail}` };
|
|
2950
|
+
}
|
|
2951
|
+
return null;
|
|
2952
|
+
}
|
|
2953
|
+
default:
|
|
2954
|
+
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
2955
|
+
}
|
|
2956
|
+
};
|
|
2957
|
+
return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
|
|
2958
|
+
}
|
|
2959
|
+
function peelToLeading(arm) {
|
|
2960
|
+
let d = arm._def;
|
|
2961
|
+
let cur = arm;
|
|
2962
|
+
for (; ; ) {
|
|
2963
|
+
if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label" || d.tag === "attempt") {
|
|
2964
|
+
cur = d.parser;
|
|
2965
|
+
d = cur._def;
|
|
2966
|
+
continue;
|
|
2967
|
+
}
|
|
2968
|
+
break;
|
|
2969
|
+
}
|
|
2970
|
+
if (d.tag === "sequence" && d.parsers.length >= 1) return d.parsers[0];
|
|
2971
|
+
return cur;
|
|
2972
|
+
}
|
|
2973
|
+
var KEYWORD_REGEX_RE = /^\^?[@#.-]?[A-Za-z][\w-]*(\(\?![^)]*\))?\$?$/;
|
|
2974
|
+
function detectAntiPatterns(rule, arms) {
|
|
2975
|
+
const out = [];
|
|
2976
|
+
arms.forEach((arm, i) => {
|
|
2977
|
+
const lead = peelToLeading(arm);
|
|
2978
|
+
const ld = lead._def;
|
|
2979
|
+
if (ld.tag === "not") {
|
|
2980
|
+
const inner = ld.parser;
|
|
2981
|
+
if (inner._def.tag === "not") {
|
|
2982
|
+
out.push({
|
|
2983
|
+
kind: "double-not",
|
|
2984
|
+
rule,
|
|
2985
|
+
armIndex: i,
|
|
2986
|
+
message: "not(not(...)) hand-rolls automatic first-char gating and MISCOMPILES among shared-first-char sibling arms (its first-set is ANY, poisoning dispatch). Remove it; let the arm lead with its consuming terminal, or use word()/keywords() for an exact first-set."
|
|
2987
|
+
});
|
|
2988
|
+
} else {
|
|
2989
|
+
out.push({
|
|
2990
|
+
kind: "leading-not",
|
|
2991
|
+
rule,
|
|
2992
|
+
armIndex: i,
|
|
2993
|
+
message: "a leading not(...) on a choice arm hand-rolls gating and poisons the choice first-set (not() is ANY). Reorder so a consuming terminal leads; keep not(...) as a TRAILING boundary only."
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
if (ld.tag === "regex" && KEYWORD_REGEX_RE.test(ld.source)) {
|
|
2998
|
+
out.push({
|
|
2999
|
+
kind: "keyword-regex",
|
|
3000
|
+
rule,
|
|
3001
|
+
armIndex: i,
|
|
3002
|
+
message: `regex(/${ld.source}/) leads this arm but is a keyword \u2014 use word('\u2026', boundary) / keywords([\u2026]) for an EXACT resolvable first-set (they lower to the same charCodeAt scan).`
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
});
|
|
3006
|
+
return out;
|
|
3007
|
+
}
|
|
3008
|
+
function analyzeGating(entry, opts) {
|
|
3009
|
+
const raw = [];
|
|
3010
|
+
const antiPatterns = [];
|
|
3011
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3012
|
+
const visit = (p, enclosingRule) => {
|
|
3013
|
+
if (visited.has(p)) return;
|
|
3014
|
+
visited.add(p);
|
|
3015
|
+
const d = p._def;
|
|
3016
|
+
const rule = ruleNameOf(p) ?? enclosingRule;
|
|
3017
|
+
if (d.tag === "choice") {
|
|
3018
|
+
raw.push({ g: analyzeChoice(p, d, rule), rule });
|
|
3019
|
+
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
3020
|
+
}
|
|
3021
|
+
const rec = d;
|
|
3022
|
+
const kids = [];
|
|
3023
|
+
if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
|
|
3024
|
+
for (const k of ["parser", "main", "skipped", "separator", "sentinel"])
|
|
3025
|
+
if (rec[k]) kids.push(rec[k]);
|
|
3026
|
+
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
3027
|
+
if (d.tag === "lazy") {
|
|
3028
|
+
try {
|
|
3029
|
+
kids.push(d.thunk());
|
|
3030
|
+
} catch {
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
for (const k of kids) visit(k, rule);
|
|
3034
|
+
};
|
|
3035
|
+
visit(entry, ruleNameOf(entry) ?? "<entry>");
|
|
3036
|
+
const perRule = /* @__PURE__ */ new Map();
|
|
3037
|
+
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3038
|
+
const seenInRule = /* @__PURE__ */ new Map();
|
|
3039
|
+
const accept = new Set(opts?.accept ?? []);
|
|
3040
|
+
const usedAccept = /* @__PURE__ */ new Set();
|
|
3041
|
+
const choices = raw.map(({ g, rule }) => {
|
|
3042
|
+
const n = seenInRule.get(rule) ?? 0;
|
|
3043
|
+
seenInRule.set(rule, n + 1);
|
|
3044
|
+
const id = (perRule.get(rule) ?? 1) === 1 ? rule : `${rule}#${n}`;
|
|
3045
|
+
const isAccepted = g.gates === "no" && accept.has(id);
|
|
3046
|
+
if (isAccepted) usedAccept.add(id);
|
|
3047
|
+
return { ...g, id, accepted: isAccepted };
|
|
3048
|
+
});
|
|
3049
|
+
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
3050
|
+
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
3051
|
+
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted);
|
|
3052
|
+
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
3053
|
+
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
3054
|
+
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, acceptedUnused, choices, antiPatterns };
|
|
3055
|
+
}
|
|
3056
|
+
function analyzeChoice(p, d, rule) {
|
|
3057
|
+
const arms = d.parsers;
|
|
3058
|
+
const shallow = arms.map((a) => a._meta.firstSet);
|
|
3059
|
+
const deep = arms.map((a) => firstSetOf(a));
|
|
3060
|
+
const anyArms = deep.map((fs, index) => ({ fs, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
3061
|
+
const { cause, detail } = classifyBroadArm(arms[index]);
|
|
3062
|
+
return { index, cause, detail, shallowAnyOnly: false, suggestion: SUGGESTIONS[cause] };
|
|
3063
|
+
});
|
|
3064
|
+
const overlaps = [];
|
|
3065
|
+
for (let i = 0; i < deep.length; i++)
|
|
3066
|
+
for (let j = i + 1; j < deep.length; j++)
|
|
3067
|
+
if (!isAny(deep[i]) && !isAny(deep[j]) && intersects2(deep[i], deep[j]))
|
|
3068
|
+
overlaps.push({
|
|
3069
|
+
a: i,
|
|
3070
|
+
b: j,
|
|
3071
|
+
on: intersection(deep[i], deep[j]),
|
|
3072
|
+
suggestion: "arms share a first char \u2014 left-factor. parseman auto-detects the sharedPrefix strategy for bare sequences (choice.ts); make the arms bare sequences with a common leading terminal, or restructure."
|
|
3073
|
+
});
|
|
3074
|
+
let gates;
|
|
3075
|
+
if (d.disjoint) gates = "yes";
|
|
3076
|
+
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
3077
|
+
else gates = "no";
|
|
3078
|
+
return {
|
|
3079
|
+
rule,
|
|
3080
|
+
strategy: d.strategy?.tag ?? "firstMatch",
|
|
3081
|
+
gates,
|
|
3082
|
+
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
3083
|
+
anyArms,
|
|
3084
|
+
overlaps
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
function formatGatingWarnings(report) {
|
|
3088
|
+
const lines = [];
|
|
3089
|
+
for (const c of report.ungated) {
|
|
3090
|
+
lines.push(`parseman gating: choice @ ${c.id} is UNGATED [${c.strategy}] \u2014 no first-char dispatch; every position speculatively enters doomed arms.`);
|
|
3091
|
+
for (const a of c.anyArms)
|
|
3092
|
+
lines.push(` \xB7 arm[${a.index}] first-set ANY (${a.cause}): ${a.detail}
|
|
3093
|
+
fix: ${a.suggestion}`);
|
|
3094
|
+
for (const o of c.overlaps)
|
|
3095
|
+
lines.push(` \xB7 arm[${o.a}] \u2229 arm[${o.b}] overlap on ${firstSetToString(o.on)}
|
|
3096
|
+
fix: ${o.suggestion}`);
|
|
3097
|
+
lines.push(` (intentional? accept it in the gating snapshot: { accept: ['${c.id}'] }.)`);
|
|
3098
|
+
}
|
|
3099
|
+
for (const ap of report.antiPatterns)
|
|
3100
|
+
lines.push(`parseman anti-pattern [${ap.kind}] @ ${ap.rule} arm[${ap.armIndex}]: ${ap.message}`);
|
|
3101
|
+
return lines;
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
// src/combinators/scanTo.ts
|
|
3105
|
+
function resolveScanSkip(explicitSkip, raw, ctx) {
|
|
3106
|
+
if (raw) return [];
|
|
3107
|
+
const trivia2 = ctx.trivia;
|
|
3108
|
+
const ambient = ctx.scanSkip;
|
|
3109
|
+
if (!trivia2 && !ambient) return explicitSkip;
|
|
3110
|
+
const out = [];
|
|
3111
|
+
if (trivia2) out.push(trivia2);
|
|
3112
|
+
if (ambient) out.push(...ambient);
|
|
3113
|
+
out.push(...explicitSkip);
|
|
3114
|
+
return out;
|
|
3115
|
+
}
|
|
3116
|
+
function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
|
|
3117
|
+
const meta = {
|
|
3118
|
+
firstSet: any(),
|
|
3119
|
+
canMatchNewline: true,
|
|
3120
|
+
isTrivia: false
|
|
3121
|
+
};
|
|
3122
|
+
const explicitSkip = skip2 ?? [];
|
|
3123
|
+
return {
|
|
3124
|
+
_tag: "scanTo",
|
|
3125
|
+
_meta: meta,
|
|
3126
|
+
_def: { tag: "scanTo", sentinel, skip: explicitSkip, raw, orEOF },
|
|
3127
|
+
parse(input, pos, ctx) {
|
|
3128
|
+
let cur = pos;
|
|
3129
|
+
const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
|
|
3130
|
+
const probeCtx = {
|
|
3131
|
+
trackLines: ctx.trackLines,
|
|
3132
|
+
state: ctx.state,
|
|
3133
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
3134
|
+
};
|
|
3135
|
+
const emit2 = (end) => {
|
|
3136
|
+
if (end > pos && cstCaptureActive(ctx)) {
|
|
3137
|
+
const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
3138
|
+
pushCstLeaf(ctx, leaf2);
|
|
3139
|
+
}
|
|
3140
|
+
};
|
|
3141
|
+
while (cur < input.length) {
|
|
3142
|
+
const s = sentinel.parse(input, cur, probeCtx);
|
|
3143
|
+
if (s.ok) {
|
|
3144
|
+
emit2(cur);
|
|
3145
|
+
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
3146
|
+
}
|
|
3147
|
+
let advanced = false;
|
|
3148
|
+
for (const skipper of skip3) {
|
|
3149
|
+
const r = skipper.parse(input, cur, probeCtx);
|
|
3150
|
+
if (r.ok && r.span.end > cur) {
|
|
3151
|
+
cur = r.span.end;
|
|
3152
|
+
advanced = true;
|
|
3153
|
+
break;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
if (!advanced) cur++;
|
|
3157
|
+
}
|
|
3158
|
+
if (orEOF) {
|
|
3159
|
+
emit2(cur);
|
|
3160
|
+
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
3161
|
+
}
|
|
3162
|
+
const sentDef = sentinel._def;
|
|
3163
|
+
const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
|
|
3164
|
+
return { ok: false, expected, span: { start: pos, end: cur } };
|
|
3165
|
+
}
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
function balanced(open, close, options = {}) {
|
|
3169
|
+
const ownSkip = options.skip ?? [];
|
|
3170
|
+
const combi = buildBalancedInterior(open, close, ownSkip);
|
|
3171
|
+
if (options.raw) return combi;
|
|
3172
|
+
const eagerParse = combi.parse.bind(combi);
|
|
3173
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3174
|
+
combi._balancedAmbient = { open, close, ownSkip };
|
|
3175
|
+
combi.parse = (input, pos, ctx) => {
|
|
3176
|
+
const amb = ctx.scanSkip;
|
|
3177
|
+
if (!amb || amb.length === 0) return eagerParse(input, pos, ctx);
|
|
3178
|
+
let interior = cache.get(amb);
|
|
3179
|
+
if (!interior) {
|
|
3180
|
+
interior = buildBalancedInterior(open, close, [...amb, ...ownSkip]);
|
|
3181
|
+
cache.set(amb, interior);
|
|
3182
|
+
}
|
|
3183
|
+
return interior.parse(input, pos, ctx);
|
|
3184
|
+
};
|
|
3185
|
+
return combi;
|
|
3186
|
+
}
|
|
3187
|
+
function buildBalancedInterior(open, close, skips) {
|
|
3188
|
+
const self = ref();
|
|
3189
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
3190
|
+
let bounded = true;
|
|
3191
|
+
for (const sk of skips) {
|
|
3192
|
+
const cs = firstSetClassChars(sk);
|
|
3193
|
+
if (cs === null) {
|
|
3194
|
+
bounded = false;
|
|
3195
|
+
break;
|
|
3196
|
+
}
|
|
3197
|
+
for (const ch2 of cs) stop.add(ch2);
|
|
3198
|
+
}
|
|
3199
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
3200
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
3201
|
+
const inner = many(choice(self, ...skips, content));
|
|
3202
|
+
const combi = transform(
|
|
3203
|
+
sequence(literal(open), inner, expect(literal(close))),
|
|
3204
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
3205
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
3206
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
3207
|
+
);
|
|
3208
|
+
if (combi._def.tag === "transform") {
|
|
3209
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
3210
|
+
combi._def.recognitionOnly = true;
|
|
3211
|
+
}
|
|
3212
|
+
self.define(combi);
|
|
3213
|
+
return combi;
|
|
3214
|
+
}
|
|
3215
|
+
function firstSetClassChars(c) {
|
|
3216
|
+
const fs = c._meta.firstSet;
|
|
3217
|
+
if (fs.kind === "empty") return [];
|
|
3218
|
+
if (fs.kind !== "ranges") return null;
|
|
3219
|
+
const out = [];
|
|
3220
|
+
for (const { lo, hi } of fs.ranges) {
|
|
3221
|
+
if (hi - lo > 8) return null;
|
|
3222
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3223
|
+
}
|
|
3224
|
+
return out;
|
|
3225
|
+
}
|
|
3226
|
+
function escapeClassChar(ch2) {
|
|
3227
|
+
return ch2.replace(/[\\\]^-]/g, "\\$&");
|
|
3228
|
+
}
|
|
3229
|
+
|
|
2789
3230
|
// src/compiler/grammar-coverage-ids.ts
|
|
2790
3231
|
function children(def, winners) {
|
|
2791
3232
|
switch (def.tag) {
|
|
@@ -2898,16 +3339,16 @@ function literalCodePoints(frag) {
|
|
|
2898
3339
|
const out = [];
|
|
2899
3340
|
let i = 0;
|
|
2900
3341
|
while (i < frag.length) {
|
|
2901
|
-
const
|
|
2902
|
-
if (
|
|
3342
|
+
const ch2 = frag[i];
|
|
3343
|
+
if (ch2 === "\\") {
|
|
2903
3344
|
const e = frag[i + 1];
|
|
2904
3345
|
if (e === void 0) return null;
|
|
2905
3346
|
out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
|
|
2906
3347
|
i += 2;
|
|
2907
3348
|
continue;
|
|
2908
3349
|
}
|
|
2909
|
-
if (META.has(
|
|
2910
|
-
out.push(
|
|
3350
|
+
if (META.has(ch2)) return null;
|
|
3351
|
+
out.push(ch2.codePointAt(0));
|
|
2911
3352
|
i += 1;
|
|
2912
3353
|
}
|
|
2913
3354
|
return out.length ? out : null;
|
|
@@ -2986,8 +3427,8 @@ function parseSeqParts(source) {
|
|
|
2986
3427
|
};
|
|
2987
3428
|
let i = 0;
|
|
2988
3429
|
while (i < source.length) {
|
|
2989
|
-
const
|
|
2990
|
-
if (
|
|
3430
|
+
const ch2 = source[i];
|
|
3431
|
+
if (ch2 === "[") {
|
|
2991
3432
|
let k = i + 1;
|
|
2992
3433
|
const neg = source[k] === "^";
|
|
2993
3434
|
if (neg) k++;
|
|
@@ -3009,7 +3450,7 @@ function parseSeqParts(source) {
|
|
|
3009
3450
|
i += runFrom(ranges, neg, i);
|
|
3010
3451
|
continue;
|
|
3011
3452
|
}
|
|
3012
|
-
if (
|
|
3453
|
+
if (ch2 === "\\") {
|
|
3013
3454
|
const e = source[i + 1];
|
|
3014
3455
|
if (e === "d" || e === "w" || e === "s") {
|
|
3015
3456
|
flush();
|
|
@@ -3038,7 +3479,7 @@ function parseSeqParts(source) {
|
|
|
3038
3479
|
} else lit.push(cp2);
|
|
3039
3480
|
continue;
|
|
3040
3481
|
}
|
|
3041
|
-
if (
|
|
3482
|
+
if (ch2 === "(") {
|
|
3042
3483
|
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
3043
3484
|
let k = i + 3;
|
|
3044
3485
|
let depth = 1;
|
|
@@ -3087,9 +3528,9 @@ function parseSeqParts(source) {
|
|
|
3087
3528
|
}
|
|
3088
3529
|
continue;
|
|
3089
3530
|
}
|
|
3090
|
-
if (META.has(
|
|
3091
|
-
const cp =
|
|
3092
|
-
i +=
|
|
3531
|
+
if (META.has(ch2)) return null;
|
|
3532
|
+
const cp = ch2.codePointAt(0);
|
|
3533
|
+
i += ch2.length;
|
|
3093
3534
|
if (source[i] === "?") {
|
|
3094
3535
|
flush();
|
|
3095
3536
|
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
@@ -3266,12 +3707,12 @@ function unwrapOuterGroup(source) {
|
|
|
3266
3707
|
let i = 3;
|
|
3267
3708
|
let ok = true;
|
|
3268
3709
|
while (i < source.length && depth > 0) {
|
|
3269
|
-
const
|
|
3270
|
-
if (
|
|
3710
|
+
const ch2 = source[i];
|
|
3711
|
+
if (ch2 === "\\") {
|
|
3271
3712
|
i += 2;
|
|
3272
3713
|
continue;
|
|
3273
3714
|
}
|
|
3274
|
-
if (
|
|
3715
|
+
if (ch2 === "[") {
|
|
3275
3716
|
let j = i + 1;
|
|
3276
3717
|
if (source[j] === "^") j++;
|
|
3277
3718
|
while (j < source.length && source[j] !== "]") {
|
|
@@ -3285,8 +3726,8 @@ function unwrapOuterGroup(source) {
|
|
|
3285
3726
|
i = j + 1;
|
|
3286
3727
|
continue;
|
|
3287
3728
|
}
|
|
3288
|
-
if (
|
|
3289
|
-
else if (
|
|
3729
|
+
if (ch2 === "(") depth++;
|
|
3730
|
+
else if (ch2 === ")") depth--;
|
|
3290
3731
|
i++;
|
|
3291
3732
|
}
|
|
3292
3733
|
if (!ok || depth !== 0 || i !== source.length) break;
|
|
@@ -3301,12 +3742,12 @@ function splitTopLevelAlternation(source) {
|
|
|
3301
3742
|
let last = 0;
|
|
3302
3743
|
let i = 0;
|
|
3303
3744
|
while (i < body.length) {
|
|
3304
|
-
const
|
|
3305
|
-
if (
|
|
3745
|
+
const ch2 = body[i];
|
|
3746
|
+
if (ch2 === "\\") {
|
|
3306
3747
|
i += 2;
|
|
3307
3748
|
continue;
|
|
3308
3749
|
}
|
|
3309
|
-
if (
|
|
3750
|
+
if (ch2 === "[") {
|
|
3310
3751
|
let j = i + 1;
|
|
3311
3752
|
if (body[j] === "^") j++;
|
|
3312
3753
|
while (j < body.length && body[j] !== "]") {
|
|
@@ -3317,18 +3758,18 @@ function splitTopLevelAlternation(source) {
|
|
|
3317
3758
|
i = j + 1;
|
|
3318
3759
|
continue;
|
|
3319
3760
|
}
|
|
3320
|
-
if (
|
|
3761
|
+
if (ch2 === "(") {
|
|
3321
3762
|
depth++;
|
|
3322
3763
|
i++;
|
|
3323
3764
|
continue;
|
|
3324
3765
|
}
|
|
3325
|
-
if (
|
|
3766
|
+
if (ch2 === ")") {
|
|
3326
3767
|
depth--;
|
|
3327
3768
|
if (depth < 0) return null;
|
|
3328
3769
|
i++;
|
|
3329
3770
|
continue;
|
|
3330
3771
|
}
|
|
3331
|
-
if (
|
|
3772
|
+
if (ch2 === "|" && depth === 0) {
|
|
3332
3773
|
parts.push(body.slice(last, i));
|
|
3333
3774
|
last = i + 1;
|
|
3334
3775
|
i++;
|
|
@@ -3447,12 +3888,12 @@ function splitDelimArms(body) {
|
|
|
3447
3888
|
let last = 0;
|
|
3448
3889
|
let i = 0;
|
|
3449
3890
|
while (i < body.length) {
|
|
3450
|
-
const
|
|
3451
|
-
if (
|
|
3891
|
+
const ch2 = body[i];
|
|
3892
|
+
if (ch2 === "\\") {
|
|
3452
3893
|
i += 2;
|
|
3453
3894
|
continue;
|
|
3454
3895
|
}
|
|
3455
|
-
if (
|
|
3896
|
+
if (ch2 === "[") {
|
|
3456
3897
|
let j = i + 1;
|
|
3457
3898
|
if (body[j] === "^") j++;
|
|
3458
3899
|
while (j < body.length && body[j] !== "]") {
|
|
@@ -3463,18 +3904,18 @@ function splitDelimArms(body) {
|
|
|
3463
3904
|
i = j + 1;
|
|
3464
3905
|
continue;
|
|
3465
3906
|
}
|
|
3466
|
-
if (
|
|
3907
|
+
if (ch2 === "(") {
|
|
3467
3908
|
depth++;
|
|
3468
3909
|
i++;
|
|
3469
3910
|
continue;
|
|
3470
3911
|
}
|
|
3471
|
-
if (
|
|
3912
|
+
if (ch2 === ")") {
|
|
3472
3913
|
depth--;
|
|
3473
3914
|
if (depth < 0) return null;
|
|
3474
3915
|
i++;
|
|
3475
3916
|
continue;
|
|
3476
3917
|
}
|
|
3477
|
-
if (
|
|
3918
|
+
if (ch2 === "|" && depth === 0) {
|
|
3478
3919
|
arms.push(body.slice(last, i));
|
|
3479
3920
|
last = i + 1;
|
|
3480
3921
|
}
|
|
@@ -3526,12 +3967,12 @@ function parseDelimited(source) {
|
|
|
3526
3967
|
let k = i + 3;
|
|
3527
3968
|
let depth = 1;
|
|
3528
3969
|
while (k < source.length && depth > 0) {
|
|
3529
|
-
const
|
|
3530
|
-
if (
|
|
3970
|
+
const ch2 = source[k];
|
|
3971
|
+
if (ch2 === "\\") {
|
|
3531
3972
|
k += 2;
|
|
3532
3973
|
continue;
|
|
3533
3974
|
}
|
|
3534
|
-
if (
|
|
3975
|
+
if (ch2 === "[") {
|
|
3535
3976
|
let j = k + 1;
|
|
3536
3977
|
if (source[j] === "^") j++;
|
|
3537
3978
|
while (j < source.length && source[j] !== "]") {
|
|
@@ -3542,8 +3983,8 @@ function parseDelimited(source) {
|
|
|
3542
3983
|
k = j + 1;
|
|
3543
3984
|
continue;
|
|
3544
3985
|
}
|
|
3545
|
-
if (
|
|
3546
|
-
else if (
|
|
3986
|
+
if (ch2 === "(") depth++;
|
|
3987
|
+
else if (ch2 === ")") depth--;
|
|
3547
3988
|
k++;
|
|
3548
3989
|
}
|
|
3549
3990
|
if (depth !== 0) return null;
|
|
@@ -4215,58 +4656,88 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
4215
4656
|
|
|
4216
4657
|
// src/compiler/codegen.ts
|
|
4217
4658
|
function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
4218
|
-
if (seen.has(p)) return { concrete: empty(), refs: [] };
|
|
4219
|
-
seen.add(p);
|
|
4220
4659
|
const d = p._def;
|
|
4660
|
+
if (d.tag === "lazy") {
|
|
4661
|
+
const name = p._ruleName;
|
|
4662
|
+
if (name !== void 0) return { alts: [[{ set: empty(), nullable: false, ref: name }]] };
|
|
4663
|
+
}
|
|
4664
|
+
if (seen.has(p)) return { alts: [[]] };
|
|
4665
|
+
seen.add(p);
|
|
4221
4666
|
const rec = (c) => leadingFirstSetRecipe(c, seen);
|
|
4222
|
-
const
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4667
|
+
const seg = (set, nullable2) => ({ set, nullable: nullable2 });
|
|
4668
|
+
const forceNullable = (r) => ({ alts: r.alts.map((alt) => alt.map((s) => ({ ...s, nullable: true }))) });
|
|
4669
|
+
const collapse = (r, nullable2) => {
|
|
4670
|
+
let set = empty();
|
|
4671
|
+
let hasRef = false;
|
|
4672
|
+
for (const alt of r.alts) {
|
|
4673
|
+
for (const s of alt) {
|
|
4674
|
+
if (s.ref !== void 0) hasRef = true;
|
|
4675
|
+
set = union(set, s.set);
|
|
4676
|
+
if (!s.nullable) break;
|
|
4231
4677
|
}
|
|
4232
4678
|
}
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
let out = { concrete: empty(), refs: [] };
|
|
4244
|
-
for (const term of d.parsers) {
|
|
4245
|
-
out = merge2(out, rec(term));
|
|
4246
|
-
if (!matchesEmpty(term)) return out;
|
|
4679
|
+
return seg(hasRef ? any() : set, nullable2);
|
|
4680
|
+
};
|
|
4681
|
+
const compute = () => {
|
|
4682
|
+
switch (d.tag) {
|
|
4683
|
+
case "lazy": {
|
|
4684
|
+
try {
|
|
4685
|
+
return rec(d.thunk());
|
|
4686
|
+
} catch {
|
|
4687
|
+
return { alts: [[seg(any(), false)]] };
|
|
4688
|
+
}
|
|
4247
4689
|
}
|
|
4248
|
-
|
|
4690
|
+
case "literal":
|
|
4691
|
+
case "regex":
|
|
4692
|
+
case "keywords":
|
|
4693
|
+
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
4694
|
+
case "choice": {
|
|
4695
|
+
const alts = [];
|
|
4696
|
+
for (const arm of d.parsers) alts.push(...rec(arm).alts);
|
|
4697
|
+
return { alts };
|
|
4698
|
+
}
|
|
4699
|
+
case "sequence": {
|
|
4700
|
+
const chain = [];
|
|
4701
|
+
for (const term of d.parsers) {
|
|
4702
|
+
if (!isZeroWidthAssertion(term)) {
|
|
4703
|
+
const tr = rec(term);
|
|
4704
|
+
if (tr.alts.length === 1) chain.push(...tr.alts[0]);
|
|
4705
|
+
else chain.push(collapse(tr, matchesEmpty(term)));
|
|
4706
|
+
}
|
|
4707
|
+
if (!matchesEmpty(term)) return { alts: [chain] };
|
|
4708
|
+
}
|
|
4709
|
+
return { alts: [chain] };
|
|
4710
|
+
}
|
|
4711
|
+
// `many`/`optional` are skippable as a whole → their leading chars start the
|
|
4712
|
+
// sequence but the chain must continue past them (force nullable). `oneOrMore`
|
|
4713
|
+
// requires at least one repetition, so it is NOT made nullable here.
|
|
4714
|
+
case "many":
|
|
4715
|
+
case "optional":
|
|
4716
|
+
return forceNullable(rec(d.parser));
|
|
4717
|
+
case "oneOrMore":
|
|
4718
|
+
case "transform":
|
|
4719
|
+
case "label":
|
|
4720
|
+
case "field":
|
|
4721
|
+
case "trivia":
|
|
4722
|
+
case "token":
|
|
4723
|
+
case "leaf":
|
|
4724
|
+
case "node":
|
|
4725
|
+
case "grammar":
|
|
4726
|
+
case "expect":
|
|
4727
|
+
return rec(d.parser);
|
|
4728
|
+
case "sepBy":
|
|
4729
|
+
return rec(d.parser);
|
|
4730
|
+
case "skip":
|
|
4731
|
+
return rec(d.main);
|
|
4732
|
+
// not / scanTo / guard / withCtx / recover / unknown → shallow set (safe).
|
|
4733
|
+
default:
|
|
4734
|
+
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
4249
4735
|
}
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
case "field":
|
|
4256
|
-
case "trivia":
|
|
4257
|
-
case "token":
|
|
4258
|
-
case "leaf":
|
|
4259
|
-
case "node":
|
|
4260
|
-
case "grammar":
|
|
4261
|
-
case "expect":
|
|
4262
|
-
return rec(d.parser);
|
|
4263
|
-
case "sepBy":
|
|
4264
|
-
return rec(d.parser);
|
|
4265
|
-
case "skip":
|
|
4266
|
-
return rec(d.main);
|
|
4267
|
-
// not / scanTo / guard / withCtx / recover / unknown → shallow set (safe).
|
|
4268
|
-
default:
|
|
4269
|
-
return { concrete: p._meta.firstSet, refs: [] };
|
|
4736
|
+
};
|
|
4737
|
+
try {
|
|
4738
|
+
return compute();
|
|
4739
|
+
} finally {
|
|
4740
|
+
seen.delete(p);
|
|
4270
4741
|
}
|
|
4271
4742
|
}
|
|
4272
4743
|
var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) return false; let s; try { s = Function.prototype.toString.call(b) } catch (e) { return true } if (/\\barguments\\b/.test(s)) return true; const m = /^[^(]*\\(([\\s\\S]*?)\\)/.exec(s); if (m && /\\.\\.\\.|=/.test(m[1])) return true; return b.length > n }";
|
|
@@ -4401,9 +4872,9 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
4401
4872
|
const i = ind(ctx);
|
|
4402
4873
|
const lf = v(ctx, "_lf");
|
|
4403
4874
|
return [
|
|
4404
|
-
`${i}if (_ctx._cstLeaves) {`,
|
|
4875
|
+
`${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
|
|
4405
4876
|
`${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
|
|
4406
|
-
`${i} _ctx._cstLeaves.push(${lf})`,
|
|
4877
|
+
`${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
|
|
4407
4878
|
`${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
|
|
4408
4879
|
`${i}}`
|
|
4409
4880
|
];
|
|
@@ -4462,16 +4933,19 @@ function ensureTriviaFn(ctx) {
|
|
|
4462
4933
|
const savedIndent = ctx.indent;
|
|
4463
4934
|
const savedFailLabel = ctx.failLabel;
|
|
4464
4935
|
const savedTrivia = ctx.activeTrivia;
|
|
4936
|
+
const savedScanSkip = ctx.activeScanSkip;
|
|
4465
4937
|
const savedCapAsTrivia = ctx.capAsTrivia;
|
|
4466
4938
|
ctx.indent = 2;
|
|
4467
4939
|
ctx.failLabel = "_triv";
|
|
4468
4940
|
ctx.capAsTrivia = true;
|
|
4469
4941
|
ctx.activeTrivia = void 0;
|
|
4942
|
+
ctx.activeScanSkip = void 0;
|
|
4470
4943
|
const r = emit(trivia2, ctx, "_pos");
|
|
4471
4944
|
ctx.indent = savedIndent;
|
|
4472
4945
|
ctx.failLabel = savedFailLabel;
|
|
4473
4946
|
ctx.capAsTrivia = savedCapAsTrivia;
|
|
4474
4947
|
ctx.activeTrivia = savedTrivia;
|
|
4948
|
+
ctx.activeScanSkip = savedScanSkip;
|
|
4475
4949
|
ctx.namedFnDecls.push([
|
|
4476
4950
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
4477
4951
|
` let _e = _pos`,
|
|
@@ -4749,7 +5223,7 @@ function emitKeywordsFast(def, ctx, pos) {
|
|
|
4749
5223
|
const bodyInd = ind(ctx) + " ";
|
|
4750
5224
|
const tries = [];
|
|
4751
5225
|
for (const w of def.words) {
|
|
4752
|
-
const cps = Array.from(w, (
|
|
5226
|
+
const cps = Array.from(w, (ch2) => ch2.codePointAt(0));
|
|
4753
5227
|
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
4754
5228
|
if (boundary) {
|
|
4755
5229
|
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
@@ -4942,7 +5416,7 @@ function canMatchEmptyAtStart(p) {
|
|
|
4942
5416
|
const d = p._def;
|
|
4943
5417
|
switch (d.tag) {
|
|
4944
5418
|
case "regex":
|
|
4945
|
-
return
|
|
5419
|
+
return regexMatchesEmpty(d.source, d.flags);
|
|
4946
5420
|
case "optional":
|
|
4947
5421
|
return true;
|
|
4948
5422
|
case "many":
|
|
@@ -4994,10 +5468,10 @@ function emitDisjointArm(p, ctx, pos, valV, endV, coverageId) {
|
|
|
4994
5468
|
}
|
|
4995
5469
|
function emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageId) {
|
|
4996
5470
|
const p = def.parsers[i];
|
|
4997
|
-
const
|
|
4998
|
-
if (!
|
|
5471
|
+
const gate2 = def.gates[i];
|
|
5472
|
+
if (!gate2) return emitDisjointArm(p, ctx, pos, valV, endV, coverageId);
|
|
4999
5473
|
const gateIdx = ctx.mapFns.length;
|
|
5000
|
-
ctx.mapFns.push(
|
|
5474
|
+
ctx.mapFns.push(gate2);
|
|
5001
5475
|
ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
|
|
5002
5476
|
const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
5003
5477
|
const stmts = [`${ind(ctx)}if (${gateCond}) {`];
|
|
@@ -5155,7 +5629,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
5155
5629
|
if (preStmts) stmts.push(...preStmts);
|
|
5156
5630
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
5157
5631
|
const p = def.parsers[i];
|
|
5158
|
-
const
|
|
5632
|
+
const gate2 = def.gates[i];
|
|
5159
5633
|
const autoNot = def.autoNot[i];
|
|
5160
5634
|
const atStart = failsAtStart(p);
|
|
5161
5635
|
const staticFx = armStaticExpected(ctx, p);
|
|
@@ -5167,9 +5641,9 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
5167
5641
|
})();
|
|
5168
5642
|
const fsGuard = armGuardPrefix === void 0 ? baseFsGuard : baseFsGuard === null ? armGuardPrefix : `${armGuardPrefix} && (${baseFsGuard})`;
|
|
5169
5643
|
let gateCond = null;
|
|
5170
|
-
if (
|
|
5644
|
+
if (gate2) {
|
|
5171
5645
|
const gateIdx = ctx.mapFns.length;
|
|
5172
|
-
ctx.mapFns.push(
|
|
5646
|
+
ctx.mapFns.push(gate2);
|
|
5173
5647
|
ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
|
|
5174
5648
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
5175
5649
|
}
|
|
@@ -5586,10 +6060,15 @@ function emitScanTo(def, ctx, pos) {
|
|
|
5586
6060
|
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
5587
6061
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
5588
6062
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
5589
|
-
|
|
6063
|
+
const effectiveSkip = def.raw ? def.skip : [
|
|
6064
|
+
...ctx.activeTrivia ? [ctx.activeTrivia] : [],
|
|
6065
|
+
...ctx.activeScanSkip ?? [],
|
|
6066
|
+
...def.skip
|
|
6067
|
+
];
|
|
6068
|
+
if (effectiveSkip.length > 0) {
|
|
5590
6069
|
const advV = v(ctx, "_stadv");
|
|
5591
6070
|
stmts.push(`${ind(ctx)}let ${advV} = false`);
|
|
5592
|
-
for (const skipper of
|
|
6071
|
+
for (const skipper of effectiveSkip) {
|
|
5593
6072
|
const { stmts: skStmts, okVar: skOk, endVar: skEnd } = emitFallible(skipper, probeCtx, curV);
|
|
5594
6073
|
stmts.push(
|
|
5595
6074
|
`${ind(ctx)}if (!${advV}) {`,
|
|
@@ -5807,8 +6286,10 @@ function emitNode(def, ctx, pos) {
|
|
|
5807
6286
|
const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
|
|
5808
6287
|
const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
|
|
5809
6288
|
const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
|
|
6289
|
+
const chNeededExpr = def.unwrap || def.collapse ? "true" : `(_ctx._pmReadsCh ??= (_ctx.build === undefined || _ctx.build._parsemanReadsChildren !== false || _ctx.build._parsemanCstCollapse !== undefined))`;
|
|
6290
|
+
const chAlloc = def.unwrap || def.collapse ? `${profileRecognizer} ? undefined : []` : `${profileRecognizer} ? undefined : ((${profileCapture} || ${chNeededExpr}) ? [] : undefined)`;
|
|
5810
6291
|
const allocStmt = structural ? `${i}const ${capTLv} = !(${profileRecognizer}) && (${profileCapture} || ${structuralCapturesTrivia ? "true" : hostTriviaGate}), ${capSTv} = !(${profileRecognizer} || ${profileCapture}) && (_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))${capFv ? `, ${capFv} = !(${profileRecognizer}) && (${profileCapture} || (_ctx.build !== undefined && _hostReads(_ctx.build, 2)))` : ""}
|
|
5811
|
-
${i}const ${chV} = ${
|
|
6292
|
+
${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
|
|
5812
6293
|
` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
|
|
5813
6294
|
` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
|
|
5814
6295
|
const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
|
|
@@ -6057,6 +6538,24 @@ function emit(p, ctx, pos) {
|
|
|
6057
6538
|
return instrument(dispatch());
|
|
6058
6539
|
}
|
|
6059
6540
|
function emitDispatch(p, ctx, pos) {
|
|
6541
|
+
const bal = p._balancedAmbient;
|
|
6542
|
+
if (bal && ctx.activeScanSkip && ctx.activeScanSkip.length > 0 && !ctx.balancedRebuildStack?.has(p)) {
|
|
6543
|
+
const interior = buildBalancedInterior(bal.open, bal.close, [...ctx.activeScanSkip, ...bal.ownSkip]);
|
|
6544
|
+
markUnusedValues(interior);
|
|
6545
|
+
if (ctx.lazyUsage) {
|
|
6546
|
+
const u = analyzeLazyUsage(interior);
|
|
6547
|
+
for (const [k, n] of u.counts) ctx.lazyUsage.counts.set(k, (ctx.lazyUsage.counts.get(k) ?? 0) + n);
|
|
6548
|
+
for (const k of u.recursive) ctx.lazyUsage.recursive.add(k);
|
|
6549
|
+
for (const [k, s] of u.sizes) ctx.lazyUsage.sizes.set(k, s);
|
|
6550
|
+
}
|
|
6551
|
+
const stack = ctx.balancedRebuildStack ?? (ctx.balancedRebuildStack = /* @__PURE__ */ new Set());
|
|
6552
|
+
stack.add(p);
|
|
6553
|
+
try {
|
|
6554
|
+
return emit(interior, ctx, pos);
|
|
6555
|
+
} finally {
|
|
6556
|
+
stack.delete(p);
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6060
6559
|
const def = p._def;
|
|
6061
6560
|
switch (def.tag) {
|
|
6062
6561
|
case "literal":
|
|
@@ -6478,9 +6977,36 @@ function ruleDependencies(ruleMap) {
|
|
|
6478
6977
|
}
|
|
6479
6978
|
return deps;
|
|
6480
6979
|
}
|
|
6980
|
+
function resolveGatingLevel(opt) {
|
|
6981
|
+
const explicit = typeof opt === "string" ? opt : opt?.level;
|
|
6982
|
+
if (explicit !== void 0) return explicit;
|
|
6983
|
+
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_GATING : void 0;
|
|
6984
|
+
if (env === "off" || env === "warn" || env === "error") return env;
|
|
6985
|
+
return "warn";
|
|
6986
|
+
}
|
|
6987
|
+
function runGatingDiagnostic(combinator, opt) {
|
|
6988
|
+
const level = resolveGatingLevel(opt);
|
|
6989
|
+
if (level === "off") return void 0;
|
|
6990
|
+
const accept = opt !== null && typeof opt === "object" ? opt.accept : void 0;
|
|
6991
|
+
let report;
|
|
6992
|
+
try {
|
|
6993
|
+
report = analyzeGating(combinator, accept ? { accept } : void 0);
|
|
6994
|
+
} catch {
|
|
6995
|
+
return void 0;
|
|
6996
|
+
}
|
|
6997
|
+
const lines = formatGatingWarnings(report);
|
|
6998
|
+
if (lines.length > 0) {
|
|
6999
|
+
if (level === "error") throw new Error(`parseman: ${report.ungated.length} ungated hot choice(s) / ${report.antiPatterns.length} anti-pattern(s)
|
|
7000
|
+
${lines.join("\n")}`);
|
|
7001
|
+
for (const l of lines) console.warn(l);
|
|
7002
|
+
}
|
|
7003
|
+
return report;
|
|
7004
|
+
}
|
|
6481
7005
|
function compile(combinator, mapFnSources, opts) {
|
|
7006
|
+
const gatingReport = runGatingDiagnostic(combinator, opts?.gating);
|
|
6482
7007
|
markUnusedValues(combinator);
|
|
6483
7008
|
const grammarTrivia = combinator._meta.grammarTrivia;
|
|
7009
|
+
const grammarScanSkip = combinator._meta.grammarScanSkip;
|
|
6484
7010
|
const ctx = {
|
|
6485
7011
|
vars: 0,
|
|
6486
7012
|
indent: 1,
|
|
@@ -6502,7 +7028,8 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
6502
7028
|
recovery: opts?.recovery ?? false,
|
|
6503
7029
|
...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
|
|
6504
7030
|
lazyUsage: analyzeLazyUsage(combinator),
|
|
6505
|
-
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
|
|
7031
|
+
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
|
|
7032
|
+
...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
|
|
6506
7033
|
};
|
|
6507
7034
|
const r = emit(combinator, ctx, "_pos");
|
|
6508
7035
|
const coverageRootRuleId = ctx.coverage?.plan.rules.get(combinator);
|
|
@@ -6566,6 +7093,7 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
6566
7093
|
source,
|
|
6567
7094
|
inlineExpression,
|
|
6568
7095
|
...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
|
|
7096
|
+
...gatingReport === void 0 ? {} : { gating: gatingReport },
|
|
6569
7097
|
parse(input, pos = 0) {
|
|
6570
7098
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
|
|
6571
7099
|
},
|
|
@@ -6601,6 +7129,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6601
7129
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
6602
7130
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
6603
7131
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
7132
|
+
const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
6604
7133
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
6605
7134
|
const d = val._def;
|
|
6606
7135
|
if (d.tag !== "lazy") return true;
|
|
@@ -6638,7 +7167,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6638
7167
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
6639
7168
|
ns,
|
|
6640
7169
|
deferFirstSetRefs: true,
|
|
6641
|
-
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
|
|
7170
|
+
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
|
|
7171
|
+
...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
|
|
6642
7172
|
};
|
|
6643
7173
|
const seen = /* @__PURE__ */ new Map();
|
|
6644
7174
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
@@ -6702,9 +7232,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6702
7232
|
ctx.failLabel = "_pfail";
|
|
6703
7233
|
ctx.recordFail = true;
|
|
6704
7234
|
const savedTrivia = ctx.activeTrivia;
|
|
6705
|
-
|
|
7235
|
+
const savedScanSkip = ctx.activeScanSkip;
|
|
7236
|
+
if (rule._meta.isTrivia) {
|
|
7237
|
+
ctx.activeTrivia = void 0;
|
|
7238
|
+
ctx.activeScanSkip = void 0;
|
|
7239
|
+
}
|
|
6706
7240
|
const body = emit(resolved, ctx, "_pos");
|
|
6707
7241
|
ctx.activeTrivia = savedTrivia;
|
|
7242
|
+
ctx.activeScanSkip = savedScanSkip;
|
|
6708
7243
|
ctx.indent = savedIndent;
|
|
6709
7244
|
ctx.failLabel = savedFail;
|
|
6710
7245
|
ctx.recordFail = savedRec;
|
|
@@ -6751,6 +7286,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6751
7286
|
}
|
|
6752
7287
|
const firstSets = /* @__PURE__ */ new Map();
|
|
6753
7288
|
const firstSetRecipes = /* @__PURE__ */ new Map();
|
|
7289
|
+
const nullable2 = /* @__PURE__ */ new Map();
|
|
6754
7290
|
for (const [key, rule] of ruleMap) {
|
|
6755
7291
|
let resolved = rule;
|
|
6756
7292
|
const d = rule._def;
|
|
@@ -6762,15 +7298,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6762
7298
|
}
|
|
6763
7299
|
}
|
|
6764
7300
|
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
6765
|
-
|
|
7301
|
+
nullable2.set(key, matchesEmpty(resolved));
|
|
7302
|
+
firstSetRecipes.set(key, canMatchEmptyAtStart(resolved) ? { alts: [[{ set: any(), nullable: false }]] } : leadingFirstSetRecipe(resolved));
|
|
6766
7303
|
}
|
|
6767
7304
|
return {
|
|
7305
|
+
v: PARSEMAN_VERSION,
|
|
6768
7306
|
ns,
|
|
6769
7307
|
keys: perEntry.map((e) => e.key),
|
|
6770
7308
|
prelude,
|
|
6771
7309
|
ruleFns,
|
|
6772
7310
|
wrappers,
|
|
6773
7311
|
firstSets,
|
|
7312
|
+
nullable: nullable2,
|
|
6774
7313
|
firstSetRecipes,
|
|
6775
7314
|
deps: ruleDependencies(ruleMap),
|
|
6776
7315
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
@@ -6849,101 +7388,6 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
|
|
|
6849
7388
|
].join("\n");
|
|
6850
7389
|
}
|
|
6851
7390
|
|
|
6852
|
-
// src/combinators/scanTo.ts
|
|
6853
|
-
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
6854
|
-
const meta = {
|
|
6855
|
-
firstSet: any(),
|
|
6856
|
-
canMatchNewline: true,
|
|
6857
|
-
isTrivia: false
|
|
6858
|
-
};
|
|
6859
|
-
return {
|
|
6860
|
-
_tag: "scanTo",
|
|
6861
|
-
_meta: meta,
|
|
6862
|
-
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
6863
|
-
parse(input, pos, ctx) {
|
|
6864
|
-
let cur = pos;
|
|
6865
|
-
const probeCtx = {
|
|
6866
|
-
trackLines: ctx.trackLines,
|
|
6867
|
-
state: ctx.state,
|
|
6868
|
-
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
6869
|
-
};
|
|
6870
|
-
const emit2 = (end) => {
|
|
6871
|
-
if (end > pos && cstCaptureActive(ctx)) {
|
|
6872
|
-
const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
6873
|
-
pushCstLeaf(ctx, leaf2);
|
|
6874
|
-
}
|
|
6875
|
-
};
|
|
6876
|
-
while (cur < input.length) {
|
|
6877
|
-
const s = sentinel.parse(input, cur, probeCtx);
|
|
6878
|
-
if (s.ok) {
|
|
6879
|
-
emit2(cur);
|
|
6880
|
-
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
6881
|
-
}
|
|
6882
|
-
let advanced = false;
|
|
6883
|
-
for (const skipper of skip2) {
|
|
6884
|
-
const r = skipper.parse(input, cur, probeCtx);
|
|
6885
|
-
if (r.ok && r.span.end > cur) {
|
|
6886
|
-
cur = r.span.end;
|
|
6887
|
-
advanced = true;
|
|
6888
|
-
break;
|
|
6889
|
-
}
|
|
6890
|
-
}
|
|
6891
|
-
if (!advanced) cur++;
|
|
6892
|
-
}
|
|
6893
|
-
if (orEOF) {
|
|
6894
|
-
emit2(cur);
|
|
6895
|
-
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
6896
|
-
}
|
|
6897
|
-
const sentDef = sentinel._def;
|
|
6898
|
-
const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
|
|
6899
|
-
return { ok: false, expected, span: { start: pos, end: cur } };
|
|
6900
|
-
}
|
|
6901
|
-
};
|
|
6902
|
-
}
|
|
6903
|
-
function balanced(open, close, options = {}) {
|
|
6904
|
-
const self = ref();
|
|
6905
|
-
const skips = options.skip ?? [];
|
|
6906
|
-
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
6907
|
-
let bounded = true;
|
|
6908
|
-
for (const sk of skips) {
|
|
6909
|
-
const cs = firstSetClassChars(sk);
|
|
6910
|
-
if (cs === null) {
|
|
6911
|
-
bounded = false;
|
|
6912
|
-
break;
|
|
6913
|
-
}
|
|
6914
|
-
for (const ch of cs) stop.add(ch);
|
|
6915
|
-
}
|
|
6916
|
-
const cls = [...stop].map(escapeClassChar).join("");
|
|
6917
|
-
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
6918
|
-
const inner = many(choice(self, ...skips, content));
|
|
6919
|
-
const combi = transform(
|
|
6920
|
-
sequence(literal(open), inner, expect(literal(close))),
|
|
6921
|
-
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
6922
|
-
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
6923
|
-
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
6924
|
-
);
|
|
6925
|
-
if (combi._def.tag === "transform") {
|
|
6926
|
-
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
6927
|
-
combi._def.recognitionOnly = true;
|
|
6928
|
-
}
|
|
6929
|
-
self.define(combi);
|
|
6930
|
-
return combi;
|
|
6931
|
-
}
|
|
6932
|
-
function firstSetClassChars(c) {
|
|
6933
|
-
const fs = c._meta.firstSet;
|
|
6934
|
-
if (fs.kind === "empty") return [];
|
|
6935
|
-
if (fs.kind !== "ranges") return null;
|
|
6936
|
-
const out = [];
|
|
6937
|
-
for (const { lo, hi } of fs.ranges) {
|
|
6938
|
-
if (hi - lo > 8) return null;
|
|
6939
|
-
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
6940
|
-
}
|
|
6941
|
-
return out;
|
|
6942
|
-
}
|
|
6943
|
-
function escapeClassChar(ch) {
|
|
6944
|
-
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
6945
|
-
}
|
|
6946
|
-
|
|
6947
7391
|
// src/combinators/withCtx.ts
|
|
6948
7392
|
function withCtx(extra, combinator) {
|
|
6949
7393
|
const meta = {
|
|
@@ -7000,7 +7444,7 @@ function childrenOf2(def) {
|
|
|
7000
7444
|
return [];
|
|
7001
7445
|
}
|
|
7002
7446
|
}
|
|
7003
|
-
var
|
|
7447
|
+
var ruleNameOf2 = (c) => c._ruleName;
|
|
7004
7448
|
function lazyTarget(c) {
|
|
7005
7449
|
if (c._def.tag !== "lazy") return null;
|
|
7006
7450
|
try {
|
|
@@ -7067,15 +7511,15 @@ function evalRuleMapIR(ir) {
|
|
|
7067
7511
|
if (Array.isArray(it)) {
|
|
7068
7512
|
const [src, comb] = it;
|
|
7069
7513
|
gateSrcs.push(src);
|
|
7070
|
-
let
|
|
7514
|
+
let gate2;
|
|
7071
7515
|
try {
|
|
7072
|
-
|
|
7516
|
+
gate2 = (0, eval)(`(${src})`);
|
|
7073
7517
|
} catch {
|
|
7074
|
-
|
|
7518
|
+
gate2 = () => {
|
|
7075
7519
|
throw new Error("IR gate fn not materialized");
|
|
7076
7520
|
};
|
|
7077
7521
|
}
|
|
7078
|
-
return { gate, combinator: comb };
|
|
7522
|
+
return { gate: gate2, combinator: comb };
|
|
7079
7523
|
}
|
|
7080
7524
|
gateSrcs.push(null);
|
|
7081
7525
|
return it;
|
|
@@ -7182,7 +7626,7 @@ var Serializer = class {
|
|
|
7182
7626
|
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
7183
7627
|
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
7184
7628
|
body(c) {
|
|
7185
|
-
return c._def.tag === "lazy" &&
|
|
7629
|
+
return c._def.tag === "lazy" && ruleNameOf2(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
7186
7630
|
}
|
|
7187
7631
|
run() {
|
|
7188
7632
|
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
@@ -7207,7 +7651,7 @@ ${body}
|
|
|
7207
7651
|
analyze(c, active) {
|
|
7208
7652
|
const def = c._def;
|
|
7209
7653
|
if (def.tag === "lazy") {
|
|
7210
|
-
if (
|
|
7654
|
+
if (ruleNameOf2(c) !== void 0) return;
|
|
7211
7655
|
const target = lazyTarget(c);
|
|
7212
7656
|
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
7213
7657
|
if (active.has(target)) {
|
|
@@ -7259,7 +7703,7 @@ ${body}
|
|
|
7259
7703
|
const kid = (x) => this.ref(x, selfOf);
|
|
7260
7704
|
switch (def.tag) {
|
|
7261
7705
|
case "lazy": {
|
|
7262
|
-
if (
|
|
7706
|
+
if (ruleNameOf2(c) !== void 0) return `g[${JSON.stringify(ruleNameOf2(c))}]`;
|
|
7263
7707
|
const target = lazyTarget(c);
|
|
7264
7708
|
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
7265
7709
|
return this.ref(target, selfOf);
|
|
@@ -7313,7 +7757,7 @@ ${body}
|
|
|
7313
7757
|
case "skip":
|
|
7314
7758
|
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
7315
7759
|
case "scanTo":
|
|
7316
|
-
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
7760
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}]${def.raw ? ", raw: true" : ""}, orEOF: ${def.orEOF} })`;
|
|
7317
7761
|
case "transform": {
|
|
7318
7762
|
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
7319
7763
|
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
|
|
@@ -7376,13 +7820,25 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
|
|
|
7376
7820
|
return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
|
|
7377
7821
|
}
|
|
7378
7822
|
const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
|
|
7379
|
-
const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
|
|
7823
|
+
const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
|
|
7380
7824
|
host._parsemanCstOutput = true;
|
|
7381
7825
|
if (collapse) host._parsemanCstCollapse = collapse;
|
|
7382
7826
|
return host;
|
|
7383
7827
|
}
|
|
7384
7828
|
cstBuildHost._parsemanCstOutput = true;
|
|
7385
7829
|
function fusedBody(pieces) {
|
|
7830
|
+
for (const p of pieces) {
|
|
7831
|
+
if (p.v === void 0) {
|
|
7832
|
+
throw new Error(
|
|
7833
|
+
`parseman: artifact "${p.ns}" is UNSTAMPED (compiled before the version-lock invariant). Recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse unversioned or cross-version artifacts.`
|
|
7834
|
+
);
|
|
7835
|
+
}
|
|
7836
|
+
if (p.v !== PARSEMAN_VERSION) {
|
|
7837
|
+
throw new Error(
|
|
7838
|
+
`parseman: artifact "${p.ns}" was compiled with parseman ${p.v}, but is being fused with parseman ${PARSEMAN_VERSION}. Compiled grammar artifacts are version-locked \u2014 recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse across versions.`
|
|
7839
|
+
);
|
|
7840
|
+
}
|
|
7841
|
+
}
|
|
7386
7842
|
const winner = /* @__PURE__ */ new Map();
|
|
7387
7843
|
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
7388
7844
|
for (const [k, p] of winner) {
|
|
@@ -7408,23 +7864,38 @@ function fusedBody(pieces) {
|
|
|
7408
7864
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
7409
7865
|
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
7410
7866
|
const ANY2 = { kind: "any" };
|
|
7867
|
+
const EMPTY3 = { kind: "empty" };
|
|
7411
7868
|
const recipes = /* @__PURE__ */ new Map();
|
|
7412
7869
|
const shallow = /* @__PURE__ */ new Map();
|
|
7870
|
+
const nullable2 = /* @__PURE__ */ new Map();
|
|
7413
7871
|
for (const [k, p] of winner) {
|
|
7414
7872
|
const r = p.firstSetRecipes?.get(k);
|
|
7415
7873
|
if (r) recipes.set(k, r);
|
|
7416
7874
|
const fs = p.firstSets?.get(k);
|
|
7417
7875
|
if (fs) shallow.set(k, fs);
|
|
7418
|
-
|
|
7876
|
+
const nu = p.nullable?.get(k);
|
|
7877
|
+
if (nu !== void 0) nullable2.set(k, nu);
|
|
7878
|
+
}
|
|
7879
|
+
const knownRef = (ref2) => recipes.has(ref2) || shallow.has(ref2);
|
|
7880
|
+
const refFS = (ref2) => knownRef(ref2) ? finalFS.get(ref2) ?? ANY2 : ANY2;
|
|
7881
|
+
const refNullable = (ref2) => knownRef(ref2) ? nullable2.get(ref2) ?? true : true;
|
|
7882
|
+
const resolveRecipe = (r) => {
|
|
7883
|
+
let out = EMPTY3;
|
|
7884
|
+
for (const chain of r.alts) {
|
|
7885
|
+
for (const seg of chain) {
|
|
7886
|
+
out = union(out, seg.ref !== void 0 ? refFS(seg.ref) : seg.set);
|
|
7887
|
+
const skippable = seg.ref !== void 0 ? seg.nullable || refNullable(seg.ref) : seg.nullable;
|
|
7888
|
+
if (!skippable) break;
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
return out;
|
|
7892
|
+
};
|
|
7419
7893
|
const finalFS = /* @__PURE__ */ new Map();
|
|
7420
|
-
for (const [k] of winner) finalFS.set(k, recipes.
|
|
7894
|
+
for (const [k] of winner) finalFS.set(k, recipes.has(k) ? EMPTY3 : shallow.get(k) ?? ANY2);
|
|
7421
7895
|
for (let iter = 0; iter <= recipes.size; iter++) {
|
|
7422
7896
|
let changed = false;
|
|
7423
7897
|
for (const [k, r] of recipes) {
|
|
7424
|
-
|
|
7425
|
-
for (const ref2 of r.refs) {
|
|
7426
|
-
fs = union(fs, recipes.has(ref2) || shallow.has(ref2) ? finalFS.get(ref2) ?? ANY2 : ANY2);
|
|
7427
|
-
}
|
|
7898
|
+
const fs = resolveRecipe(r);
|
|
7428
7899
|
if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(fs)) {
|
|
7429
7900
|
finalFS.set(k, fs);
|
|
7430
7901
|
changed = true;
|
|
@@ -7556,8 +8027,8 @@ function composeLeaf(_items) {
|
|
|
7556
8027
|
throw new Error("composeLeaf(): requires Parseman macro lowering; runtime composition is forbidden");
|
|
7557
8028
|
}
|
|
7558
8029
|
|
|
7559
|
-
// src/combinators/
|
|
7560
|
-
function
|
|
8030
|
+
// src/combinators/gate.ts
|
|
8031
|
+
function gate(predicate) {
|
|
7561
8032
|
const meta = {
|
|
7562
8033
|
firstSet: { kind: "any" },
|
|
7563
8034
|
canMatchNewline: false,
|
|
@@ -7575,6 +8046,9 @@ function guard(predicate) {
|
|
|
7575
8046
|
};
|
|
7576
8047
|
}
|
|
7577
8048
|
|
|
8049
|
+
// src/combinators/guard.ts
|
|
8050
|
+
var guard = gate;
|
|
8051
|
+
|
|
7578
8052
|
// src/combinators/completions.ts
|
|
7579
8053
|
function completionsAt(target, input, offset, options = {}) {
|
|
7580
8054
|
const probe = { offset, best: null };
|
|
@@ -8106,12 +8580,14 @@ function runOnce(entry, input, options, phase, profileState) {
|
|
|
8106
8580
|
const profile = profileState ?? (phase === void 0 ? void 0 : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 });
|
|
8107
8581
|
const skipGlobalSinks = phase === "recognizer" || phase === "capture";
|
|
8108
8582
|
const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
|
|
8583
|
+
const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
|
|
8109
8584
|
const ctx = {
|
|
8110
8585
|
trackLines: false,
|
|
8111
8586
|
...skipGlobalSinks ? { _pmProfile: profile } : { _triviaLog: triviaLog, _errors: errors, ...profile === void 0 ? {} : { _pmProfile: profile } },
|
|
8112
8587
|
build: options.build,
|
|
8113
8588
|
state: options.state,
|
|
8114
8589
|
...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
|
|
8590
|
+
...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
|
|
8115
8591
|
...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
|
|
8116
8592
|
...options.tolerant ? { _tolerant: true, _rec: REC } : {},
|
|
8117
8593
|
...options.instrumentation === void 0 ? {} : options.instrumentation
|
|
@@ -8397,7 +8873,7 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
8397
8873
|
return def.type === void 0 ? node(build(def.parser), def.build, opts) : node(def.type, build(def.parser), def.build, opts);
|
|
8398
8874
|
}
|
|
8399
8875
|
case "guard":
|
|
8400
|
-
return
|
|
8876
|
+
return gate(def.predicate);
|
|
8401
8877
|
case "withCtx":
|
|
8402
8878
|
return withCtx(def.extra, build(def.parser));
|
|
8403
8879
|
case "expect":
|
|
@@ -8452,9 +8928,9 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
8452
8928
|
if (prev) merge(after, prev.span.end, run2);
|
|
8453
8929
|
}
|
|
8454
8930
|
}
|
|
8455
|
-
const
|
|
8456
|
-
if (
|
|
8457
|
-
for (const c of
|
|
8931
|
+
const ch2 = node2.children;
|
|
8932
|
+
if (ch2) {
|
|
8933
|
+
for (const c of ch2) {
|
|
8458
8934
|
if (typeof c === "object" && c !== null && c.children !== void 0) {
|
|
8459
8935
|
visit(c);
|
|
8460
8936
|
}
|
|
@@ -8765,6 +9241,7 @@ export {
|
|
|
8765
9241
|
absoluteSpanCST,
|
|
8766
9242
|
absolutize,
|
|
8767
9243
|
absolutizeCST,
|
|
9244
|
+
analyzeGating,
|
|
8768
9245
|
annotateSpan,
|
|
8769
9246
|
applyEdit,
|
|
8770
9247
|
attempt,
|
|
@@ -8789,8 +9266,11 @@ export {
|
|
|
8789
9266
|
cstBuildHost,
|
|
8790
9267
|
expect,
|
|
8791
9268
|
field,
|
|
9269
|
+
firstSetToString,
|
|
9270
|
+
formatGatingWarnings,
|
|
8792
9271
|
gapIsSignificant,
|
|
8793
9272
|
gapText,
|
|
9273
|
+
gate,
|
|
8794
9274
|
grammarCoverageDefinitions,
|
|
8795
9275
|
guard,
|
|
8796
9276
|
indentMixed,
|