parseman 0.44.0 → 0.46.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/choice-cost-gate.d.ts +158 -0
- package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
- package/dist/analysis/choice-cost-render.d.ts +88 -0
- package/dist/analysis/choice-cost-render.d.ts.map +1 -0
- package/dist/analysis/choice-cost.d.ts +383 -0
- package/dist/analysis/choice-cost.d.ts.map +1 -0
- package/dist/analysis/corpus.d.ts +73 -0
- package/dist/analysis/corpus.d.ts.map +1 -0
- package/dist/analysis/diagnose-render.d.ts +65 -0
- package/dist/analysis/diagnose-render.d.ts.map +1 -0
- package/dist/analysis/diagnose.d.ts +133 -0
- package/dist/analysis/diagnose.d.ts.map +1 -0
- package/dist/analysis/diagnostics.cjs +11272 -0
- package/dist/analysis/diagnostics.cjs.map +7 -0
- package/dist/analysis/diagnostics.d.ts +27 -0
- package/dist/analysis/diagnostics.d.ts.map +1 -0
- package/dist/analysis/diagnostics.js +11249 -0
- package/dist/analysis/diagnostics.js.map +7 -0
- package/dist/analysis/fix-render.d.ts +43 -0
- package/dist/analysis/fix-render.d.ts.map +1 -0
- package/dist/analysis/fix.d.ts +172 -0
- package/dist/analysis/fix.d.ts.map +1 -0
- package/dist/analysis/gating.d.ts +17 -9
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/analysis/grammar.d.ts.map +1 -1
- package/dist/analysis/rebuild.d.ts +61 -0
- package/dist/analysis/rebuild.d.ts.map +1 -0
- package/dist/analysis/terminal.d.ts +142 -0
- package/dist/analysis/terminal.d.ts.map +1 -0
- package/dist/cli/index.cjs +12507 -0
- package/dist/cli/index.cjs.map +7 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +12484 -0
- package/dist/cli/index.js.map +7 -0
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +19 -1
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +19 -0
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/compiler/build-arity.d.ts +28 -0
- package/dist/compiler/build-arity.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +80 -60
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/degradation.d.ts +123 -0
- package/dist/compiler/degradation.d.ts.map +1 -0
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/inline-build.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +45 -7
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/module-hoist.d.ts +106 -0
- package/dist/compiler/module-hoist.d.ts.map +1 -0
- package/dist/coverage.d.ts +13 -0
- package/dist/coverage.d.ts.map +1 -1
- package/dist/cst/reflection.d.ts.map +1 -1
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/index.cjs +1846 -610
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +11 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1846 -610
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs +40 -30
- package/dist/language-service/index.cjs.map +3 -3
- package/dist/language-service/index.js +40 -30
- package/dist/language-service/index.js.map +3 -3
- package/dist/oracle/digest.d.ts +132 -6
- package/dist/oracle/digest.d.ts.map +1 -1
- package/dist/oracle/index.cjs +136 -305
- package/dist/oracle/index.cjs.map +4 -4
- package/dist/oracle/index.d.ts +43 -31
- package/dist/oracle/index.d.ts.map +1 -1
- package/dist/oracle/index.js +136 -305
- package/dist/oracle/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +3 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/free-identifiers.d.ts +23 -0
- package/dist/plugin/free-identifiers.d.ts.map +1 -0
- package/dist/plugin/index.cjs +1529 -763
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts +18 -0
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1529 -763
- package/dist/plugin/index.js.map +4 -4
- package/dist/plugin/reducer-resolver.d.ts +57 -0
- package/dist/plugin/reducer-resolver.d.ts.map +1 -0
- package/dist/run/index.cjs +33 -30
- package/dist/run/index.cjs.map +2 -2
- package/dist/run/index.js +33 -30
- package/dist/run/index.js.map +2 -2
- package/dist/spec/index.cjs +222 -34
- package/dist/spec/index.cjs.map +4 -4
- package/dist/spec/index.js +222 -34
- package/dist/spec/index.js.map +4 -4
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +23 -4
- package/dist/combinators/guard.d.ts +0 -8
- package/dist/combinators/guard.d.ts.map +0 -1
- package/dist/cst/offset-model.d.ts +0 -119
- package/dist/cst/offset-model.d.ts.map +0 -1
- package/dist/oracle/corpus.d.ts +0 -32
- package/dist/oracle/corpus.d.ts.map +0 -1
- package/dist/oracle/identity.d.ts +0 -117
- package/dist/oracle/identity.d.ts.map +0 -1
package/dist/plugin/index.cjs
CHANGED
|
@@ -34,18 +34,18 @@ __export(plugin_exports, {
|
|
|
34
34
|
transformMacro: () => transformMacro
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(plugin_exports);
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var fs2 = __toESM(require("node:fs"), 1);
|
|
38
|
+
var path2 = __toESM(require("node:path"), 1);
|
|
39
39
|
var import_unplugin = require("unplugin");
|
|
40
|
-
var
|
|
40
|
+
var import_oxc_parser4 = require("oxc-parser");
|
|
41
41
|
var import_oxc_resolver = require("oxc-resolver");
|
|
42
42
|
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
43
43
|
|
|
44
44
|
// src/combinators/first-set.ts
|
|
45
|
-
function resolveNamedRef(p,
|
|
46
|
-
if (
|
|
45
|
+
function resolveNamedRef(p, resolve3) {
|
|
46
|
+
if (resolve3 === void 0) return void 0;
|
|
47
47
|
const name = p._ruleName;
|
|
48
|
-
return name === void 0 ? void 0 :
|
|
48
|
+
return name === void 0 ? void 0 : resolve3(name);
|
|
49
49
|
}
|
|
50
50
|
function union(a, b) {
|
|
51
51
|
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
@@ -67,12 +67,12 @@ function fromChar(code) {
|
|
|
67
67
|
return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
|
|
68
68
|
}
|
|
69
69
|
function startsFirstSet(combinator, input, pos) {
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
if (
|
|
70
|
+
const fs3 = combinator._meta.firstSet;
|
|
71
|
+
if (fs3.kind === "any") return true;
|
|
72
|
+
if (fs3.kind === "empty") return false;
|
|
73
73
|
const code = input.codePointAt(pos);
|
|
74
74
|
if (code === void 0) return false;
|
|
75
|
-
for (const r of
|
|
75
|
+
for (const r of fs3.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
76
76
|
return false;
|
|
77
77
|
}
|
|
78
78
|
function fromRange(lo, hi) {
|
|
@@ -84,18 +84,19 @@ function any() {
|
|
|
84
84
|
function empty() {
|
|
85
85
|
return { kind: "empty" };
|
|
86
86
|
}
|
|
87
|
-
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(),
|
|
87
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve3) {
|
|
88
88
|
if (seen.has(p)) return true;
|
|
89
89
|
seen.add(p);
|
|
90
|
-
const me = (c) => matchesEmpty(c, seen,
|
|
90
|
+
const me = (c) => matchesEmpty(c, seen, resolve3);
|
|
91
91
|
const d = p._def;
|
|
92
92
|
switch (d.tag) {
|
|
93
93
|
case "literal":
|
|
94
94
|
return d.value.length === 0;
|
|
95
95
|
case "keywords":
|
|
96
96
|
return false;
|
|
97
|
+
// The routed token itself is the selector's match, never empty; a fallback can be.
|
|
97
98
|
case "routed":
|
|
98
|
-
return false;
|
|
99
|
+
return d.fallback === void 0 ? false : me(d.fallback);
|
|
99
100
|
case "regex":
|
|
100
101
|
try {
|
|
101
102
|
const m = new RegExp(d.source).exec("");
|
|
@@ -141,7 +142,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
141
142
|
try {
|
|
142
143
|
return me(d.thunk());
|
|
143
144
|
} catch {
|
|
144
|
-
const t = resolveNamedRef(p,
|
|
145
|
+
const t = resolveNamedRef(p, resolve3);
|
|
145
146
|
return t ? me(t) : true;
|
|
146
147
|
}
|
|
147
148
|
default:
|
|
@@ -167,29 +168,29 @@ function narrowBy(acc, constraint) {
|
|
|
167
168
|
}
|
|
168
169
|
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
169
170
|
}
|
|
170
|
-
function applyAssertion(
|
|
171
|
-
if (assertion === null) return
|
|
172
|
-
if (
|
|
173
|
-
return narrowBy(
|
|
171
|
+
function applyAssertion(fs3, assertion) {
|
|
172
|
+
if (assertion === null) return fs3;
|
|
173
|
+
if (fs3.kind === "empty") return assertion;
|
|
174
|
+
return narrowBy(fs3, assertion) ?? fs3;
|
|
174
175
|
}
|
|
175
176
|
function sequenceFirstSet(parsers) {
|
|
176
|
-
let
|
|
177
|
+
let fs3 = empty();
|
|
177
178
|
let assertion = null;
|
|
178
179
|
for (const p of parsers) {
|
|
179
180
|
if (isPositiveLookahead(p)) {
|
|
180
181
|
assertion = narrowBy(assertion, p._meta.firstSet);
|
|
181
182
|
continue;
|
|
182
183
|
}
|
|
183
|
-
if (!isZeroWidthAssertion(p))
|
|
184
|
-
if (!matchesEmpty(p)) return applyAssertion(
|
|
184
|
+
if (!isZeroWidthAssertion(p)) fs3 = union(fs3, p._meta.firstSet);
|
|
185
|
+
if (!matchesEmpty(p)) return applyAssertion(fs3, assertion);
|
|
185
186
|
}
|
|
186
|
-
return applyAssertion(
|
|
187
|
+
return applyAssertion(fs3, assertion);
|
|
187
188
|
}
|
|
188
|
-
function firstSetOf(p, seen = /* @__PURE__ */ new Set(),
|
|
189
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve3) {
|
|
189
190
|
if (seen.has(p)) return any();
|
|
190
191
|
seen.add(p);
|
|
191
|
-
const
|
|
192
|
-
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(),
|
|
192
|
+
const fs3 = (c) => firstSetOf(c, seen, resolve3);
|
|
193
|
+
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve3);
|
|
193
194
|
const d = p._def;
|
|
194
195
|
switch (d.tag) {
|
|
195
196
|
case "literal":
|
|
@@ -198,17 +199,17 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
198
199
|
return p._meta.firstSet;
|
|
199
200
|
// terminals: no refs, cached set is exact
|
|
200
201
|
case "dispatch":
|
|
201
|
-
return
|
|
202
|
+
return fs3(d.selector);
|
|
202
203
|
case "lazy":
|
|
203
204
|
try {
|
|
204
|
-
return
|
|
205
|
+
return fs3(d.thunk());
|
|
205
206
|
} catch {
|
|
206
|
-
const t = resolveNamedRef(p,
|
|
207
|
-
return t ?
|
|
207
|
+
const t = resolveNamedRef(p, resolve3);
|
|
208
|
+
return t ? fs3(t) : any();
|
|
208
209
|
}
|
|
209
210
|
case "choice": {
|
|
210
211
|
let out = empty();
|
|
211
|
-
for (const arm of d.parsers) out = union(out,
|
|
212
|
+
for (const arm of d.parsers) out = union(out, fs3(arm));
|
|
212
213
|
return out;
|
|
213
214
|
}
|
|
214
215
|
case "sequence": {
|
|
@@ -216,17 +217,17 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
216
217
|
let assertion = null;
|
|
217
218
|
for (const term of d.parsers) {
|
|
218
219
|
if (isPositiveLookahead(term)) {
|
|
219
|
-
assertion = narrowBy(assertion,
|
|
220
|
+
assertion = narrowBy(assertion, fs3(term));
|
|
220
221
|
continue;
|
|
221
222
|
}
|
|
222
|
-
if (!isZeroWidthAssertion(term)) out = union(out,
|
|
223
|
+
if (!isZeroWidthAssertion(term)) out = union(out, fs3(term));
|
|
223
224
|
if (!empties(term)) return applyAssertion(out, assertion);
|
|
224
225
|
}
|
|
225
226
|
return applyAssertion(out, assertion);
|
|
226
227
|
}
|
|
227
228
|
case "peek": {
|
|
228
229
|
const inner = d.parser;
|
|
229
|
-
return empties(inner) ? any() :
|
|
230
|
+
return empties(inner) ? any() : fs3(inner);
|
|
230
231
|
}
|
|
231
232
|
case "oneOrMore":
|
|
232
233
|
case "many":
|
|
@@ -239,12 +240,12 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
239
240
|
case "node":
|
|
240
241
|
case "grammar":
|
|
241
242
|
case "expect":
|
|
242
|
-
return
|
|
243
|
+
return fs3(d.parser);
|
|
243
244
|
case "sepBy":
|
|
244
|
-
return
|
|
245
|
+
return fs3(d.parser);
|
|
245
246
|
// both min 0 and min 1 start with the item
|
|
246
247
|
case "skip":
|
|
247
|
-
return
|
|
248
|
+
return fs3(d.main);
|
|
248
249
|
default:
|
|
249
250
|
return p._meta.firstSet;
|
|
250
251
|
}
|
|
@@ -715,13 +716,13 @@ function shortScanner(source, flags) {
|
|
|
715
716
|
return minOne && end === pos ? null : end;
|
|
716
717
|
};
|
|
717
718
|
}
|
|
718
|
-
function asciiCaseFold(
|
|
719
|
-
if (
|
|
720
|
-
const ranges =
|
|
719
|
+
function asciiCaseFold(fs3) {
|
|
720
|
+
if (fs3.kind !== "ranges") return fs3;
|
|
721
|
+
const ranges = fs3.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
|
|
721
722
|
const add = (lo, hi) => {
|
|
722
723
|
if (lo <= hi) ranges.push({ lo, hi });
|
|
723
724
|
};
|
|
724
|
-
for (const r of
|
|
725
|
+
for (const r of fs3.ranges) {
|
|
725
726
|
const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
|
|
726
727
|
add(uLo + 32, uHi + 32);
|
|
727
728
|
const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
|
|
@@ -779,9 +780,9 @@ function charNode(set, nl) {
|
|
|
779
780
|
return { t: "char", set, nl };
|
|
780
781
|
}
|
|
781
782
|
function rangesToSet(ranges) {
|
|
782
|
-
let
|
|
783
|
-
for (const [lo, hi] of ranges)
|
|
784
|
-
return
|
|
783
|
+
let fs3 = empty();
|
|
784
|
+
for (const [lo, hi] of ranges) fs3 = union(fs3, fromRange(lo, hi));
|
|
785
|
+
return fs3;
|
|
785
786
|
}
|
|
786
787
|
function rangesHaveNewline(ranges) {
|
|
787
788
|
return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
|
|
@@ -987,17 +988,17 @@ function firstSet(n) {
|
|
|
987
988
|
case "rep":
|
|
988
989
|
return firstSet(n.node);
|
|
989
990
|
case "alt": {
|
|
990
|
-
let
|
|
991
|
-
for (const arm of n.arms)
|
|
992
|
-
return
|
|
991
|
+
let fs3 = empty();
|
|
992
|
+
for (const arm of n.arms) fs3 = union(fs3, firstSet(arm));
|
|
993
|
+
return fs3;
|
|
993
994
|
}
|
|
994
995
|
case "seq": {
|
|
995
|
-
let
|
|
996
|
+
let fs3 = empty();
|
|
996
997
|
for (const part of n.parts) {
|
|
997
|
-
|
|
998
|
+
fs3 = union(fs3, firstSet(part));
|
|
998
999
|
if (!nullable(part)) break;
|
|
999
1000
|
}
|
|
1000
|
-
return
|
|
1001
|
+
return fs3;
|
|
1001
1002
|
}
|
|
1002
1003
|
}
|
|
1003
1004
|
}
|
|
@@ -1526,8 +1527,8 @@ function classifyTriviaArm(arm) {
|
|
|
1526
1527
|
}
|
|
1527
1528
|
const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
|
|
1528
1529
|
if (lc) {
|
|
1529
|
-
const
|
|
1530
|
-
return { kind: "untilLineBreak", code: (
|
|
1530
|
+
const marker2 = lc[1];
|
|
1531
|
+
return { kind: "untilLineBreak", code: (marker2.length === 2 ? marker2[1] : marker2[0]).charCodeAt(0) };
|
|
1531
1532
|
}
|
|
1532
1533
|
return null;
|
|
1533
1534
|
}
|
|
@@ -1672,12 +1673,12 @@ function orSentinel(a, b) {
|
|
|
1672
1673
|
}
|
|
1673
1674
|
};
|
|
1674
1675
|
}
|
|
1675
|
-
function firstSetSentinel(
|
|
1676
|
-
if (
|
|
1677
|
-
const ranges =
|
|
1676
|
+
function firstSetSentinel(fs3) {
|
|
1677
|
+
if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return null;
|
|
1678
|
+
const ranges = fs3.ranges;
|
|
1678
1679
|
return {
|
|
1679
1680
|
_tag: "firstSetSentinel",
|
|
1680
|
-
_meta: { firstSet:
|
|
1681
|
+
_meta: { firstSet: fs3, canMatchNewline: false, isTrivia: false },
|
|
1681
1682
|
_def: { tag: "unknown" },
|
|
1682
1683
|
parse(input, pos) {
|
|
1683
1684
|
const code = pos < input.length ? input.codePointAt(pos) : void 0;
|
|
@@ -1719,11 +1720,11 @@ function sequence(...parsers) {
|
|
|
1719
1720
|
let followSentinels;
|
|
1720
1721
|
function parseTolerant(input, pos, ctx) {
|
|
1721
1722
|
followSentinels ??= parsers.map((_, i) => {
|
|
1722
|
-
const
|
|
1723
|
+
const fs3 = parsers.slice(i + 1).reduce(
|
|
1723
1724
|
(acc, p) => union(acc, firstSetOf(p)),
|
|
1724
1725
|
{ kind: "empty" }
|
|
1725
1726
|
);
|
|
1726
|
-
return firstSetSentinel(
|
|
1727
|
+
return firstSetSentinel(fs3);
|
|
1727
1728
|
});
|
|
1728
1729
|
const values = def.valueUnused ? void 0 : [];
|
|
1729
1730
|
let cur = pos;
|
|
@@ -1866,8 +1867,11 @@ function derive(c, seen) {
|
|
|
1866
1867
|
case "not":
|
|
1867
1868
|
case "peek":
|
|
1868
1869
|
return deriveExpected2(def.parser);
|
|
1870
|
+
// A bare routed() can only fail as "there is no routed token here". With a
|
|
1871
|
+
// fallback, THAT is the branch that can fail, so its expectations are the useful
|
|
1872
|
+
// ones — the routed path either succeeds or is not taken.
|
|
1869
1873
|
case "routed":
|
|
1870
|
-
return ["routed()"];
|
|
1874
|
+
return def.fallback === void 0 ? ["routed()"] : deriveExpected2(def.fallback);
|
|
1871
1875
|
case "lazy": {
|
|
1872
1876
|
const name = c._ruleName;
|
|
1873
1877
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -2054,7 +2058,7 @@ function detectStrategy(parsers) {
|
|
|
2054
2058
|
function bareLeadingTermKey(term) {
|
|
2055
2059
|
const d = term._def;
|
|
2056
2060
|
if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
|
|
2057
|
-
if (d.tag === "regex") return `R:${d.source
|
|
2061
|
+
if (d.tag === "regex") return `R:${JSON.stringify([d.source, d.flags])}`;
|
|
2058
2062
|
return null;
|
|
2059
2063
|
}
|
|
2060
2064
|
function detectSharedPrefix(parsers) {
|
|
@@ -2170,10 +2174,10 @@ function codesToFirstSet(codes) {
|
|
|
2170
2174
|
ranges.push({ lo, hi });
|
|
2171
2175
|
return { kind: "ranges", ranges };
|
|
2172
2176
|
}
|
|
2173
|
-
function inFirstSet(code,
|
|
2174
|
-
if (
|
|
2175
|
-
if (
|
|
2176
|
-
for (const r of
|
|
2177
|
+
function inFirstSet(code, fs3) {
|
|
2178
|
+
if (fs3.kind === "any") return true;
|
|
2179
|
+
if (fs3.kind === "empty") return false;
|
|
2180
|
+
for (const r of fs3.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
2177
2181
|
return false;
|
|
2178
2182
|
}
|
|
2179
2183
|
function areDisjoint(sets) {
|
|
@@ -2186,9 +2190,9 @@ function areDisjoint(sets) {
|
|
|
2186
2190
|
function buildAsciiDispatch(parsers) {
|
|
2187
2191
|
const table = Array(128).fill(-1);
|
|
2188
2192
|
for (let i = 0; i < parsers.length; i++) {
|
|
2189
|
-
const
|
|
2190
|
-
if (
|
|
2191
|
-
for (const { lo, hi } of
|
|
2193
|
+
const fs3 = parsers[i]._meta.firstSet;
|
|
2194
|
+
if (fs3.kind !== "ranges") continue;
|
|
2195
|
+
for (const { lo, hi } of fs3.ranges) {
|
|
2192
2196
|
for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
|
|
2193
2197
|
table[code] = i;
|
|
2194
2198
|
}
|
|
@@ -2309,17 +2313,21 @@ function when(key, parser2, opts = {}) {
|
|
|
2309
2313
|
function otherwise(parser2) {
|
|
2310
2314
|
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2311
2315
|
}
|
|
2312
|
-
function routed() {
|
|
2316
|
+
function routed(fallback) {
|
|
2313
2317
|
return {
|
|
2314
2318
|
_tag: "routed",
|
|
2315
|
-
_meta: {
|
|
2316
|
-
|
|
2319
|
+
_meta: {
|
|
2320
|
+
// The routed token is whatever the selector matched, so the first set stays
|
|
2321
|
+
// `any` even with a fallback — `any` already subsumes the fallback's.
|
|
2322
|
+
firstSet: { kind: "any" },
|
|
2323
|
+
canMatchNewline: fallback?._meta.canMatchNewline ?? false,
|
|
2324
|
+
isTrivia: false
|
|
2325
|
+
},
|
|
2326
|
+
_def: fallback === void 0 ? { tag: "routed" } : { tag: "routed", fallback },
|
|
2317
2327
|
parse(_input, pos, ctx) {
|
|
2318
2328
|
const item = ctx._routed;
|
|
2319
|
-
if (item === void 0) {
|
|
2320
|
-
|
|
2321
|
-
}
|
|
2322
|
-
if (pos !== item.span.start) {
|
|
2329
|
+
if (item === void 0 || pos !== item.span.start) {
|
|
2330
|
+
if (fallback !== void 0) return fallback.parse(_input, pos, ctx);
|
|
2323
2331
|
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2324
2332
|
}
|
|
2325
2333
|
if (cstCaptureActive(ctx)) {
|
|
@@ -2826,12 +2834,12 @@ function markUnusedValues(root) {
|
|
|
2826
2834
|
}
|
|
2827
2835
|
};
|
|
2828
2836
|
const resolveRoot = (c) => {
|
|
2829
|
-
const
|
|
2837
|
+
const guard = /* @__PURE__ */ new Set();
|
|
2830
2838
|
let cur = c;
|
|
2831
|
-
while (!
|
|
2839
|
+
while (!guard.has(cur)) {
|
|
2832
2840
|
const d = cur._def;
|
|
2833
2841
|
if (d.tag !== "lazy") return cur;
|
|
2834
|
-
|
|
2842
|
+
guard.add(cur);
|
|
2835
2843
|
try {
|
|
2836
2844
|
cur = d.thunk();
|
|
2837
2845
|
} catch {
|
|
@@ -2973,10 +2981,11 @@ function childrenOf(def) {
|
|
|
2973
2981
|
return [def.main, def.skipped];
|
|
2974
2982
|
case "scanTo":
|
|
2975
2983
|
return [def.sentinel, ...def.skip];
|
|
2984
|
+
case "routed":
|
|
2985
|
+
return def.fallback ? [def.fallback] : [];
|
|
2976
2986
|
case "literal":
|
|
2977
2987
|
case "regex":
|
|
2978
2988
|
case "keywords":
|
|
2979
|
-
case "routed":
|
|
2980
2989
|
case "guard":
|
|
2981
2990
|
case "lazy":
|
|
2982
2991
|
case "unknown":
|
|
@@ -3199,54 +3208,276 @@ function peek(combinator) {
|
|
|
3199
3208
|
};
|
|
3200
3209
|
}
|
|
3201
3210
|
|
|
3211
|
+
// src/compiler/degradation.ts
|
|
3212
|
+
function formatDegradation(d) {
|
|
3213
|
+
return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
|
|
3214
|
+
}
|
|
3215
|
+
var DETAIL_CAP = 8;
|
|
3216
|
+
function formatDegradations(list) {
|
|
3217
|
+
const byCode = /* @__PURE__ */ new Map();
|
|
3218
|
+
for (const d of list) {
|
|
3219
|
+
const bucket = byCode.get(d.code);
|
|
3220
|
+
if (bucket) bucket.push(d);
|
|
3221
|
+
else byCode.set(d.code, [d]);
|
|
3222
|
+
}
|
|
3223
|
+
const out = [];
|
|
3224
|
+
for (const [code, items] of byCode) {
|
|
3225
|
+
for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
|
|
3226
|
+
if (items.length > DETAIL_CAP) {
|
|
3227
|
+
out.push(
|
|
3228
|
+
`[parseman] degraded [${code}] +${items.length - DETAIL_CAP} more site(s) not listed (${items.length} total). Set PARSEMAN_DEGRADATION=error to fail the build on these.`
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
return out;
|
|
3233
|
+
}
|
|
3234
|
+
var _sinks = [];
|
|
3235
|
+
function beginDegradationCapture() {
|
|
3236
|
+
_sinks.push(/* @__PURE__ */ new Map());
|
|
3237
|
+
}
|
|
3238
|
+
function endDegradationCapture() {
|
|
3239
|
+
const sink = _sinks.pop();
|
|
3240
|
+
return sink ? [...sink.values()] : [];
|
|
3241
|
+
}
|
|
3242
|
+
function degradationCaptureDepth() {
|
|
3243
|
+
return _sinks.length;
|
|
3244
|
+
}
|
|
3245
|
+
function unwindDegradationCapture(depth) {
|
|
3246
|
+
const found = [];
|
|
3247
|
+
while (_sinks.length > depth) {
|
|
3248
|
+
const sink = _sinks.pop();
|
|
3249
|
+
found.unshift(...sink.values());
|
|
3250
|
+
}
|
|
3251
|
+
return found;
|
|
3252
|
+
}
|
|
3253
|
+
function resolveDegradationLevel(explicit) {
|
|
3254
|
+
if (explicit !== void 0) return explicit;
|
|
3255
|
+
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
|
|
3256
|
+
if (env === "off" || env === "warn" || env === "error") return env;
|
|
3257
|
+
return "warn";
|
|
3258
|
+
}
|
|
3259
|
+
function degradationKey(d) {
|
|
3260
|
+
return JSON.stringify([d.code, d.where, d.subject]);
|
|
3261
|
+
}
|
|
3262
|
+
function recordDegradation(d) {
|
|
3263
|
+
const level = resolveDegradationLevel();
|
|
3264
|
+
if (level === "off") return;
|
|
3265
|
+
const key = degradationKey(d);
|
|
3266
|
+
const sink = _sinks[_sinks.length - 1];
|
|
3267
|
+
if (sink) {
|
|
3268
|
+
if (!sink.has(key)) sink.set(key, d);
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
if (_immediate.has(key)) return;
|
|
3272
|
+
_immediate.add(key);
|
|
3273
|
+
if (level === "error") throw new Error(`parseman: degraded compilation path
|
|
3274
|
+
${formatDegradation(d)}`);
|
|
3275
|
+
console.warn(formatDegradation(d));
|
|
3276
|
+
}
|
|
3277
|
+
var _immediate = /* @__PURE__ */ new Set();
|
|
3278
|
+
function beginCompileDegradationDrain() {
|
|
3279
|
+
if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
|
|
3280
|
+
};
|
|
3281
|
+
const depth = degradationCaptureDepth();
|
|
3282
|
+
beginDegradationCapture();
|
|
3283
|
+
return (report) => {
|
|
3284
|
+
const found = unwindDegradationCapture(depth);
|
|
3285
|
+
if (!report || found.length === 0) return;
|
|
3286
|
+
const fresh = found.filter((d) => {
|
|
3287
|
+
const k = degradationKey(d);
|
|
3288
|
+
if (_immediate.has(k)) return false;
|
|
3289
|
+
_immediate.add(k);
|
|
3290
|
+
return true;
|
|
3291
|
+
});
|
|
3292
|
+
if (fresh.length === 0) return;
|
|
3293
|
+
const lines = formatDegradations(fresh);
|
|
3294
|
+
if (resolveDegradationLevel() === "error") {
|
|
3295
|
+
throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
|
|
3296
|
+
${lines.join("\n")}`);
|
|
3297
|
+
}
|
|
3298
|
+
for (const l of lines) console.warn(l);
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3202
3302
|
// src/compiler/build-arity.ts
|
|
3203
3303
|
function cstOutputHost(build) {
|
|
3204
3304
|
return build?._parsemanCstOutput === true;
|
|
3205
3305
|
}
|
|
3206
|
-
var
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
|
|
3306
|
+
var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
|
|
3307
|
+
var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
|
|
3308
|
+
var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
|
|
3309
|
+
var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
|
|
3211
3310
|
function confirmedBuildArity(src) {
|
|
3212
3311
|
const s = src.trim();
|
|
3213
|
-
|
|
3214
|
-
if (
|
|
3215
|
-
if (
|
|
3216
|
-
const inner = (
|
|
3217
|
-
if (inner ===
|
|
3218
|
-
|
|
3312
|
+
if (NATIVE_CODE_RE.test(s)) return null;
|
|
3313
|
+
if (/\barguments\b/.test(s)) return null;
|
|
3314
|
+
if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
|
|
3315
|
+
const inner = paramListText(s);
|
|
3316
|
+
if (inner === null) return null;
|
|
3317
|
+
if (inner.trim() === "") return 0;
|
|
3318
|
+
const parts = splitTopLevel(inner);
|
|
3319
|
+
if (parts === null) return null;
|
|
3219
3320
|
for (const part of parts) {
|
|
3220
|
-
if (!
|
|
3321
|
+
if (!isConfirmableParam(part.trim())) return null;
|
|
3221
3322
|
}
|
|
3222
|
-
if (/\barguments\b/.test(s)) return null;
|
|
3223
3323
|
return parts.length;
|
|
3224
3324
|
}
|
|
3325
|
+
function paramListText(s) {
|
|
3326
|
+
const open = PARAM_LIST_OPEN_RE.exec(s);
|
|
3327
|
+
if (!open) return null;
|
|
3328
|
+
const start = open[0].length;
|
|
3329
|
+
const end = matchingParen(s, start);
|
|
3330
|
+
if (end === null) return null;
|
|
3331
|
+
if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
|
|
3332
|
+
return s.slice(start, end);
|
|
3333
|
+
}
|
|
3334
|
+
function matchingParen(s, start) {
|
|
3335
|
+
let depth = 0;
|
|
3336
|
+
for (let i = start; i < s.length; i++) {
|
|
3337
|
+
const c = s[i];
|
|
3338
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
3339
|
+
const close = skipString(s, i);
|
|
3340
|
+
if (close === null) return null;
|
|
3341
|
+
i = close;
|
|
3342
|
+
continue;
|
|
3343
|
+
}
|
|
3344
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
3345
|
+
else if (c === "]" || c === "}") {
|
|
3346
|
+
if (--depth < 0) return null;
|
|
3347
|
+
} else if (c === ")") {
|
|
3348
|
+
if (depth === 0) return i;
|
|
3349
|
+
depth--;
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
return null;
|
|
3353
|
+
}
|
|
3354
|
+
function skipString(s, open) {
|
|
3355
|
+
const quote = s[open];
|
|
3356
|
+
for (let i = open + 1; i < s.length; i++) {
|
|
3357
|
+
if (s[i] === "\\") {
|
|
3358
|
+
i++;
|
|
3359
|
+
continue;
|
|
3360
|
+
}
|
|
3361
|
+
if (s[i] === quote) return i;
|
|
3362
|
+
}
|
|
3363
|
+
return null;
|
|
3364
|
+
}
|
|
3365
|
+
function splitTopLevel(inner) {
|
|
3366
|
+
const parts = [];
|
|
3367
|
+
let depth = 0;
|
|
3368
|
+
let angle = 0;
|
|
3369
|
+
let last = 0;
|
|
3370
|
+
for (let i = 0; i < inner.length; i++) {
|
|
3371
|
+
const c = inner[i];
|
|
3372
|
+
if (c === "=" && inner[i + 1] === ">") {
|
|
3373
|
+
i++;
|
|
3374
|
+
continue;
|
|
3375
|
+
}
|
|
3376
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
3377
|
+
const close = skipString(inner, i);
|
|
3378
|
+
if (close === null) return null;
|
|
3379
|
+
i = close;
|
|
3380
|
+
continue;
|
|
3381
|
+
}
|
|
3382
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
3383
|
+
else if (c === ")" || c === "]" || c === "}") {
|
|
3384
|
+
if (--depth < 0) return null;
|
|
3385
|
+
} else if (c === "<") angle++;
|
|
3386
|
+
else if (c === ">") {
|
|
3387
|
+
if (--angle < 0) return null;
|
|
3388
|
+
} else if (c === "," && depth === 0 && angle === 0) {
|
|
3389
|
+
parts.push(inner.slice(last, i));
|
|
3390
|
+
last = i + 1;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
if (depth !== 0 || angle !== 0) return null;
|
|
3394
|
+
parts.push(inner.slice(last));
|
|
3395
|
+
return parts;
|
|
3396
|
+
}
|
|
3397
|
+
function isConfirmableParam(part) {
|
|
3398
|
+
const head = PARAM_HEAD_RE.exec(part);
|
|
3399
|
+
if (!head) return false;
|
|
3400
|
+
const rest = part.slice(head[0].length);
|
|
3401
|
+
if (rest === "") return true;
|
|
3402
|
+
if (rest[0] !== ":") return false;
|
|
3403
|
+
return !hasDefaultAssign(rest.slice(1));
|
|
3404
|
+
}
|
|
3405
|
+
function hasDefaultAssign(annotation) {
|
|
3406
|
+
for (let i = 0; i < annotation.length; i++) {
|
|
3407
|
+
if (annotation[i] !== "=") continue;
|
|
3408
|
+
if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
|
|
3409
|
+
i++;
|
|
3410
|
+
continue;
|
|
3411
|
+
}
|
|
3412
|
+
const prev = annotation[i - 1];
|
|
3413
|
+
if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
|
|
3414
|
+
return true;
|
|
3415
|
+
}
|
|
3416
|
+
return false;
|
|
3417
|
+
}
|
|
3418
|
+
function buildAnalysisSrc(def) {
|
|
3419
|
+
return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
|
|
3420
|
+
}
|
|
3421
|
+
var RESOLVER_REASONS = {
|
|
3422
|
+
"rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
|
|
3423
|
+
"arguments": "its body references `arguments`",
|
|
3424
|
+
"reassigned": "the binding it names is reassigned, so the function read here is not decidable",
|
|
3425
|
+
"not-a-function": "the binding it names is not a function declaration",
|
|
3426
|
+
"unresolved-import": "its module could not be resolved or parsed",
|
|
3427
|
+
"not-found": "the name did not resolve to any binding",
|
|
3428
|
+
"computed": "it is a computed expression",
|
|
3429
|
+
"foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
|
|
3430
|
+
"ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
|
|
3431
|
+
};
|
|
3432
|
+
function unconfirmableReason(def, src) {
|
|
3433
|
+
const declared = def.buildArityUnresolved;
|
|
3434
|
+
if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
|
|
3435
|
+
const s = src.trim();
|
|
3436
|
+
const params = s.slice(0, s.indexOf(")") + 1);
|
|
3437
|
+
if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
|
|
3438
|
+
if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
|
|
3439
|
+
if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
|
|
3440
|
+
return "it names a binding this build could not resolve to a function declaration";
|
|
3441
|
+
}
|
|
3442
|
+
return "its parameter list could not be parsed";
|
|
3443
|
+
}
|
|
3444
|
+
function confirmedArityForDef(def) {
|
|
3445
|
+
if (def.buildArity !== void 0) return def.buildArity;
|
|
3446
|
+
const src = buildAnalysisSrc(def);
|
|
3447
|
+
const arity = confirmedBuildArity(src);
|
|
3448
|
+
if (arity !== null) return arity;
|
|
3449
|
+
const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
|
|
3450
|
+
recordDegradation({
|
|
3451
|
+
code: "build-arity-unconfirmed",
|
|
3452
|
+
severity: "warn",
|
|
3453
|
+
where: `node("${def.type ?? "<inferred>"}")`,
|
|
3454
|
+
subject,
|
|
3455
|
+
fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
|
|
3456
|
+
otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
|
|
3457
|
+
});
|
|
3458
|
+
return null;
|
|
3459
|
+
}
|
|
3225
3460
|
function buildReadsChildren(def) {
|
|
3226
3461
|
if (!def.build) return true;
|
|
3227
|
-
const
|
|
3228
|
-
const arity = confirmedBuildArity(src);
|
|
3462
|
+
const arity = confirmedArityForDef(def);
|
|
3229
3463
|
if (arity === null) return true;
|
|
3230
3464
|
return arity >= 1;
|
|
3231
3465
|
}
|
|
3232
3466
|
function buildReadsRaw(def) {
|
|
3233
3467
|
if (!def.build) return true;
|
|
3234
|
-
const
|
|
3235
|
-
const arity = confirmedBuildArity(src);
|
|
3468
|
+
const arity = confirmedArityForDef(def);
|
|
3236
3469
|
if (arity === null) return true;
|
|
3237
3470
|
return arity >= 4;
|
|
3238
3471
|
}
|
|
3239
3472
|
function buildReadsTrivia(def) {
|
|
3240
3473
|
if (!def.build) return true;
|
|
3241
|
-
const
|
|
3242
|
-
const arity = confirmedBuildArity(src);
|
|
3474
|
+
const arity = confirmedArityForDef(def);
|
|
3243
3475
|
if (arity === null) return true;
|
|
3244
3476
|
return arity >= 5;
|
|
3245
3477
|
}
|
|
3246
3478
|
function buildReadsState(def) {
|
|
3247
3479
|
if (!def.build) return true;
|
|
3248
|
-
const
|
|
3249
|
-
const arity = confirmedBuildArity(src);
|
|
3480
|
+
const arity = confirmedArityForDef(def);
|
|
3250
3481
|
if (arity === null) return true;
|
|
3251
3482
|
return arity >= 6;
|
|
3252
3483
|
}
|
|
@@ -3254,8 +3485,7 @@ function buildReadsState(def) {
|
|
|
3254
3485
|
// src/compiler/fields.ts
|
|
3255
3486
|
function buildReadsFields(def) {
|
|
3256
3487
|
if (!def.build) return true;
|
|
3257
|
-
const
|
|
3258
|
-
const arity = confirmedBuildArity(src);
|
|
3488
|
+
const arity = confirmedArityForDef(def);
|
|
3259
3489
|
if (arity === null) return true;
|
|
3260
3490
|
return arity >= 2;
|
|
3261
3491
|
}
|
|
@@ -3436,6 +3666,13 @@ function isCstChild(value) {
|
|
|
3436
3666
|
function missingInferredType() {
|
|
3437
3667
|
throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
|
|
3438
3668
|
}
|
|
3669
|
+
function normalizeBuildArity(arity) {
|
|
3670
|
+
if (arity === void 0) return void 0;
|
|
3671
|
+
if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
|
|
3672
|
+
throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
|
|
3673
|
+
}
|
|
3674
|
+
return arity;
|
|
3675
|
+
}
|
|
3439
3676
|
function normalizeProject(project) {
|
|
3440
3677
|
if (project === void 0) return void 0;
|
|
3441
3678
|
if (!Number.isInteger(project) || project < 0) {
|
|
@@ -3471,13 +3708,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
3471
3708
|
const captureTrivia = opts?.captureTrivia === true;
|
|
3472
3709
|
const trailingTrivia = opts?.trailingTrivia === true;
|
|
3473
3710
|
const tags = opts?.tags;
|
|
3711
|
+
const buildArity = normalizeBuildArity(opts?.buildArity);
|
|
3474
3712
|
if (unwrap && collapse) {
|
|
3475
3713
|
throw new Error("node() options cannot set both unwrap and collapse");
|
|
3476
3714
|
}
|
|
3477
3715
|
if (project !== void 0 && (unwrap || collapse)) {
|
|
3478
3716
|
throw new Error("node() options cannot combine project with unwrap or collapse");
|
|
3479
3717
|
}
|
|
3480
|
-
const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
|
|
3718
|
+
const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
|
|
3481
3719
|
const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
|
|
3482
3720
|
const clonesState = build ? buildReadsState(def) : project === void 0;
|
|
3483
3721
|
const hasOwnFields = parserHasOwnFields(combinator);
|
|
@@ -3523,7 +3761,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
3523
3761
|
const cstOutput = cstOutputHost(ctx.build);
|
|
3524
3762
|
const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
|
|
3525
3763
|
const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
3526
|
-
const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
|
|
3764
|
+
const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
|
|
3527
3765
|
const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
|
|
3528
3766
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
3529
3767
|
pushCstChild(ctx, built, rawEntry);
|
|
@@ -3718,7 +3956,7 @@ function leaf(root, fn) {
|
|
|
3718
3956
|
}
|
|
3719
3957
|
|
|
3720
3958
|
// src/version.ts
|
|
3721
|
-
var PARSEMAN_VERSION = "0.
|
|
3959
|
+
var PARSEMAN_VERSION = "0.46.0";
|
|
3722
3960
|
|
|
3723
3961
|
// src/cst/host-mode.ts
|
|
3724
3962
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3738,346 +3976,11 @@ function assertHostModeCompatible(mode, build, hostBranchElided = true) {
|
|
|
3738
3976
|
}
|
|
3739
3977
|
|
|
3740
3978
|
// src/analysis/gating.ts
|
|
3741
|
-
var isAny = (fs2) => fs2.kind === "any";
|
|
3742
|
-
function intersects2(a, b) {
|
|
3743
|
-
if (a.kind === "any" || b.kind === "any") return true;
|
|
3744
|
-
if (a.kind === "empty" || b.kind === "empty") return false;
|
|
3745
|
-
for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true;
|
|
3746
|
-
return false;
|
|
3747
|
-
}
|
|
3748
|
-
function intersection(a, b) {
|
|
3749
|
-
if (a.kind === "any") return b;
|
|
3750
|
-
if (b.kind === "any") return a;
|
|
3751
|
-
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
3752
|
-
const ranges = [];
|
|
3753
|
-
for (const ra of a.ranges) for (const rb of b.ranges) {
|
|
3754
|
-
const lo = Math.max(ra.lo, rb.lo);
|
|
3755
|
-
const hi = Math.min(ra.hi, rb.hi);
|
|
3756
|
-
if (lo <= hi) ranges.push({ lo, hi });
|
|
3757
|
-
}
|
|
3758
|
-
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
3759
|
-
}
|
|
3760
|
-
function combine(sets) {
|
|
3761
|
-
let acc = { kind: "empty" };
|
|
3762
|
-
for (const s of sets) {
|
|
3763
|
-
if (s.kind === "any") return { kind: "any" };
|
|
3764
|
-
if (s.kind === "empty") continue;
|
|
3765
|
-
acc = acc.kind === "ranges" ? { kind: "ranges", ranges: [...acc.ranges, ...s.ranges] } : s;
|
|
3766
|
-
}
|
|
3767
|
-
return acc;
|
|
3768
|
-
}
|
|
3769
3979
|
var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
|
|
3770
|
-
function firstSetToString(
|
|
3771
|
-
if (
|
|
3772
|
-
if (
|
|
3773
|
-
return
|
|
3774
|
-
}
|
|
3775
|
-
var ruleNameOf2 = (p) => p._ruleName;
|
|
3776
|
-
var SUGGESTIONS = {
|
|
3777
|
-
"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.",
|
|
3778
|
-
"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 \u2014 use peek(X), which is zero-width AND carries X first-set.",
|
|
3779
|
-
"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. A plain sepBy(item, sep) is ALSO nullable \u2014 pass { min: 1 } for a list that cannot be empty.",
|
|
3780
|
-
"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.",
|
|
3781
|
-
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
3782
|
-
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
3783
|
-
};
|
|
3784
|
-
function classifyBroadArm(arm, resolve2) {
|
|
3785
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3786
|
-
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
3787
|
-
const walk = (p) => {
|
|
3788
|
-
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
3789
|
-
seen.add(p);
|
|
3790
|
-
const d = p._def;
|
|
3791
|
-
switch (d.tag) {
|
|
3792
|
-
case "literal":
|
|
3793
|
-
case "regex":
|
|
3794
|
-
case "keywords":
|
|
3795
|
-
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
3796
|
-
case "not":
|
|
3797
|
-
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
3798
|
-
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
3799
|
-
// (or the body is nullable) — walk in for the real cause.
|
|
3800
|
-
case "peek":
|
|
3801
|
-
return walk(d.parser);
|
|
3802
|
-
case "scanTo":
|
|
3803
|
-
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
3804
|
-
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
3805
|
-
// its own — it is the poison. `withCtx`/`recover` FORWARD their inner
|
|
3806
|
-
// first-set (see firstSetOf), so a broad result comes from the inner parser:
|
|
3807
|
-
// walk into it for a precise cause.
|
|
3808
|
-
case "guard":
|
|
3809
|
-
return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
|
|
3810
|
-
// attempt/withCtx/recover FORWARD their inner first-set (transparent for
|
|
3811
|
-
// dispatch), so a broad result comes from the inner parser — walk into it.
|
|
3812
|
-
case "attempt":
|
|
3813
|
-
case "withCtx":
|
|
3814
|
-
case "recover":
|
|
3815
|
-
return walk(d.parser);
|
|
3816
|
-
case "lazy": {
|
|
3817
|
-
const name = p._ruleName;
|
|
3818
|
-
let target;
|
|
3819
|
-
try {
|
|
3820
|
-
target = d.thunk();
|
|
3821
|
-
} catch {
|
|
3822
|
-
const bound = name !== void 0 ? resolve2?.(name) : void 0;
|
|
3823
|
-
if (bound === void 0)
|
|
3824
|
-
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
3825
|
-
target = bound;
|
|
3826
|
-
}
|
|
3827
|
-
const inner = walk(target);
|
|
3828
|
-
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
3829
|
-
return inner;
|
|
3830
|
-
}
|
|
3831
|
-
case "optional":
|
|
3832
|
-
case "many": {
|
|
3833
|
-
const inner = walk(d.parser);
|
|
3834
|
-
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
3835
|
-
}
|
|
3836
|
-
case "oneOrMore":
|
|
3837
|
-
case "transform":
|
|
3838
|
-
case "label":
|
|
3839
|
-
case "field":
|
|
3840
|
-
case "trivia":
|
|
3841
|
-
case "token":
|
|
3842
|
-
case "leaf":
|
|
3843
|
-
case "node":
|
|
3844
|
-
case "grammar":
|
|
3845
|
-
case "expect":
|
|
3846
|
-
return walk(d.parser);
|
|
3847
|
-
case "skip":
|
|
3848
|
-
return walk(d.main);
|
|
3849
|
-
case "sequence": {
|
|
3850
|
-
let sawNullablePrefix = false;
|
|
3851
|
-
for (const t of d.parsers) {
|
|
3852
|
-
if (t._def.tag === "not") {
|
|
3853
|
-
sawNullablePrefix = true;
|
|
3854
|
-
continue;
|
|
3855
|
-
}
|
|
3856
|
-
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
|
|
3857
|
-
const inner = walk(t);
|
|
3858
|
-
if (sawNullablePrefix)
|
|
3859
|
-
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
3860
|
-
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
3861
|
-
}
|
|
3862
|
-
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve2)) {
|
|
3863
|
-
sawNullablePrefix = true;
|
|
3864
|
-
continue;
|
|
3865
|
-
}
|
|
3866
|
-
return null;
|
|
3867
|
-
}
|
|
3868
|
-
return { cause: "broad-recognizer", detail: "sequence of only nullable/zero-width terms" };
|
|
3869
|
-
}
|
|
3870
|
-
case "choice": {
|
|
3871
|
-
for (const a of d.parsers) {
|
|
3872
|
-
const r = walk(a);
|
|
3873
|
-
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
3874
|
-
}
|
|
3875
|
-
return null;
|
|
3876
|
-
}
|
|
3877
|
-
case "dispatch":
|
|
3878
|
-
return walk(d.selector);
|
|
3879
|
-
default:
|
|
3880
|
-
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
3881
|
-
}
|
|
3882
|
-
};
|
|
3883
|
-
return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
|
|
3884
|
-
}
|
|
3885
|
-
function peelToLeading(arm) {
|
|
3886
|
-
let d = arm._def;
|
|
3887
|
-
let cur = arm;
|
|
3888
|
-
for (; ; ) {
|
|
3889
|
-
if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label" || d.tag === "attempt") {
|
|
3890
|
-
cur = d.parser;
|
|
3891
|
-
d = cur._def;
|
|
3892
|
-
continue;
|
|
3893
|
-
}
|
|
3894
|
-
break;
|
|
3895
|
-
}
|
|
3896
|
-
if (d.tag === "sequence" && d.parsers.length >= 1) return d.parsers[0];
|
|
3897
|
-
return cur;
|
|
3898
|
-
}
|
|
3899
|
-
var KEYWORD_REGEX_RE = /^\^?[@#.-]?[A-Za-z][\w-]*(\(\?![^)]*\))?\$?$/;
|
|
3900
|
-
function detectAntiPatterns(rule, arms) {
|
|
3901
|
-
const out = [];
|
|
3902
|
-
arms.forEach((arm, i) => {
|
|
3903
|
-
const lead = peelToLeading(arm);
|
|
3904
|
-
const ld = lead._def;
|
|
3905
|
-
if (ld.tag === "not") {
|
|
3906
|
-
const inner = ld.parser;
|
|
3907
|
-
if (inner._def.tag === "not") {
|
|
3908
|
-
out.push({
|
|
3909
|
-
kind: "double-not",
|
|
3910
|
-
rule,
|
|
3911
|
-
armIndex: i,
|
|
3912
|
-
message: "not(not(...)) hand-rolls automatic first-char gating and MISCOMPILES among shared-first-char sibling arms (its first-set is ANY, poisoning dispatch). Prefer letting the arm lead with its consuming terminal; where a real positive lookahead IS needed, use peek(X) \u2014 zero-width like not(not(X)) but it CARRIES X's first-set, so the arm still dispatches."
|
|
3913
|
-
});
|
|
3914
|
-
} else {
|
|
3915
|
-
out.push({
|
|
3916
|
-
kind: "leading-not",
|
|
3917
|
-
rule,
|
|
3918
|
-
armIndex: i,
|
|
3919
|
-
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."
|
|
3920
|
-
});
|
|
3921
|
-
}
|
|
3922
|
-
}
|
|
3923
|
-
if (ld.tag === "regex" && KEYWORD_REGEX_RE.test(ld.source)) {
|
|
3924
|
-
out.push({
|
|
3925
|
-
kind: "keyword-regex",
|
|
3926
|
-
rule,
|
|
3927
|
-
armIndex: i,
|
|
3928
|
-
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).`
|
|
3929
|
-
});
|
|
3930
|
-
}
|
|
3931
|
-
});
|
|
3932
|
-
return out;
|
|
3933
|
-
}
|
|
3934
|
-
function analyzeGating(entry, opts) {
|
|
3935
|
-
return analyzeGatingRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3936
|
-
}
|
|
3937
|
-
function analyzeGatingRules(ruleMap, opts) {
|
|
3938
|
-
const raw = [];
|
|
3939
|
-
const antiPatterns = [];
|
|
3940
|
-
const unanalysable = [];
|
|
3941
|
-
const seenUnanalysable = /* @__PURE__ */ new Set();
|
|
3942
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3943
|
-
const noteUnanalysable = (u) => {
|
|
3944
|
-
const key = `${u.rule}\0${u.kind}`;
|
|
3945
|
-
if (seenUnanalysable.has(key)) return;
|
|
3946
|
-
seenUnanalysable.add(key);
|
|
3947
|
-
unanalysable.push(u);
|
|
3948
|
-
};
|
|
3949
|
-
const visit = (p, enclosingRule) => {
|
|
3950
|
-
if (visited.has(p)) return;
|
|
3951
|
-
visited.add(p);
|
|
3952
|
-
const def = p?._def;
|
|
3953
|
-
if (def === null || typeof def !== "object") {
|
|
3954
|
-
noteUnanalysable(
|
|
3955
|
-
typeof p === "function" ? {
|
|
3956
|
-
rule: enclosingRule,
|
|
3957
|
-
kind: "fused-rule",
|
|
3958
|
-
reason: "fused rule function \u2014 a compose()/fuse result has no combinator graph. Analyze the composed grammar itself (analyzeGrammarGating) so the carried IR is re-lowered first."
|
|
3959
|
-
} : {
|
|
3960
|
-
rule: enclosingRule,
|
|
3961
|
-
kind: "not-a-combinator",
|
|
3962
|
-
reason: `value of type ${p === null ? "null" : typeof p} is not a combinator and carries no _def descriptor.`
|
|
3963
|
-
}
|
|
3964
|
-
);
|
|
3965
|
-
return;
|
|
3966
|
-
}
|
|
3967
|
-
const d = def;
|
|
3968
|
-
const rule = ruleNameOf2(p) ?? enclosingRule;
|
|
3969
|
-
if (d.tag === "choice") {
|
|
3970
|
-
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
3971
|
-
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
3972
|
-
}
|
|
3973
|
-
const rec = d;
|
|
3974
|
-
const kids = [];
|
|
3975
|
-
if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
|
|
3976
|
-
for (const k of ["parser", "main", "skipped", "separator", "sentinel"])
|
|
3977
|
-
if (rec[k]) kids.push(rec[k]);
|
|
3978
|
-
if (d.tag === "dispatch") {
|
|
3979
|
-
kids.push(d.selector);
|
|
3980
|
-
for (const c of d.cases) kids.push(c.parser);
|
|
3981
|
-
if (d.matchers) for (const c of d.matchers) kids.push(c.parser);
|
|
3982
|
-
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3983
|
-
}
|
|
3984
|
-
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
3985
|
-
if (d.tag === "lazy") {
|
|
3986
|
-
try {
|
|
3987
|
-
kids.push(d.thunk());
|
|
3988
|
-
} catch {
|
|
3989
|
-
}
|
|
3990
|
-
}
|
|
3991
|
-
for (const k of kids) visit(k, rule);
|
|
3992
|
-
};
|
|
3993
|
-
for (const [name, root] of ruleMap) visit(root, ruleNameOf2(root) ?? name);
|
|
3994
|
-
const perRule = /* @__PURE__ */ new Map();
|
|
3995
|
-
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3996
|
-
const seenInRule = /* @__PURE__ */ new Map();
|
|
3997
|
-
const accept = new Set(opts?.accept ?? []);
|
|
3998
|
-
const usedAccept = /* @__PURE__ */ new Set();
|
|
3999
|
-
const choices = raw.map(({ g, rule }) => {
|
|
4000
|
-
const n = seenInRule.get(rule) ?? 0;
|
|
4001
|
-
seenInRule.set(rule, n + 1);
|
|
4002
|
-
const id = (perRule.get(rule) ?? 1) === 1 ? rule : `${rule}#${n}`;
|
|
4003
|
-
const isAccepted = g.gates === "no" && accept.has(id);
|
|
4004
|
-
if (isAccepted) usedAccept.add(id);
|
|
4005
|
-
return { ...g, id, accepted: isAccepted };
|
|
4006
|
-
});
|
|
4007
|
-
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
4008
|
-
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
4009
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
4010
|
-
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
4011
|
-
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
4012
|
-
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
4013
|
-
return {
|
|
4014
|
-
totalChoices: choices.length,
|
|
4015
|
-
gated,
|
|
4016
|
-
recoverable,
|
|
4017
|
-
unanalysable,
|
|
4018
|
-
ungated,
|
|
4019
|
-
accepted,
|
|
4020
|
-
deferred,
|
|
4021
|
-
acceptedUnused,
|
|
4022
|
-
choices,
|
|
4023
|
-
antiPatterns
|
|
4024
|
-
};
|
|
4025
|
-
}
|
|
4026
|
-
function analyzeChoice(p, d, rule, resolve2) {
|
|
4027
|
-
const arms = d.parsers;
|
|
4028
|
-
const shallow = arms.map((a) => a._meta.firstSet);
|
|
4029
|
-
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve2));
|
|
4030
|
-
const anyArms = deep.map((fs2, index) => ({ fs: fs2, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
4031
|
-
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve2);
|
|
4032
|
-
return { index, cause, detail, shallowAnyOnly: false, unresolvedExternal: unresolvedExternal === true, suggestion: SUGGESTIONS[cause] };
|
|
4033
|
-
});
|
|
4034
|
-
const overlaps = [];
|
|
4035
|
-
for (let i = 0; i < deep.length; i++)
|
|
4036
|
-
for (let j = i + 1; j < deep.length; j++)
|
|
4037
|
-
if (!isAny(deep[i]) && !isAny(deep[j]) && intersects2(deep[i], deep[j]))
|
|
4038
|
-
overlaps.push({
|
|
4039
|
-
a: i,
|
|
4040
|
-
b: j,
|
|
4041
|
-
on: intersection(deep[i], deep[j]),
|
|
4042
|
-
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."
|
|
4043
|
-
});
|
|
4044
|
-
let gates;
|
|
4045
|
-
if (d.disjoint) gates = "yes";
|
|
4046
|
-
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
4047
|
-
else gates = "no";
|
|
4048
|
-
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
4049
|
-
return {
|
|
4050
|
-
rule,
|
|
4051
|
-
strategy: d.strategy?.tag ?? "firstMatch",
|
|
4052
|
-
gates,
|
|
4053
|
-
deferred,
|
|
4054
|
-
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
4055
|
-
anyArms,
|
|
4056
|
-
overlaps
|
|
4057
|
-
};
|
|
4058
|
-
}
|
|
4059
|
-
function formatGatingWarnings(report) {
|
|
4060
|
-
const lines = [];
|
|
4061
|
-
if (report.unanalysable.length > 0) {
|
|
4062
|
-
lines.push(
|
|
4063
|
-
`parseman gating: ${report.unanalysable.length} rule(s) UNANALYSABLE \u2014 this report is PARTIAL (${report.totalChoices} choice(s) examined). An empty finding list below does NOT mean the grammar is clean.`
|
|
4064
|
-
);
|
|
4065
|
-
for (const u of report.unanalysable)
|
|
4066
|
-
lines.push(` \xB7 ${u.rule} [${u.kind}]: ${u.reason}`);
|
|
4067
|
-
}
|
|
4068
|
-
for (const c of report.ungated) {
|
|
4069
|
-
lines.push(`parseman gating: choice @ ${c.id} is UNGATED [${c.strategy}] \u2014 no first-char dispatch; every position speculatively enters doomed arms.`);
|
|
4070
|
-
for (const a of c.anyArms)
|
|
4071
|
-
lines.push(` \xB7 arm[${a.index}] first-set ANY (${a.cause}): ${a.detail}
|
|
4072
|
-
fix: ${a.suggestion}`);
|
|
4073
|
-
for (const o of c.overlaps)
|
|
4074
|
-
lines.push(` \xB7 arm[${o.a}] \u2229 arm[${o.b}] overlap on ${firstSetToString(o.on)}
|
|
4075
|
-
fix: ${o.suggestion}`);
|
|
4076
|
-
lines.push(` (intentional? accept it in the gating snapshot: { accept: ['${c.id}'] }.)`);
|
|
4077
|
-
}
|
|
4078
|
-
for (const ap of report.antiPatterns)
|
|
4079
|
-
lines.push(`parseman anti-pattern [${ap.kind}] @ ${ap.rule} arm[${ap.armIndex}]: ${ap.message}`);
|
|
4080
|
-
return lines;
|
|
3980
|
+
function firstSetToString(fs3) {
|
|
3981
|
+
if (fs3.kind === "any") return "ANY";
|
|
3982
|
+
if (fs3.kind === "empty") return "(empty)";
|
|
3983
|
+
return fs3.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
|
|
4081
3984
|
}
|
|
4082
3985
|
|
|
4083
3986
|
// src/analysis/duplication.ts
|
|
@@ -4120,7 +4023,7 @@ function childrenOf2(d) {
|
|
|
4120
4023
|
}
|
|
4121
4024
|
}
|
|
4122
4025
|
}
|
|
4123
|
-
var
|
|
4026
|
+
var ruleNameOf2 = (p) => p._ruleName;
|
|
4124
4027
|
var fnKey = (src, fn) => src ?? (typeof fn === "function" ? String(fn) : "");
|
|
4125
4028
|
function payloadKey(p, d) {
|
|
4126
4029
|
switch (d.tag) {
|
|
@@ -4131,7 +4034,7 @@ function payloadKey(p, d) {
|
|
|
4131
4034
|
case "keywords":
|
|
4132
4035
|
return `keywords\0${[...d.words].join("")}\0${d.caseInsensitive}\0${d.boundary ?? ""}`;
|
|
4133
4036
|
case "lazy": {
|
|
4134
|
-
const n =
|
|
4037
|
+
const n = ruleNameOf2(p);
|
|
4135
4038
|
return n === void 0 ? `ref\0#${anonId(p)}` : `ref\0${n}`;
|
|
4136
4039
|
}
|
|
4137
4040
|
case "label":
|
|
@@ -4200,7 +4103,7 @@ var Interner = class {
|
|
|
4200
4103
|
var MAX_RENDER = 160;
|
|
4201
4104
|
function render(p, depth = 3) {
|
|
4202
4105
|
const d = p._def;
|
|
4203
|
-
const name =
|
|
4106
|
+
const name = ruleNameOf2(p);
|
|
4204
4107
|
switch (d.tag) {
|
|
4205
4108
|
case "literal":
|
|
4206
4109
|
return `literal('${d.value}')`;
|
|
@@ -4388,7 +4291,7 @@ function keywordAlternationHazards(words, boundary) {
|
|
|
4388
4291
|
return out;
|
|
4389
4292
|
}
|
|
4390
4293
|
function analyzeDuplication(entry, opts) {
|
|
4391
|
-
return analyzeDuplicationRules([[
|
|
4294
|
+
return analyzeDuplicationRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
4392
4295
|
}
|
|
4393
4296
|
function assertAnalyzable(ruleMap) {
|
|
4394
4297
|
for (const [name, rule] of ruleMap) {
|
|
@@ -4426,11 +4329,11 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
4426
4329
|
return n;
|
|
4427
4330
|
};
|
|
4428
4331
|
const visited = /* @__PURE__ */ new Map();
|
|
4429
|
-
const visit = (p, rule,
|
|
4332
|
+
const visit = (p, rule, path3, enclosingNode, enclosingReducer) => {
|
|
4430
4333
|
if (visited.has(p)) return;
|
|
4431
4334
|
const d = p._def;
|
|
4432
|
-
const own =
|
|
4433
|
-
const here = own !== void 0 && own !== rule ? { rule: own, path: "" } : { rule, path:
|
|
4335
|
+
const own = ruleNameOf2(p);
|
|
4336
|
+
const here = own !== void 0 && own !== rule ? { rule: own, path: "" } : { rule, path: path3 };
|
|
4434
4337
|
visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
|
|
4435
4338
|
const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
|
|
4436
4339
|
const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
|
|
@@ -4442,12 +4345,12 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
4442
4345
|
if (d.tag === "lazy") {
|
|
4443
4346
|
try {
|
|
4444
4347
|
const target = d.thunk();
|
|
4445
|
-
visit(target,
|
|
4348
|
+
visit(target, ruleNameOf2(target) ?? ruleNameOf2(p) ?? here.rule, "", null, false);
|
|
4446
4349
|
} catch {
|
|
4447
4350
|
}
|
|
4448
4351
|
}
|
|
4449
4352
|
};
|
|
4450
|
-
for (const [name, root] of ruleMap) visit(root,
|
|
4353
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf2(root) ?? name, "", null, false);
|
|
4451
4354
|
const accept = new Set(opts?.accept ?? []);
|
|
4452
4355
|
const used = /* @__PURE__ */ new Set();
|
|
4453
4356
|
const keep = (f) => {
|
|
@@ -4464,10 +4367,10 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
4464
4367
|
const divergentNodes = findDivergentNodes(visited, shapeOf);
|
|
4465
4368
|
const structureLoss = findStructureLoss(visited, opts?.resolveRef);
|
|
4466
4369
|
const keywordRegexes = findKeywordRegexes(visited);
|
|
4467
|
-
const subsume = (
|
|
4370
|
+
const subsume = (fs3) => {
|
|
4468
4371
|
const covered = /* @__PURE__ */ new Set();
|
|
4469
4372
|
const surviving = /* @__PURE__ */ new Set();
|
|
4470
|
-
for (const f of [...
|
|
4373
|
+
for (const f of [...fs3].sort((a, b) => b.savings - a.savings)) {
|
|
4471
4374
|
if (f._members.every((m) => covered.has(m))) continue;
|
|
4472
4375
|
surviving.add(f.id);
|
|
4473
4376
|
for (const m of f._members) markSubtree(m, covered);
|
|
@@ -4590,10 +4493,10 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
|
|
|
4590
4493
|
return out.sort((a, b) => b.savings - a.savings);
|
|
4591
4494
|
}
|
|
4592
4495
|
function findDivergentSlot(members, shapeOf) {
|
|
4593
|
-
const
|
|
4496
|
+
const path3 = [];
|
|
4594
4497
|
const defs = [];
|
|
4595
4498
|
let cur = [...members];
|
|
4596
|
-
for (let
|
|
4499
|
+
for (let guard = 0; guard < 64; guard++) {
|
|
4597
4500
|
const d0 = cur[0]._def;
|
|
4598
4501
|
const kidLists = cur.map((c) => childrenOf2(c._def));
|
|
4599
4502
|
const arity = kidLists[0].length;
|
|
@@ -4605,14 +4508,14 @@ function findDivergentSlot(members, shapeOf) {
|
|
|
4605
4508
|
}
|
|
4606
4509
|
if (diff.length !== 1) break;
|
|
4607
4510
|
const i = diff[0];
|
|
4608
|
-
|
|
4511
|
+
path3.push(i);
|
|
4609
4512
|
defs.push(d0);
|
|
4610
4513
|
cur = kidLists.map((k) => k[i]);
|
|
4611
4514
|
const nextArity = childrenOf2(cur[0]._def).length;
|
|
4612
4515
|
const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
|
|
4613
4516
|
if (!samePayload || nextArity === 0) break;
|
|
4614
4517
|
}
|
|
4615
|
-
if (
|
|
4518
|
+
if (path3.length === 0) return null;
|
|
4616
4519
|
let slotSize = 0;
|
|
4617
4520
|
const sizeOf = (p) => {
|
|
4618
4521
|
let n = 1;
|
|
@@ -4620,14 +4523,14 @@ function findDivergentSlot(members, shapeOf) {
|
|
|
4620
4523
|
return n;
|
|
4621
4524
|
};
|
|
4622
4525
|
for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
|
|
4623
|
-
return { path:
|
|
4526
|
+
return { path: path3, defs, variants: cur, slotSize };
|
|
4624
4527
|
}
|
|
4625
|
-
function renderWithHole(p,
|
|
4626
|
-
if (depth ===
|
|
4528
|
+
function renderWithHole(p, path3, depth = 0) {
|
|
4529
|
+
if (depth === path3.length) return "\u2039slot\u203A";
|
|
4627
4530
|
const d = p._def;
|
|
4628
4531
|
const kids = childrenOf2(d);
|
|
4629
|
-
const idx =
|
|
4630
|
-
const parts = kids.map((k, i) => i === idx ? renderWithHole(k,
|
|
4532
|
+
const idx = path3[depth];
|
|
4533
|
+
const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path3, depth + 1) : render(k, 2));
|
|
4631
4534
|
switch (d.tag) {
|
|
4632
4535
|
case "sequence":
|
|
4633
4536
|
return `sequence(${parts.join(", ")})`;
|
|
@@ -4831,13 +4734,13 @@ function findRegexClasses(visited) {
|
|
|
4831
4734
|
}
|
|
4832
4735
|
return out.sort((a, b) => Number(b.drifted) - Number(a.drifted) || b.count - a.count);
|
|
4833
4736
|
}
|
|
4834
|
-
function
|
|
4737
|
+
function intersects2(a, b) {
|
|
4835
4738
|
if (a.kind === "any" || b.kind === "any") return true;
|
|
4836
4739
|
if (a.kind === "empty" || b.kind === "empty") return false;
|
|
4837
4740
|
for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true;
|
|
4838
4741
|
return false;
|
|
4839
4742
|
}
|
|
4840
|
-
function
|
|
4743
|
+
function intersection(a, b) {
|
|
4841
4744
|
if (a.kind === "any") return b;
|
|
4842
4745
|
if (b.kind === "any") return a;
|
|
4843
4746
|
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
@@ -4866,19 +4769,19 @@ function leadingRegex(arm) {
|
|
|
4866
4769
|
const t = termsOf(arm)[0];
|
|
4867
4770
|
return t !== void 0 && t._def.tag === "regex";
|
|
4868
4771
|
}
|
|
4869
|
-
function findOverlaps(visited, shapeOf,
|
|
4772
|
+
function findOverlaps(visited, shapeOf, resolve3) {
|
|
4870
4773
|
const out = [];
|
|
4871
4774
|
for (const v2 of visited.values()) {
|
|
4872
4775
|
const d = v2.d;
|
|
4873
4776
|
if (d.tag !== "choice") continue;
|
|
4874
4777
|
const arms = d.parsers;
|
|
4875
|
-
const
|
|
4778
|
+
const fs3 = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve3));
|
|
4876
4779
|
const strategy = d.strategy;
|
|
4877
4780
|
for (let i = 0; i < arms.length; i++) {
|
|
4878
4781
|
for (let j = i + 1; j < arms.length; j++) {
|
|
4879
|
-
const A =
|
|
4782
|
+
const A = fs3[i], B = fs3[j];
|
|
4880
4783
|
if (A.kind === "any" || B.kind === "any") continue;
|
|
4881
|
-
if (!
|
|
4784
|
+
if (!intersects2(A, B)) continue;
|
|
4882
4785
|
const ta = termsOf(arms[i]), tb = termsOf(arms[j]);
|
|
4883
4786
|
let shared = 0;
|
|
4884
4787
|
while (shared < ta.length && shared < tb.length && shapeOf(ta[shared]) === shapeOf(tb[shared])) shared++;
|
|
@@ -4890,7 +4793,7 @@ function findOverlaps(visited, shapeOf, resolve2) {
|
|
|
4890
4793
|
site: v2.site,
|
|
4891
4794
|
a: i,
|
|
4892
4795
|
b: j,
|
|
4893
|
-
on:
|
|
4796
|
+
on: intersection(A, B),
|
|
4894
4797
|
sharedLeadingTerms: shared,
|
|
4895
4798
|
sharedPrefix: shared > 0 ? clamp(ta.slice(0, shared).map((t) => render(t, 2)).join(", ")) : null,
|
|
4896
4799
|
handledByStrategy: handled,
|
|
@@ -5192,7 +5095,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
|
|
|
5192
5095
|
for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
|
|
5193
5096
|
}
|
|
5194
5097
|
var MAX_LOST_TYPES = 8;
|
|
5195
|
-
function findStructureLoss(visited,
|
|
5098
|
+
function findStructureLoss(visited, resolve3) {
|
|
5196
5099
|
const nestedCache = /* @__PURE__ */ new Map();
|
|
5197
5100
|
const nested = (p) => {
|
|
5198
5101
|
const hit = nestedCache.get(p);
|
|
@@ -5209,18 +5112,18 @@ function findStructureLoss(visited, resolve2) {
|
|
|
5209
5112
|
const arms = d.parsers;
|
|
5210
5113
|
const resolved = arms.map(resolveArm);
|
|
5211
5114
|
const nodeTypes = resolved.map(wrappingNodeType);
|
|
5212
|
-
const
|
|
5115
|
+
const fs3 = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve3));
|
|
5213
5116
|
for (let i = 0; i < arms.length; i++) {
|
|
5214
5117
|
if (d.gates[i] != null) continue;
|
|
5215
5118
|
const type = nodeTypes[i] ?? null;
|
|
5216
5119
|
if (type === null) continue;
|
|
5217
5120
|
if (nested(resolved[i]).size !== 0) continue;
|
|
5218
|
-
const A =
|
|
5121
|
+
const A = fs3[i];
|
|
5219
5122
|
if (A.kind === "any") continue;
|
|
5220
5123
|
for (let j = i + 1; j < arms.length; j++) {
|
|
5221
5124
|
if (nodeTypes[j] !== type) continue;
|
|
5222
|
-
const B =
|
|
5223
|
-
if (B.kind === "any" || !
|
|
5125
|
+
const B = fs3[j];
|
|
5126
|
+
if (B.kind === "any" || !intersects2(A, B)) continue;
|
|
5224
5127
|
const lost = nested(resolved[j]);
|
|
5225
5128
|
if (lost.size === 0) continue;
|
|
5226
5129
|
const lostNodeTypes = [...lost].sort();
|
|
@@ -5231,7 +5134,7 @@ function findStructureLoss(visited, resolve2) {
|
|
|
5231
5134
|
nodeType: type,
|
|
5232
5135
|
earlier: i,
|
|
5233
5136
|
later: j,
|
|
5234
|
-
on:
|
|
5137
|
+
on: intersection(A, B),
|
|
5235
5138
|
lostNodeTypes: lostNodeTypes.slice(0, MAX_LOST_TYPES),
|
|
5236
5139
|
earlierShape: clamp(render(resolved[i], 3)),
|
|
5237
5140
|
laterShape: clamp(render(resolved[j], 3)),
|
|
@@ -5489,11 +5392,11 @@ function buildBalancedInterior(open, close, skips) {
|
|
|
5489
5392
|
return combi;
|
|
5490
5393
|
}
|
|
5491
5394
|
function firstSetClassChars(c) {
|
|
5492
|
-
const
|
|
5493
|
-
if (
|
|
5494
|
-
if (
|
|
5395
|
+
const fs3 = c._meta.firstSet;
|
|
5396
|
+
if (fs3.kind === "empty") return [];
|
|
5397
|
+
if (fs3.kind !== "ranges") return null;
|
|
5495
5398
|
const out = [];
|
|
5496
|
-
for (const { lo, hi } of
|
|
5399
|
+
for (const { lo, hi } of fs3.ranges) {
|
|
5497
5400
|
if (hi - lo > 8) return null;
|
|
5498
5401
|
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
5499
5402
|
}
|
|
@@ -5507,13 +5410,13 @@ function escapeClassChar(ch2) {
|
|
|
5507
5410
|
function idPart(value) {
|
|
5508
5411
|
return encodeURIComponent(value);
|
|
5509
5412
|
}
|
|
5510
|
-
function dispatchArmIds(def,
|
|
5413
|
+
function dispatchArmIds(def, path3) {
|
|
5511
5414
|
const ids = [];
|
|
5512
|
-
for (const entry of def.cases) ids.push(`dispatch:${
|
|
5415
|
+
for (const entry of def.cases) ids.push(`dispatch:${path3}/when:${entry.keys.map(idPart).join("|")}`);
|
|
5513
5416
|
for (const entry of def.matchers ?? []) {
|
|
5514
|
-
ids.push(`dispatch:${
|
|
5417
|
+
ids.push(`dispatch:${path3}/matcher:${entry.kind}:${idPart(entry.value)}${entry.kind === "matches" && entry.flags ? `:${idPart(entry.flags)}` : ""}`);
|
|
5515
5418
|
}
|
|
5516
|
-
if (def.otherwise) ids.push(`dispatch:${
|
|
5419
|
+
if (def.otherwise) ids.push(`dispatch:${path3}/otherwise`);
|
|
5517
5420
|
return ids;
|
|
5518
5421
|
}
|
|
5519
5422
|
function children(def, winners) {
|
|
@@ -5552,7 +5455,15 @@ function children(def, winners) {
|
|
|
5552
5455
|
let resolved;
|
|
5553
5456
|
try {
|
|
5554
5457
|
resolved = def.thunk();
|
|
5555
|
-
} catch {
|
|
5458
|
+
} catch (e) {
|
|
5459
|
+
recordDegradation({
|
|
5460
|
+
code: "coverage-definitions-unavailable",
|
|
5461
|
+
severity: "warn",
|
|
5462
|
+
where: "<lazy rule reference>",
|
|
5463
|
+
subject: `unresolvable reference (${e.message})`,
|
|
5464
|
+
fellBackTo: "the coverage walk stopped at this reference, so every definition below it is ABSENT from the denominator \u2014 which raises the reported ratio rather than lowering it",
|
|
5465
|
+
otherwise: "the referenced rule and its whole subtree would contribute definitions"
|
|
5466
|
+
});
|
|
5556
5467
|
return [];
|
|
5557
5468
|
}
|
|
5558
5469
|
const name = resolved._ruleName;
|
|
@@ -5582,7 +5493,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5582
5493
|
}
|
|
5583
5494
|
}
|
|
5584
5495
|
const seen = /* @__PURE__ */ new Set();
|
|
5585
|
-
const visit = (parser2,
|
|
5496
|
+
const visit = (parser2, path3) => {
|
|
5586
5497
|
if (seen.has(parser2)) return;
|
|
5587
5498
|
seen.add(parser2);
|
|
5588
5499
|
const rule = winnerNames.get(parser2) ?? parser2._ruleName;
|
|
@@ -5592,22 +5503,22 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5592
5503
|
rules2.set(parser2, id);
|
|
5593
5504
|
}
|
|
5594
5505
|
if (parser2._def.tag === "choice") {
|
|
5595
|
-
const ids = parser2._def.parsers.map((_, index) => `choice:${
|
|
5506
|
+
const ids = parser2._def.parsers.map((_, index) => `choice:${path3}/arm:${index}`);
|
|
5596
5507
|
choices.set(parser2, ids);
|
|
5597
5508
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
5598
5509
|
}
|
|
5599
5510
|
if (parser2._def.tag === "dispatch") {
|
|
5600
|
-
const ids = dispatchArmIds(parser2._def,
|
|
5511
|
+
const ids = dispatchArmIds(parser2._def, path3);
|
|
5601
5512
|
dispatches.set(parser2, ids);
|
|
5602
5513
|
ids.forEach((id) => definitions.set(id, { id, kind: "dispatch-arm" }));
|
|
5603
5514
|
}
|
|
5604
|
-
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${
|
|
5515
|
+
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path3}`);
|
|
5605
5516
|
if (parser2._def.tag === "label") {
|
|
5606
|
-
const id = `label:${
|
|
5517
|
+
const id = `label:${path3}`;
|
|
5607
5518
|
labels.set(parser2, [id]);
|
|
5608
5519
|
definitions.set(id, { id, kind: "label" });
|
|
5609
5520
|
}
|
|
5610
|
-
children(parser2._def, winners).forEach((child, index) => visit(child, `${
|
|
5521
|
+
children(parser2._def, winners).forEach((child, index) => visit(child, `${path3}/${parser2._def.tag}:${index}`));
|
|
5611
5522
|
};
|
|
5612
5523
|
const roots = Array.isArray(entry) ? entry : [entry];
|
|
5613
5524
|
for (const root of roots) {
|
|
@@ -5665,15 +5576,15 @@ function parseStringShape(source) {
|
|
|
5665
5576
|
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
5666
5577
|
const inner = source.slice(1, -1);
|
|
5667
5578
|
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
5668
|
-
const
|
|
5579
|
+
const ESC2 = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
5669
5580
|
let body;
|
|
5670
5581
|
let esc;
|
|
5671
|
-
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${
|
|
5582
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC2}\\)\\*$`).exec(inner);
|
|
5672
5583
|
if (m) {
|
|
5673
5584
|
body = m[1];
|
|
5674
5585
|
esc = m[2];
|
|
5675
5586
|
} else {
|
|
5676
|
-
m = new RegExp(`^\\((?:\\?:)?${
|
|
5587
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC2}\\|${CLS}\\)\\*$`).exec(inner);
|
|
5677
5588
|
if (m) {
|
|
5678
5589
|
esc = m[1];
|
|
5679
5590
|
body = m[2];
|
|
@@ -6379,10 +6290,10 @@ var ALT_SWITCH_MIN_CASES = 3;
|
|
|
6379
6290
|
function planAltSwitch(firsts) {
|
|
6380
6291
|
const cases = [];
|
|
6381
6292
|
let total = 0;
|
|
6382
|
-
for (const
|
|
6383
|
-
if (!
|
|
6293
|
+
for (const fs3 of firsts) {
|
|
6294
|
+
if (!fs3 || fs3.negated) return { kind: "if" };
|
|
6384
6295
|
const pts = [];
|
|
6385
|
-
for (const [lo, hi] of
|
|
6296
|
+
for (const [lo, hi] of fs3.ranges) {
|
|
6386
6297
|
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
6387
6298
|
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
6388
6299
|
}
|
|
@@ -6457,8 +6368,8 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
6457
6368
|
} else {
|
|
6458
6369
|
const c0 = codeAt(start, firstChar);
|
|
6459
6370
|
shape.arms.forEach((arm, k) => {
|
|
6460
|
-
const
|
|
6461
|
-
const cond =
|
|
6371
|
+
const fs3 = shape.firsts[k];
|
|
6372
|
+
const cond = fs3.negated ? `!(${classCond(c0, fs3.ranges)})` : `(${classCond(c0, fs3.ranges)})`;
|
|
6462
6373
|
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
6463
6374
|
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
6464
6375
|
lines.push(...m.setup);
|
|
@@ -6875,27 +6786,53 @@ function emitScannableTerminal(shape, c) {
|
|
|
6875
6786
|
}
|
|
6876
6787
|
|
|
6877
6788
|
// src/compiler/inline-build.ts
|
|
6878
|
-
var
|
|
6789
|
+
var ANN = String.raw`\s*\??\s*(?::[^,)=]+)?`;
|
|
6790
|
+
var ANY_P = String.raw`\w+${ANN}`;
|
|
6791
|
+
var MK_BUILD_RE = new RegExp(
|
|
6792
|
+
String.raw`^\(\s*(\w+)${ANN}\s*,\s*${ANY_P}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\5\s*,\s*\1\s*,\s*\3\s*,\s*\2\s*,\s*\4\s*\)\s*$`
|
|
6793
|
+
);
|
|
6879
6794
|
function escapeRegExp(s) {
|
|
6880
6795
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6881
6796
|
}
|
|
6797
|
+
function identMkBuildRe(typeIdent) {
|
|
6798
|
+
return new RegExp(
|
|
6799
|
+
String.raw`^\(\s*(\w+)${ANN}\s*,\s*${ANY_P}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*\)\s*=>\s*(?:[\w$.]*\.)?mk\s*\(\s*${escapeRegExp(typeIdent)}\s*,\s*\1\s*,\s*\3\s*,\s*\2\s*,\s*\4\s*\)\s*$`
|
|
6800
|
+
);
|
|
6801
|
+
}
|
|
6882
6802
|
function looseMkBuildRe(type) {
|
|
6883
6803
|
return new RegExp(
|
|
6884
|
-
String.raw`^\(\s*(\w+)\s*,\s
|
|
6804
|
+
String.raw`^\(\s*(\w+)${ANN}\s*,\s*${ANY_P}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*\)\s*=>\s*.+\(\s*(['"])${escapeRegExp(type)}\5\s*,\s*\1\s*,\s*\3\s*,\s*\2\s*,\s*\4\s*\)\s*$`
|
|
6885
6805
|
);
|
|
6886
6806
|
}
|
|
6807
|
+
var LOOKS_LIKE_MK_RE = /=>\s*(?:[\w$.]*\.)?mk\s*\(/;
|
|
6887
6808
|
function analyzeMkInlineBuild(def) {
|
|
6888
6809
|
if (!def.build) return null;
|
|
6889
6810
|
if (def.type === void 0) return null;
|
|
6890
|
-
const src = (def
|
|
6811
|
+
const src = buildAnalysisSrc(def).trim();
|
|
6891
6812
|
const strict = src.match(MK_BUILD_RE);
|
|
6892
6813
|
if (strict) {
|
|
6893
6814
|
const mkType = strict[6];
|
|
6894
|
-
|
|
6815
|
+
if (mkType === def.type) return def.type;
|
|
6816
|
+
reportMkMiss(def.type, src, `it builds a "${mkType}" node, not "${def.type}"`);
|
|
6817
|
+
return null;
|
|
6895
6818
|
}
|
|
6896
6819
|
if (looseMkBuildRe(def.type).test(src)) return def.type;
|
|
6820
|
+
if (def.typeSrc !== void 0 && identMkBuildRe(def.typeSrc).test(src)) return def.type;
|
|
6821
|
+
if (LOOKS_LIKE_MK_RE.test(src)) {
|
|
6822
|
+
reportMkMiss(def.type, src, "its parameter list or argument order does not match `(children, fields, span, rawChildren, triviaLog) => mk(type, children, rawChildren, span, triviaLog)`");
|
|
6823
|
+
}
|
|
6897
6824
|
return null;
|
|
6898
6825
|
}
|
|
6826
|
+
function reportMkMiss(type, src, why) {
|
|
6827
|
+
recordDegradation({
|
|
6828
|
+
code: "mk-inline-missed",
|
|
6829
|
+
severity: "warn",
|
|
6830
|
+
where: `node("${type}")`,
|
|
6831
|
+
subject: `build reducer \`${src.split("\n")[0].slice(0, 80)}\``,
|
|
6832
|
+
fellBackTo: `looks like an inline-\`mk\` builder but ${why}, so every match pays a \`_build[n](...)\` call instead of an inlined object literal`,
|
|
6833
|
+
otherwise: "the node object would be constructed at the call site with no call frame"
|
|
6834
|
+
});
|
|
6835
|
+
}
|
|
6899
6836
|
function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
|
|
6900
6837
|
return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
|
|
6901
6838
|
}
|
|
@@ -7078,7 +7015,30 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
7078
7015
|
seen.delete(p);
|
|
7079
7016
|
}
|
|
7080
7017
|
}
|
|
7018
|
+
function tern(cond, whenTrue, whenFalse) {
|
|
7019
|
+
if (cond === "false") return whenFalse;
|
|
7020
|
+
if (cond === "true") return whenTrue;
|
|
7021
|
+
return `${cond} ? ${whenTrue} : ${whenFalse}`;
|
|
7022
|
+
}
|
|
7023
|
+
function notGate(cond) {
|
|
7024
|
+
if (cond === "false") return "true";
|
|
7025
|
+
if (cond === "true") return "false";
|
|
7026
|
+
return `!(${cond})`;
|
|
7027
|
+
}
|
|
7028
|
+
function orGate(a, b) {
|
|
7029
|
+
if (a === "false") return b;
|
|
7030
|
+
if (b === "false") return a;
|
|
7031
|
+
if (a === "true" || b === "true") return "true";
|
|
7032
|
+
return `${a} || ${b}`;
|
|
7033
|
+
}
|
|
7034
|
+
function andGate(a, b) {
|
|
7035
|
+
if (a === "false" || b === "false") return "false";
|
|
7036
|
+
if (a === "true") return b;
|
|
7037
|
+
if (b === "true") return a;
|
|
7038
|
+
return `${a} && ${b}`;
|
|
7039
|
+
}
|
|
7081
7040
|
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 }";
|
|
7041
|
+
var RAW_ENTRY_DECL = "const _rawEntry = (v, input, s, e, span) => (typeof v === 'object' && v !== null && (v._tag === 'node' || v._tag === 'leaf' || v._tag === 'parseError')) ? v : { _tag: 'leaf', value: typeof v === 'string' ? v : (typeof v === 'object' && v !== null ? input.slice(s, e) : ''), span: span ?? { start: s, end: e } }";
|
|
7082
7042
|
var LINE_TRACK_DECL = "const _trackLines = (_ctx, input, start, end) => { const from = _ctx._lineScannedTo ?? 0; if (end <= from) return; for (let i = from; i < end; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1); _ctx._lineScannedTo = end }";
|
|
7083
7043
|
var LINE_SPAN_DECL = "const _lineCol = (_ctx, offset) => { const starts = _ctx._lineStarts; let lo = 0, hi = starts.length - 1; while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (starts[mid] <= offset) lo = mid; else hi = mid - 1 } return [lo + 1, offset - starts[lo] + 1] }\nconst _spanLines = (_ctx, start, end) => { const s = _lineCol(_ctx, start), e = _lineCol(_ctx, end); return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] } }";
|
|
7084
7044
|
var _loweringSink = null;
|
|
@@ -7090,6 +7050,27 @@ function endLoweringCapture() {
|
|
|
7090
7050
|
_loweringSink = null;
|
|
7091
7051
|
return misses;
|
|
7092
7052
|
}
|
|
7053
|
+
var _inlineCapSink = null;
|
|
7054
|
+
function beginInlineCapCapture() {
|
|
7055
|
+
_inlineCapSink = [];
|
|
7056
|
+
}
|
|
7057
|
+
function endInlineCapCapture() {
|
|
7058
|
+
const sites = _inlineCapSink ?? [];
|
|
7059
|
+
_inlineCapSink = null;
|
|
7060
|
+
return sites;
|
|
7061
|
+
}
|
|
7062
|
+
function formatInlineCapSites(sites, max) {
|
|
7063
|
+
if (sites.length === 0) return [];
|
|
7064
|
+
const byFn = /* @__PURE__ */ new Map();
|
|
7065
|
+
for (const s of sites) {
|
|
7066
|
+
const cur = byFn.get(s.fn);
|
|
7067
|
+
if (cur) {
|
|
7068
|
+
cur.count += 1;
|
|
7069
|
+
cur.nodes += s.nodes;
|
|
7070
|
+
} else byFn.set(s.fn, { count: 1, nodes: s.nodes });
|
|
7071
|
+
}
|
|
7072
|
+
return [...byFn].map(([fn, { count, nodes }]) => `[parseman] inline-cap ${fn}: inline budget of ${max} node(s) spent \u2014 ${count} single-use ref(s) totalling ~${nodes} node(s) became called functions instead of inline bodies. Raise it with maxInline (or PARSEMAN_MAX_INLINE) if this rule is hot.`);
|
|
7073
|
+
}
|
|
7093
7074
|
function hasSelectedRootTrivia(ctx) {
|
|
7094
7075
|
return ctx.rootTriviaClassified === true;
|
|
7095
7076
|
}
|
|
@@ -7373,10 +7354,10 @@ function ensureTriviaFn(ctx) {
|
|
|
7373
7354
|
function ensureTriviaCaptureFn(ctx) {
|
|
7374
7355
|
return ensureTriviaFn(ctx);
|
|
7375
7356
|
}
|
|
7376
|
-
function firstSetCond(codeVar,
|
|
7377
|
-
if (
|
|
7378
|
-
if (
|
|
7379
|
-
return
|
|
7357
|
+
function firstSetCond(codeVar, fs3) {
|
|
7358
|
+
if (fs3.kind === "any") return "true";
|
|
7359
|
+
if (fs3.kind === "empty") return "false";
|
|
7360
|
+
return fs3.ranges.map(
|
|
7380
7361
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
7381
7362
|
).join(" || ");
|
|
7382
7363
|
}
|
|
@@ -7389,10 +7370,10 @@ function planDisjointDispatch(parsers) {
|
|
|
7389
7370
|
const cases = [];
|
|
7390
7371
|
let total = 0;
|
|
7391
7372
|
for (const p of parsers) {
|
|
7392
|
-
const
|
|
7393
|
-
if (
|
|
7373
|
+
const fs3 = p._meta.firstSet;
|
|
7374
|
+
if (fs3.kind !== "ranges") return { kind: "if" };
|
|
7394
7375
|
const pts = [];
|
|
7395
|
-
for (const r of
|
|
7376
|
+
for (const r of fs3.ranges) {
|
|
7396
7377
|
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
7397
7378
|
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
7398
7379
|
}
|
|
@@ -7785,9 +7766,9 @@ function emitRegex(def, ctx, pos, canMatchNewline = true) {
|
|
|
7785
7766
|
stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
|
|
7786
7767
|
return { stmts, valueVar: vv, endVar };
|
|
7787
7768
|
}
|
|
7788
|
-
function syncSentinelExpr(
|
|
7789
|
-
if (
|
|
7790
|
-
return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(
|
|
7769
|
+
function syncSentinelExpr(fs3) {
|
|
7770
|
+
if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return null;
|
|
7771
|
+
return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(fs3.ranges)} })`;
|
|
7791
7772
|
}
|
|
7792
7773
|
function emitSeqValues(def, ctx, pos) {
|
|
7793
7774
|
const startV = v(ctx, "_start");
|
|
@@ -7801,8 +7782,8 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
7801
7782
|
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${syncInV} = _ctx._sync`);
|
|
7802
7783
|
const publishSync = (i) => {
|
|
7803
7784
|
if (!ctx.recovery) return "";
|
|
7804
|
-
const
|
|
7805
|
-
const ref2 = syncSentinelExpr(
|
|
7785
|
+
const fs3 = def.parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" });
|
|
7786
|
+
const ref2 = syncSentinelExpr(fs3);
|
|
7806
7787
|
return `${ind(ctx)}if (_ctx._tolerant) _ctx._sync = ${ref2 ?? syncInV}`;
|
|
7807
7788
|
};
|
|
7808
7789
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
@@ -7838,9 +7819,11 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
7838
7819
|
const markLog = v(ctx, "_mklg");
|
|
7839
7820
|
const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
|
|
7840
7821
|
const scanEndV = v(ctx, "_sne");
|
|
7841
|
-
const
|
|
7822
|
+
const logV = v(ctx, "_tlg");
|
|
7823
|
+
const capArg = ctx.noHoist ? "0" : hasSelectedRootTrivia(ctx) ? `(${logV} !== undefined || _ctx._rootTriviaLog !== undefined) ? 2 : 0` : `${logV} !== undefined ? 2 : 0`;
|
|
7842
7824
|
stmts.push(
|
|
7843
|
-
`${ind(ctx)}const ${
|
|
7825
|
+
`${ind(ctx)}const ${logV} = _ctx._triviaLog`,
|
|
7826
|
+
`${ind(ctx)}const ${markLog} = ${logV} !== undefined ? ${logV}.length : 0`,
|
|
7844
7827
|
...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
|
|
7845
7828
|
`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
|
|
7846
7829
|
...emitLineTrack(ctx, curV, scanEndV)
|
|
@@ -7850,7 +7833,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
7850
7833
|
const endAfterV = v(ctx, "_sea");
|
|
7851
7834
|
stmts.push(
|
|
7852
7835
|
`${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
|
|
7853
|
-
`${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else { if (
|
|
7836
|
+
`${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else { if (${logV} !== undefined && ${logV}.length !== ${markLog}) ${logV}.length = ${markLog};${markRootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog};` : ""} }`
|
|
7854
7837
|
);
|
|
7855
7838
|
valueVars.push(r2.valueVar);
|
|
7856
7839
|
continue;
|
|
@@ -7916,8 +7899,8 @@ function canMatchEmptyAtStart(p) {
|
|
|
7916
7899
|
}
|
|
7917
7900
|
}
|
|
7918
7901
|
function needsFirstSetGuard(p) {
|
|
7919
|
-
const
|
|
7920
|
-
return
|
|
7902
|
+
const fs3 = p._meta.firstSet;
|
|
7903
|
+
return fs3.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
7921
7904
|
}
|
|
7922
7905
|
function coverageHit(ctx, id, offset = "_pos", end) {
|
|
7923
7906
|
return id === void 0 ? [] : [
|
|
@@ -8922,9 +8905,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
8922
8905
|
endVar: pos
|
|
8923
8906
|
};
|
|
8924
8907
|
}
|
|
8925
|
-
function emitRouted(ctx, pos) {
|
|
8908
|
+
function emitRouted(ctx, pos, def) {
|
|
8926
8909
|
const local = ctx.routedLocal;
|
|
8910
|
+
const fallback = def.fallback;
|
|
8927
8911
|
if (local !== void 0) {
|
|
8912
|
+
if (fallback !== void 0 && local.startVar !== pos) {
|
|
8913
|
+
return emitRoutedWithFallback(ctx, pos, fallback, {
|
|
8914
|
+
test: `${local.startVar} === ${pos}`,
|
|
8915
|
+
valExpr: local.valueVar,
|
|
8916
|
+
startExpr: local.startVar,
|
|
8917
|
+
endExpr: local.endVar
|
|
8918
|
+
});
|
|
8919
|
+
}
|
|
8928
8920
|
return {
|
|
8929
8921
|
stmts: [
|
|
8930
8922
|
...emitIfFail(ctx, `${local.startVar} !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
@@ -8935,9 +8927,19 @@ function emitRouted(ctx, pos) {
|
|
|
8935
8927
|
};
|
|
8936
8928
|
}
|
|
8937
8929
|
const item = v(ctx, "_rt");
|
|
8930
|
+
const read = `${ind(ctx)}const ${item} = _ctx._routed`;
|
|
8931
|
+
if (fallback !== void 0) {
|
|
8932
|
+
const r = emitRoutedWithFallback(ctx, pos, fallback, {
|
|
8933
|
+
test: `${item} !== undefined && ${item}.span.start === ${pos}`,
|
|
8934
|
+
valExpr: `${item}.value`,
|
|
8935
|
+
startExpr: `${item}.span.start`,
|
|
8936
|
+
endExpr: `${item}.span.end`
|
|
8937
|
+
});
|
|
8938
|
+
return { ...r, stmts: [read, ...r.stmts] };
|
|
8939
|
+
}
|
|
8938
8940
|
return {
|
|
8939
8941
|
stmts: [
|
|
8940
|
-
|
|
8942
|
+
read,
|
|
8941
8943
|
...emitIfFail(ctx, `${item} === undefined || ${item}.span.start !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
8942
8944
|
...emitLeafCapture(ctx, `${item}.value`, `${item}.span.start`, `${item}.span.end`)
|
|
8943
8945
|
],
|
|
@@ -8945,6 +8947,30 @@ function emitRouted(ctx, pos) {
|
|
|
8945
8947
|
endVar: `${item}.span.end`
|
|
8946
8948
|
};
|
|
8947
8949
|
}
|
|
8950
|
+
function emitRoutedWithFallback(ctx, pos, fallback, routed2) {
|
|
8951
|
+
const valV = v(ctx, "_rtv");
|
|
8952
|
+
const endV = v(ctx, "_rte");
|
|
8953
|
+
const i = ind(ctx);
|
|
8954
|
+
ctx.indent++;
|
|
8955
|
+
const capture = emitLeafCapture(ctx, routed2.valExpr, routed2.startExpr, routed2.endExpr);
|
|
8956
|
+
const fb = emit(fallback, ctx, pos);
|
|
8957
|
+
const inner = ind(ctx);
|
|
8958
|
+
ctx.indent--;
|
|
8959
|
+
return {
|
|
8960
|
+
stmts: [
|
|
8961
|
+
`${i}let ${valV}, ${endV}`,
|
|
8962
|
+
`${i}if (${routed2.test}) {`,
|
|
8963
|
+
...capture,
|
|
8964
|
+
`${inner}${valV} = ${routed2.valExpr}; ${endV} = ${routed2.endExpr}`,
|
|
8965
|
+
`${i}} else {`,
|
|
8966
|
+
...fb.stmts,
|
|
8967
|
+
`${inner}${valV} = ${fb.valueVar}; ${endV} = ${fb.endVar}`,
|
|
8968
|
+
`${i}}`
|
|
8969
|
+
],
|
|
8970
|
+
valueVar: valV,
|
|
8971
|
+
endVar: endV
|
|
8972
|
+
};
|
|
8973
|
+
}
|
|
8948
8974
|
function escapeTokenLiteral(value) {
|
|
8949
8975
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
8950
8976
|
}
|
|
@@ -9126,22 +9152,16 @@ function emitNode(def, ctx, pos) {
|
|
|
9126
9152
|
const smk = structural ? v(ctx, "_smk") : null;
|
|
9127
9153
|
if (structural) ctx.needsHostReads = true;
|
|
9128
9154
|
const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
|
|
9129
|
-
const
|
|
9130
|
-
const
|
|
9131
|
-
const profileRecognizer = recV;
|
|
9132
|
-
const profileCapture = capV;
|
|
9133
|
-
const cstMode = ctx.hostMode === "cst";
|
|
9134
|
-
const directCstV = !structural && !cstMode && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
|
|
9135
|
-
const directCstGate = directCstV === null ? "false" : profileCapture;
|
|
9155
|
+
const profileRecognizer = "false";
|
|
9156
|
+
const profileCapture = "false";
|
|
9136
9157
|
const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
|
|
9137
9158
|
const chNeededExpr = def.unwrap || def.collapse ? "true" : `(_ctx._pmReadsCh ??= (_ctx.build === undefined || _ctx.build._parsemanReadsChildren !== false || _ctx.build._parsemanCstCollapse !== undefined))`;
|
|
9138
|
-
const chAlloc = def.unwrap || def.collapse ?
|
|
9139
|
-
const
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
|
|
9159
|
+
const chAlloc = def.unwrap || def.collapse ? tern(profileRecognizer, "undefined", "[]") : tern(profileRecognizer, "undefined", `${tern(orGate(profileCapture, chNeededExpr), "[]", "undefined")}`);
|
|
9160
|
+
const dcstAlloc = "undefined";
|
|
9161
|
+
const allocStmt = structural ? `${i}const ${capTLv} = ${andGate(notGate(profileRecognizer), orGate(profileCapture, structuralCapturesTrivia ? "true" : hostTriviaGate))}, ${capSTv} = ${andGate(notGate(orGate(profileRecognizer, profileCapture)), "(_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))")}${capFv ? `, ${capFv} = ${andGate(notGate(profileRecognizer), orGate(profileCapture, "(_ctx.build !== undefined && _hostReads(_ctx.build, 2))"))}` : ""}
|
|
9162
|
+
${i}const ${chV} = ${chAlloc}, ${rawV} = ${tern(profileRecognizer, "undefined", "[]")}, ${tlV} = ${tern(profileRecognizer, "undefined", innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`)}` : capturesTrivia ? `${i}const ${capTLv} = ${notGate(profileRecognizer)}, ${chV} = ${tern(profileRecognizer, "undefined", capturesChildren ? "[]" : dcstAlloc)}, ${rawV} = ${tern(profileRecognizer, "undefined", capturesRaw ? "[]" : dcstAlloc)}, ${tlV} = ${tern(profileRecognizer, "undefined", "[]")}` : `${i}const ${chV} = ${tern(profileRecognizer, "undefined", capturesChildren ? "[]" : dcstAlloc)}, ${rawV} = ${tern(profileRecognizer, "undefined", capturesRaw ? "[]" : dcstAlloc)}`;
|
|
9143
9163
|
const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
|
|
9144
|
-
const fieldsOn = structural ? capFv ?? "false" :
|
|
9164
|
+
const fieldsOn = structural ? capFv ?? "false" : tern(profileRecognizer, "false", capturesFields ? "true" : "false");
|
|
9145
9165
|
const sf = hasFields ? v(ctx, "_sf") : null;
|
|
9146
9166
|
const fArr = hasFields ? v(ctx, "_fa") : null;
|
|
9147
9167
|
const fObj = hasFields ? v(ctx, "_fields") : "undefined";
|
|
@@ -9150,7 +9170,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9150
9170
|
const installTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${capTLv ?? "false"}; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build !== undefined && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}` : "";
|
|
9151
9171
|
const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
|
|
9152
9172
|
const preGuard = [];
|
|
9153
|
-
if (!ctx.recovery && !ctx.coverage &&
|
|
9173
|
+
if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
|
|
9154
9174
|
const gcV = v(ctx, "_ngc");
|
|
9155
9175
|
const gExp = armStaticExpected(ctx, def.parser);
|
|
9156
9176
|
const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
|
|
@@ -9161,7 +9181,6 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9161
9181
|
}
|
|
9162
9182
|
const stmts = [
|
|
9163
9183
|
...preGuard,
|
|
9164
|
-
profHoist,
|
|
9165
9184
|
allocStmt,
|
|
9166
9185
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
|
|
9167
9186
|
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}${installTrivia}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
|
|
@@ -9178,21 +9197,13 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9178
9197
|
}
|
|
9179
9198
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}${restoreTrivia}${sf ? `; _ctx._fields = ${sf}` : ""}`);
|
|
9180
9199
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
9181
|
-
stmts.push(
|
|
9182
|
-
`${i}if (_ctx._pmProfile) {`,
|
|
9183
|
-
`${i} _ctx._pmProfile.nodes++`,
|
|
9184
|
-
`${i} if (${profileCapture}) {`,
|
|
9185
|
-
`${i} _ctx._pmProfile.childSlots += ${chV}.length; _ctx._pmProfile.rawSlots += ${rawV}.length; _ctx._pmProfile.triviaSlots += ${tlV}.length${fArr ? `; _ctx._pmProfile.fieldSlots += ${fArr} ? ${fArr}.length : 0` : ""}`,
|
|
9186
|
-
`${i} }`,
|
|
9187
|
-
`${i}}`
|
|
9188
|
-
);
|
|
9189
9200
|
let stV = "undefined";
|
|
9190
9201
|
if (structural) {
|
|
9191
9202
|
stV = v(ctx, "_nst");
|
|
9192
9203
|
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
9193
9204
|
} else if (clonesState) {
|
|
9194
9205
|
stV = v(ctx, "_nst");
|
|
9195
|
-
stmts.push(`${i}const ${stV} =
|
|
9206
|
+
stmts.push(`${i}const ${stV} = ${andGate(notGate(orGate(profileRecognizer, profileCapture)), "_ctx.state !== undefined")} ? Object.assign({}, _ctx.state) : undefined`);
|
|
9196
9207
|
}
|
|
9197
9208
|
if (sf && fArr) {
|
|
9198
9209
|
const fe = v(ctx, "_fe");
|
|
@@ -9216,19 +9227,22 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9216
9227
|
if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
|
|
9217
9228
|
const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: ${nodeSpanV}, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, nodeSpanV, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
|
|
9218
9229
|
const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
|
|
9219
|
-
const hostBuildExpr = `_ctx.
|
|
9230
|
+
const hostBuildExpr = `_ctx.build(${hostBuildArgs})`;
|
|
9220
9231
|
const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
|
|
9221
|
-
const
|
|
9232
|
+
const hostCollapses = structural || cstOut;
|
|
9233
|
+
const hostCollapseExpr = hostCollapses ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
|
|
9222
9234
|
const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
|
|
9223
9235
|
const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
|
|
9224
9236
|
const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
);
|
|
9237
|
+
const ndGate = orGate(profileRecognizer, profileCapture);
|
|
9238
|
+
const recGate = notGate(profileRecognizer);
|
|
9239
|
+
const pushIndent = recGate === "true" ? i : `${i} `;
|
|
9240
|
+
const pushCh = `${pushIndent}if (${sc}) ${sc}.push(${ndV})`;
|
|
9241
|
+
ctx.needsRawEntry = true;
|
|
9242
|
+
const pushRaw = `${pushIndent}if (${sr}) ${sr}.push(_rawEntry(${ndV}, input, ${pos}, ${endVar}${ctx.lineTracking ? `, ${nodeSpanV}` : ""}))`;
|
|
9243
|
+
stmts.push(`${i}const ${ndV} = ${tern(ndGate, "undefined", `(${finalExpr})`)}`);
|
|
9244
|
+
if (recGate === "true") stmts.push(pushCh, pushRaw);
|
|
9245
|
+
else stmts.push(`${i}if (${recGate}) {`, pushCh, pushRaw, `${i}}`);
|
|
9232
9246
|
return { stmts, valueVar: ndV, endVar };
|
|
9233
9247
|
}
|
|
9234
9248
|
function emitRuntimeFallback(parser2, ctx, pos) {
|
|
@@ -9257,7 +9271,12 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
9257
9271
|
} catch {
|
|
9258
9272
|
return emitRuntimeFallback(p, ctx, pos);
|
|
9259
9273
|
}
|
|
9260
|
-
|
|
9274
|
+
const cost = usage.sizes.get(resolved) ?? 1;
|
|
9275
|
+
if (cost <= ctx.inlineLeft) {
|
|
9276
|
+
ctx.inlineLeft -= cost;
|
|
9277
|
+
return emit(resolved, ctx, pos);
|
|
9278
|
+
}
|
|
9279
|
+
_inlineCapSink?.push({ fn: ctx.currentFnName ?? "<root>", nodes: cost });
|
|
9261
9280
|
}
|
|
9262
9281
|
if (!ctx.namedParsers.has(p)) {
|
|
9263
9282
|
const fnName2 = canonical2 ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
@@ -9272,6 +9291,9 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
9272
9291
|
const savedIndent = ctx.indent;
|
|
9273
9292
|
const savedFailLabel = ctx.failLabel;
|
|
9274
9293
|
const savedRecord = ctx.recordFail;
|
|
9294
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
9295
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
9296
|
+
ctx.currentFnName = fnName2;
|
|
9275
9297
|
const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
|
|
9276
9298
|
const savedActiveRule = ctx.activeCoverageRuleId;
|
|
9277
9299
|
const savedSuppressFailure = ctx.suppressCoverageFailure;
|
|
@@ -9286,6 +9308,8 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
9286
9308
|
ctx.indent = savedIndent;
|
|
9287
9309
|
ctx.failLabel = savedFailLabel;
|
|
9288
9310
|
ctx.recordFail = savedRecord;
|
|
9311
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
9312
|
+
ctx.currentFnName = savedFnName;
|
|
9289
9313
|
ctx.activeCoverageRuleId = savedActiveRule;
|
|
9290
9314
|
ctx.suppressCoverageFailure = savedSuppressFailure;
|
|
9291
9315
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
|
|
@@ -9380,6 +9404,9 @@ function emit(p, ctx, pos) {
|
|
|
9380
9404
|
const savedIndent = ctx.indent;
|
|
9381
9405
|
const savedFailLabel = ctx.failLabel;
|
|
9382
9406
|
const savedRecord = ctx.recordFail;
|
|
9407
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
9408
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
9409
|
+
ctx.currentFnName = fnName;
|
|
9383
9410
|
ctx.indent = 1;
|
|
9384
9411
|
ctx.failLabel = "_pfail";
|
|
9385
9412
|
ctx.recordFail = true;
|
|
@@ -9387,6 +9414,8 @@ function emit(p, ctx, pos) {
|
|
|
9387
9414
|
ctx.indent = savedIndent;
|
|
9388
9415
|
ctx.failLabel = savedFailLabel;
|
|
9389
9416
|
ctx.recordFail = savedRecord;
|
|
9417
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
9418
|
+
ctx.currentFnName = savedFnName;
|
|
9390
9419
|
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
9391
9420
|
return instrument(emitNamedFnCall(ctx, fnName, pos));
|
|
9392
9421
|
}
|
|
@@ -9580,7 +9609,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
9580
9609
|
case "peek":
|
|
9581
9610
|
return emitPeek(def, ctx, pos);
|
|
9582
9611
|
case "routed":
|
|
9583
|
-
return emitRouted(ctx, pos);
|
|
9612
|
+
return emitRouted(ctx, pos, def);
|
|
9584
9613
|
case "node":
|
|
9585
9614
|
return emitNode(def, ctx, pos);
|
|
9586
9615
|
case "scanTo":
|
|
@@ -9615,6 +9644,9 @@ function emitDispatch(p, ctx, pos) {
|
|
|
9615
9644
|
const savedIndent = ctx.indent;
|
|
9616
9645
|
const savedFailLabel = ctx.failLabel;
|
|
9617
9646
|
const savedRecord = ctx.recordFail;
|
|
9647
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
9648
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
9649
|
+
ctx.currentFnName = fnName;
|
|
9618
9650
|
ctx.indent = 1;
|
|
9619
9651
|
ctx.failLabel = "_pfail";
|
|
9620
9652
|
ctx.recordFail = true;
|
|
@@ -9622,6 +9654,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
9622
9654
|
ctx.indent = savedIndent;
|
|
9623
9655
|
ctx.failLabel = savedFailLabel;
|
|
9624
9656
|
ctx.recordFail = savedRecord;
|
|
9657
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
9658
|
+
ctx.currentFnName = savedFnName;
|
|
9625
9659
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
9626
9660
|
}
|
|
9627
9661
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -9780,12 +9814,16 @@ function childrenOf3(def) {
|
|
|
9780
9814
|
return [def.parser, def.sentinel];
|
|
9781
9815
|
case "scanTo":
|
|
9782
9816
|
return [def.sentinel, ...def.skip];
|
|
9817
|
+
// A `routed()` fallback IS emitted at this site (emitRouted), so the usage
|
|
9818
|
+
// analysis must see the edge — otherwise a fallback shared by several routed()
|
|
9819
|
+
// sites is miscounted as single-use and inlined at each.
|
|
9820
|
+
case "routed":
|
|
9821
|
+
return def.fallback ? [def.fallback] : [];
|
|
9783
9822
|
case "lazy":
|
|
9784
9823
|
case "literal":
|
|
9785
9824
|
case "regex":
|
|
9786
9825
|
case "keywords":
|
|
9787
9826
|
case "guard":
|
|
9788
|
-
case "routed":
|
|
9789
9827
|
case "unknown":
|
|
9790
9828
|
return [];
|
|
9791
9829
|
}
|
|
@@ -9806,6 +9844,17 @@ function isHoistableTag(tag) {
|
|
|
9806
9844
|
return HOISTABLE_TAGS.has(tag);
|
|
9807
9845
|
}
|
|
9808
9846
|
var HOIST_MIN_SUBTREE = 3;
|
|
9847
|
+
var INLINE_MAX_NODES = 1e3;
|
|
9848
|
+
function resolveInlineMax(explicit) {
|
|
9849
|
+
if (explicit !== void 0) return explicit;
|
|
9850
|
+
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_MAX_INLINE : void 0;
|
|
9851
|
+
if (env !== void 0 && env !== "") {
|
|
9852
|
+
if (env === "off" || env === "infinity") return Infinity;
|
|
9853
|
+
const n = Number(env);
|
|
9854
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
9855
|
+
}
|
|
9856
|
+
return INLINE_MAX_NODES;
|
|
9857
|
+
}
|
|
9809
9858
|
function analyzeLazyUsage(root) {
|
|
9810
9859
|
return analyzeLazyUsageMulti([root]);
|
|
9811
9860
|
}
|
|
@@ -9911,102 +9960,6 @@ function ruleDependencies(ruleMap) {
|
|
|
9911
9960
|
}
|
|
9912
9961
|
return deps;
|
|
9913
9962
|
}
|
|
9914
|
-
function resolveGatingLevel(opt) {
|
|
9915
|
-
const explicit = typeof opt === "string" ? opt : opt?.level;
|
|
9916
|
-
if (explicit !== void 0) return explicit;
|
|
9917
|
-
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_GATING : void 0;
|
|
9918
|
-
if (env === "off" || env === "warn" || env === "error") return env;
|
|
9919
|
-
return "warn";
|
|
9920
|
-
}
|
|
9921
|
-
function runGatingDiagnostic(combinator, opt) {
|
|
9922
|
-
return reportGating(opt, (o) => analyzeGating(combinator, o));
|
|
9923
|
-
}
|
|
9924
|
-
function runGatingDiagnosticRules(ruleMap, opt) {
|
|
9925
|
-
return reportGating(opt, (o) => analyzeGatingRules(ruleMap, o));
|
|
9926
|
-
}
|
|
9927
|
-
function runFusedGatingDiagnostic(getRuleMaps, opt, getOpaque) {
|
|
9928
|
-
if (resolveGatingLevel(opt) === "off") return void 0;
|
|
9929
|
-
const ruleMaps = getRuleMaps();
|
|
9930
|
-
const opaque = getOpaque?.() ?? [];
|
|
9931
|
-
const opaqueFindings = opaque.map((o) => ({
|
|
9932
|
-
rule: o.ruleNames.length > 0 ? o.ruleNames.join(", ") : `<artifact ${o.ns}>`,
|
|
9933
|
-
kind: "opaque-artifact",
|
|
9934
|
-
reason: `precompiled artifact "${o.ns}" carries compiled rule functions, not re-lowerable IR \u2014 its choices were not examined at this fuse.`
|
|
9935
|
-
}));
|
|
9936
|
-
if (!ruleMaps.some((map) => hasExternalRuleRef(map))) {
|
|
9937
|
-
if (opaqueFindings.length === 0) return void 0;
|
|
9938
|
-
return reportGating(opt, () => ({
|
|
9939
|
-
totalChoices: 0,
|
|
9940
|
-
gated: 0,
|
|
9941
|
-
recoverable: 0,
|
|
9942
|
-
unanalysable: opaqueFindings,
|
|
9943
|
-
ungated: [],
|
|
9944
|
-
accepted: [],
|
|
9945
|
-
deferred: [],
|
|
9946
|
-
acceptedUnused: [],
|
|
9947
|
-
choices: [],
|
|
9948
|
-
antiPatterns: []
|
|
9949
|
-
}));
|
|
9950
|
-
}
|
|
9951
|
-
const winners = /* @__PURE__ */ new Map();
|
|
9952
|
-
for (const map of ruleMaps) for (const [name, rule] of map) {
|
|
9953
|
-
if (rule._def.tag === "lazy") {
|
|
9954
|
-
try {
|
|
9955
|
-
rule._def.thunk();
|
|
9956
|
-
} catch {
|
|
9957
|
-
continue;
|
|
9958
|
-
}
|
|
9959
|
-
}
|
|
9960
|
-
winners.set(name, rule);
|
|
9961
|
-
}
|
|
9962
|
-
const entries = [...winners];
|
|
9963
|
-
return reportGating(opt, (o) => {
|
|
9964
|
-
const deferredHere = new Set(analyzeGatingRules(entries, o).deferred.map((c) => c.id));
|
|
9965
|
-
const fused = analyzeGatingRules(entries, { ...o, resolveRef: (name) => winners.get(name) });
|
|
9966
|
-
return {
|
|
9967
|
-
...fused,
|
|
9968
|
-
ungated: fused.ungated.filter((c) => deferredHere.has(c.id)),
|
|
9969
|
-
antiPatterns: [],
|
|
9970
|
-
unanalysable: [...fused.unanalysable, ...opaqueFindings]
|
|
9971
|
-
};
|
|
9972
|
-
});
|
|
9973
|
-
}
|
|
9974
|
-
function reportGating(opt, analyze) {
|
|
9975
|
-
const level = resolveGatingLevel(opt);
|
|
9976
|
-
if (level === "off") return void 0;
|
|
9977
|
-
const obj = opt !== null && typeof opt === "object" ? opt : void 0;
|
|
9978
|
-
const analyzeOpts = obj?.accept !== void 0 || obj?.entryName !== void 0 ? { ...obj.accept !== void 0 ? { accept: obj.accept } : {}, ...obj.entryName !== void 0 ? { entryName: obj.entryName } : {} } : void 0;
|
|
9979
|
-
let report;
|
|
9980
|
-
try {
|
|
9981
|
-
report = analyze(analyzeOpts);
|
|
9982
|
-
} catch (e) {
|
|
9983
|
-
const reason = e instanceof Error ? `${e.name}: ${e.message}` : String(e);
|
|
9984
|
-
const failed = {
|
|
9985
|
-
totalChoices: 0,
|
|
9986
|
-
gated: 0,
|
|
9987
|
-
recoverable: 0,
|
|
9988
|
-
unanalysable: [{ rule: "<whole grammar>", kind: "not-a-combinator", reason: `gating analysis threw \u2014 ${reason}` }],
|
|
9989
|
-
ungated: [],
|
|
9990
|
-
accepted: [],
|
|
9991
|
-
deferred: [],
|
|
9992
|
-
acceptedUnused: [],
|
|
9993
|
-
choices: [],
|
|
9994
|
-
antiPatterns: []
|
|
9995
|
-
};
|
|
9996
|
-
const failLines = formatGatingWarnings(failed);
|
|
9997
|
-
if (level === "error") throw new Error(`parseman: gating analysis FAILED (no verdict available)
|
|
9998
|
-
${failLines.join("\n")}`);
|
|
9999
|
-
for (const l of failLines) console.warn(l);
|
|
10000
|
-
return failed;
|
|
10001
|
-
}
|
|
10002
|
-
const lines = formatGatingWarnings(report);
|
|
10003
|
-
if (lines.length > 0) {
|
|
10004
|
-
if (level === "error") throw new Error(`parseman: ${report.ungated.length} ungated hot choice(s) / ${report.antiPatterns.length} anti-pattern(s) / ${report.unanalysable.length} unanalysable rule(s)
|
|
10005
|
-
${lines.join("\n")}`);
|
|
10006
|
-
for (const l of lines) console.warn(l);
|
|
10007
|
-
}
|
|
10008
|
-
return report;
|
|
10009
|
-
}
|
|
10010
9963
|
function resolveDuplicationLevel(opt) {
|
|
10011
9964
|
const explicit = typeof opt === "string" ? opt : opt?.level;
|
|
10012
9965
|
if (explicit !== void 0) return explicit;
|
|
@@ -10049,7 +10002,17 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
|
|
|
10049
10002
|
return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
|
|
10050
10003
|
}
|
|
10051
10004
|
function compile(combinator, mapFnSources, opts) {
|
|
10052
|
-
const
|
|
10005
|
+
const drain = beginCompileDegradationDrain();
|
|
10006
|
+
let completed = false;
|
|
10007
|
+
try {
|
|
10008
|
+
const compiled = compileImpl(combinator, mapFnSources, opts);
|
|
10009
|
+
completed = true;
|
|
10010
|
+
return compiled;
|
|
10011
|
+
} finally {
|
|
10012
|
+
drain(completed);
|
|
10013
|
+
}
|
|
10014
|
+
}
|
|
10015
|
+
function compileImpl(combinator, mapFnSources, opts) {
|
|
10053
10016
|
runDuplicationDiagnostic(combinator, opts?.duplication);
|
|
10054
10017
|
markUnusedValues(combinator);
|
|
10055
10018
|
const grammarTrivia = combinator._meta.grammarTrivia;
|
|
@@ -10057,9 +10020,12 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10057
10020
|
const grammarScanSkip = combinator._meta.grammarScanSkip;
|
|
10058
10021
|
const grammarHostMode = combinator._meta.grammarHostMode;
|
|
10059
10022
|
const grammarTrackLines = combinator._meta.grammarTrackLines;
|
|
10023
|
+
const _inlineMax = resolveInlineMax(opts?.maxInline);
|
|
10060
10024
|
const ctx = {
|
|
10061
10025
|
vars: 0,
|
|
10062
10026
|
indent: 1,
|
|
10027
|
+
inlineMax: _inlineMax,
|
|
10028
|
+
inlineLeft: _inlineMax,
|
|
10063
10029
|
regexDecls: [],
|
|
10064
10030
|
regexMap: /* @__PURE__ */ new Map(),
|
|
10065
10031
|
expectedDecls: [],
|
|
@@ -10105,11 +10071,13 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10105
10071
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
10106
10072
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
10107
10073
|
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
10074
|
+
const rawEntryDecls = ctx.needsRawEntry ? [RAW_ENTRY_DECL] : [];
|
|
10108
10075
|
const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
|
|
10109
10076
|
const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
|
|
10110
10077
|
const source = [
|
|
10111
10078
|
...emptyTlDecls,
|
|
10112
10079
|
...hostReadsDecls,
|
|
10080
|
+
...rawEntryDecls,
|
|
10113
10081
|
...lineTrackDecls,
|
|
10114
10082
|
...lineSpanDecls,
|
|
10115
10083
|
...ctx.regexDecls,
|
|
@@ -10124,6 +10092,7 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10124
10092
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
10125
10093
|
...emptyTlDecls,
|
|
10126
10094
|
...hostReadsDecls,
|
|
10095
|
+
...rawEntryDecls,
|
|
10127
10096
|
...lineTrackDecls,
|
|
10128
10097
|
...lineSpanDecls,
|
|
10129
10098
|
...ctx.regexDecls,
|
|
@@ -10173,8 +10142,7 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10173
10142
|
const compiledMeta = {
|
|
10174
10143
|
source,
|
|
10175
10144
|
inlineExpression,
|
|
10176
|
-
...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
|
|
10177
|
-
...gatingReport === void 0 ? {} : { gating: gatingReport }
|
|
10145
|
+
...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
|
|
10178
10146
|
};
|
|
10179
10147
|
if (!ctx.lineTracking) {
|
|
10180
10148
|
return {
|
|
@@ -10239,7 +10207,6 @@ function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels, ambien
|
|
|
10239
10207
|
return `Object.assign(${fnSource}, { _meta: {${labels === void 0 ? "" : ` triviaKindLabels: ${JSON.stringify(labels)},`}${classified === void 0 ? "" : " rootTriviaClassified: true,"} } })`;
|
|
10240
10208
|
}
|
|
10241
10209
|
function compileRuleMap(ruleMap, opts) {
|
|
10242
|
-
runGatingDiagnosticRules(ruleMap, opts?.gating);
|
|
10243
10210
|
runDuplicationDiagnosticRules(ruleMap, opts?.duplication);
|
|
10244
10211
|
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
10245
10212
|
const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
|
|
@@ -10248,9 +10215,12 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
10248
10215
|
const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
10249
10216
|
const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
|
|
10250
10217
|
const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
|
|
10218
|
+
const _inlineMax = resolveInlineMax(opts?.maxInline);
|
|
10251
10219
|
const ctx = {
|
|
10252
10220
|
vars: 0,
|
|
10253
10221
|
indent: 1,
|
|
10222
|
+
inlineMax: _inlineMax,
|
|
10223
|
+
inlineLeft: _inlineMax,
|
|
10254
10224
|
regexDecls: [],
|
|
10255
10225
|
regexMap: /* @__PURE__ */ new Map(),
|
|
10256
10226
|
expectedDecls: [],
|
|
@@ -10316,6 +10286,7 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
10316
10286
|
const hoistedDecls = [
|
|
10317
10287
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
10318
10288
|
ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
|
|
10289
|
+
ctx.needsRawEntry ? ` ${RAW_ENTRY_DECL}` : "",
|
|
10319
10290
|
ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "",
|
|
10320
10291
|
ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "",
|
|
10321
10292
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
@@ -10388,7 +10359,6 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
10388
10359
|
}
|
|
10389
10360
|
function compileLinkable(ruleMapArg, ns, opts) {
|
|
10390
10361
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
10391
|
-
if (opts?.gating !== void 0) runGatingDiagnosticRules(ruleMapArg, opts.gating);
|
|
10392
10362
|
if (opts?.duplication !== void 0) runDuplicationDiagnosticRules(ruleMapArg, opts.duplication);
|
|
10393
10363
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
10394
10364
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
@@ -10407,9 +10377,12 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10407
10377
|
}
|
|
10408
10378
|
});
|
|
10409
10379
|
const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
|
|
10380
|
+
const _inlineMax = resolveInlineMax(opts?.maxInline);
|
|
10410
10381
|
const ctx = {
|
|
10411
10382
|
vars: 0,
|
|
10412
10383
|
indent: 1,
|
|
10384
|
+
inlineMax: _inlineMax,
|
|
10385
|
+
inlineLeft: _inlineMax,
|
|
10413
10386
|
regexDecls: [],
|
|
10414
10387
|
regexMap: /* @__PURE__ */ new Map(),
|
|
10415
10388
|
expectedDecls: [],
|
|
@@ -10505,6 +10478,9 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10505
10478
|
}
|
|
10506
10479
|
}
|
|
10507
10480
|
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
10481
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
10482
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
10483
|
+
ctx.currentFnName = fn;
|
|
10508
10484
|
ctx.indent = 1;
|
|
10509
10485
|
ctx.failLabel = "_pfail";
|
|
10510
10486
|
ctx.recordFail = true;
|
|
@@ -10520,6 +10496,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10520
10496
|
ctx.indent = savedIndent;
|
|
10521
10497
|
ctx.failLabel = savedFail;
|
|
10522
10498
|
ctx.recordFail = savedRec;
|
|
10499
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
10500
|
+
ctx.currentFnName = savedFnName;
|
|
10523
10501
|
const ruleId = ctx.coverage?.plan.rules.get(rule);
|
|
10524
10502
|
const stmts = ruleId === void 0 ? body.stmts : [
|
|
10525
10503
|
`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
|
|
@@ -10604,6 +10582,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10604
10582
|
deps: ruleDependencies(ruleMap),
|
|
10605
10583
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
10606
10584
|
needsHostReads: !!ctx.needsHostReads,
|
|
10585
|
+
needsRawEntry: !!ctx.needsRawEntry,
|
|
10607
10586
|
hostMode: ctx.hostMode ?? "ast",
|
|
10608
10587
|
hostBranchElided: !!ctx.hostBranchElided,
|
|
10609
10588
|
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
@@ -10678,14 +10657,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
|
|
|
10678
10657
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
10679
10658
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
10680
10659
|
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
10660
|
+
const rawEntryDecl = ctx.needsRawEntry ? ` ${RAW_ENTRY_DECL}` : "";
|
|
10681
10661
|
const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
|
|
10682
10662
|
const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
|
|
10683
|
-
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
|
|
10663
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!rawEntryDecl || !!lineTrackDecl || !!lineSpanDecl;
|
|
10684
10664
|
if (!needsWrapper) return innerFn;
|
|
10685
10665
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
10686
10666
|
const hoistedDecls = [
|
|
10687
10667
|
emptyTlDecl,
|
|
10688
10668
|
hostReadsDecl,
|
|
10669
|
+
rawEntryDecl,
|
|
10689
10670
|
lineTrackDecl,
|
|
10690
10671
|
lineSpanDecl,
|
|
10691
10672
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
@@ -10755,18 +10736,19 @@ function childrenOf4(def) {
|
|
|
10755
10736
|
return [def.main, def.skipped];
|
|
10756
10737
|
case "scanTo":
|
|
10757
10738
|
return [def.sentinel, ...def.skip];
|
|
10739
|
+
case "routed":
|
|
10740
|
+
return def.fallback ? [def.fallback] : [];
|
|
10758
10741
|
case "lazy":
|
|
10759
10742
|
case "literal":
|
|
10760
10743
|
case "regex":
|
|
10761
10744
|
case "keywords":
|
|
10762
|
-
case "routed":
|
|
10763
10745
|
case "guard":
|
|
10764
10746
|
case "recover":
|
|
10765
10747
|
case "unknown":
|
|
10766
10748
|
return [];
|
|
10767
10749
|
}
|
|
10768
10750
|
}
|
|
10769
|
-
var
|
|
10751
|
+
var ruleNameOf3 = (c) => c._ruleName;
|
|
10770
10752
|
function lazyTarget(c) {
|
|
10771
10753
|
if (c._def.tag !== "lazy") return null;
|
|
10772
10754
|
try {
|
|
@@ -10841,7 +10823,7 @@ function evalRuleMapIR(ir) {
|
|
|
10841
10823
|
l._def.fnSrc = src;
|
|
10842
10824
|
return l;
|
|
10843
10825
|
};
|
|
10844
|
-
const _nd = (type, child, src, opts, staticError) => {
|
|
10826
|
+
const _nd = (type, child, src, opts, staticError, sigSrc) => {
|
|
10845
10827
|
if (staticError !== void 0 && staticError.length > 0) {
|
|
10846
10828
|
throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
|
|
10847
10829
|
}
|
|
@@ -10850,6 +10832,7 @@ function evalRuleMapIR(ir) {
|
|
|
10850
10832
|
}), opts);
|
|
10851
10833
|
n._def.buildSrc = src;
|
|
10852
10834
|
if (staticError !== void 0) n._def.buildStaticError = staticError;
|
|
10835
|
+
if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
|
|
10853
10836
|
return n;
|
|
10854
10837
|
};
|
|
10855
10838
|
const _gch = (items) => {
|
|
@@ -11002,7 +10985,7 @@ var Serializer = class {
|
|
|
11002
10985
|
if (seen.has(c)) return;
|
|
11003
10986
|
seen.add(c);
|
|
11004
10987
|
if (c._def.tag === "lazy") {
|
|
11005
|
-
if (
|
|
10988
|
+
if (ruleNameOf3(c) !== void 0) throw new Unserializable("scanSkip unit references a grammar rule");
|
|
11006
10989
|
const target = lazyTarget(c);
|
|
11007
10990
|
if (target) this.assertNoRuleRef(target, seen);
|
|
11008
10991
|
return;
|
|
@@ -11013,7 +10996,7 @@ var Serializer = class {
|
|
|
11013
10996
|
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
11014
10997
|
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
11015
10998
|
body(c) {
|
|
11016
|
-
return c._def.tag === "lazy" &&
|
|
10999
|
+
return c._def.tag === "lazy" && ruleNameOf3(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
11017
11000
|
}
|
|
11018
11001
|
run() {
|
|
11019
11002
|
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
@@ -11055,7 +11038,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11055
11038
|
analyze(c, active) {
|
|
11056
11039
|
const def = c._def;
|
|
11057
11040
|
if (def.tag === "lazy") {
|
|
11058
|
-
if (
|
|
11041
|
+
if (ruleNameOf3(c) !== void 0) return;
|
|
11059
11042
|
const target = lazyTarget(c);
|
|
11060
11043
|
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
11061
11044
|
if (active.has(target)) {
|
|
@@ -11107,7 +11090,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11107
11090
|
const kid = (x) => this.ref(x, selfOf);
|
|
11108
11091
|
switch (def.tag) {
|
|
11109
11092
|
case "lazy": {
|
|
11110
|
-
const name = this.localRuleNames.get(c) ??
|
|
11093
|
+
const name = this.localRuleNames.get(c) ?? ruleNameOf3(c);
|
|
11111
11094
|
if (name !== void 0) return `g[${JSON.stringify(name)}]`;
|
|
11112
11095
|
const target = lazyTarget(c);
|
|
11113
11096
|
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
@@ -11162,7 +11145,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11162
11145
|
case "peek":
|
|
11163
11146
|
return `peek(${kid(def.parser)})`;
|
|
11164
11147
|
case "routed":
|
|
11165
|
-
return "routed()"
|
|
11148
|
+
return def.fallback === void 0 ? "routed()" : `routed(${kid(def.fallback)})`;
|
|
11166
11149
|
case "trivia":
|
|
11167
11150
|
return `trivia(${kid(def.parser)})`;
|
|
11168
11151
|
case "token":
|
|
@@ -11195,6 +11178,9 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11195
11178
|
...projectEntry === void 0 ? [] : [projectEntry],
|
|
11196
11179
|
...def.captureTrivia ? ["captureTrivia: true"] : [],
|
|
11197
11180
|
...def.trailingTrivia ? ["trailingTrivia: true"] : [],
|
|
11181
|
+
// Declared or resolved arity. Carried so a re-lowered composed artifact does
|
|
11182
|
+
// not silently re-acquire the fail-open capture cost this module resolved away.
|
|
11183
|
+
...def.buildArity === void 0 ? [] : [`buildArity: ${def.buildArity}`],
|
|
11198
11184
|
...tagEntry === void 0 ? [] : [tagEntry]
|
|
11199
11185
|
];
|
|
11200
11186
|
const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
|
|
@@ -11203,8 +11189,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11203
11189
|
return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
|
|
11204
11190
|
}
|
|
11205
11191
|
if (def.buildSrc !== void 0) {
|
|
11206
|
-
const
|
|
11207
|
-
|
|
11192
|
+
const tail = [];
|
|
11193
|
+
if (def.buildStaticError !== void 0 || def.buildSigSrc !== void 0) {
|
|
11194
|
+
if (!opts) tail.push("undefined");
|
|
11195
|
+
tail.push(def.buildStaticError === void 0 ? "undefined" : JSON.stringify(def.buildStaticError));
|
|
11196
|
+
}
|
|
11197
|
+
if (def.buildSigSrc !== void 0) tail.push(JSON.stringify(def.buildSigSrc));
|
|
11198
|
+
const trailing = tail.length > 0 ? `, ${tail.join(", ")}` : "";
|
|
11199
|
+
return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts}${trailing})`;
|
|
11208
11200
|
}
|
|
11209
11201
|
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
11210
11202
|
}
|
|
@@ -11268,7 +11260,7 @@ function pickPieces(pieces, names) {
|
|
|
11268
11260
|
const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
|
|
11269
11261
|
return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc), nodeMeta: filt(pc.nodeMeta, pc) })).filter((pc) => pc.keys.length > 0);
|
|
11270
11262
|
}
|
|
11271
|
-
function fusedBody(pieces) {
|
|
11263
|
+
function fusedBody(pieces, hoist) {
|
|
11272
11264
|
for (const p of pieces) {
|
|
11273
11265
|
if (p.v === void 0) {
|
|
11274
11266
|
throw new Error(
|
|
@@ -11292,12 +11284,14 @@ function fusedBody(pieces) {
|
|
|
11292
11284
|
const contributingPieces = [...contributing];
|
|
11293
11285
|
const needsEmptyTl = contributingPieces.some((p) => p.needsEmptyTl);
|
|
11294
11286
|
const needsHostReads = contributingPieces.some((p) => p.needsHostReads);
|
|
11287
|
+
const needsRawEntry = contributingPieces.some((p) => p.needsRawEntry);
|
|
11295
11288
|
const lines = [
|
|
11296
11289
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
11297
11290
|
"const _pfFail = {}",
|
|
11298
11291
|
"let _pfEnd",
|
|
11299
11292
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
11300
11293
|
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
11294
|
+
...needsRawEntry ? [RAW_ENTRY_DECL] : [],
|
|
11301
11295
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
11302
11296
|
...new Set(contributingPieces.flatMap((p) => p.prelude)),
|
|
11303
11297
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
@@ -11306,8 +11300,7 @@ function fusedBody(pieces) {
|
|
|
11306
11300
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
11307
11301
|
const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
|
|
11308
11302
|
const { mode, elided } = hostModeOfPieces(pieces);
|
|
11309
|
-
const
|
|
11310
|
-
...lines,
|
|
11303
|
+
const tailSrc = [
|
|
11311
11304
|
"const _map = {",
|
|
11312
11305
|
wrapperEntries.join(",\n"),
|
|
11313
11306
|
"}",
|
|
@@ -11323,6 +11316,7 @@ function fusedBody(pieces) {
|
|
|
11323
11316
|
"}",
|
|
11324
11317
|
"return _map"
|
|
11325
11318
|
].join("\n");
|
|
11319
|
+
const rawBody = [...lines, tailSrc].join("\n");
|
|
11326
11320
|
const ANY2 = { kind: "any" };
|
|
11327
11321
|
const EMPTY3 = { kind: "empty" };
|
|
11328
11322
|
const recipes = /* @__PURE__ */ new Map();
|
|
@@ -11331,8 +11325,8 @@ function fusedBody(pieces) {
|
|
|
11331
11325
|
for (const [k, p] of winner) {
|
|
11332
11326
|
const r = p.firstSetRecipes?.get(k);
|
|
11333
11327
|
if (r) recipes.set(k, r);
|
|
11334
|
-
const
|
|
11335
|
-
if (
|
|
11328
|
+
const fs3 = p.firstSets?.get(k);
|
|
11329
|
+
if (fs3) shallow.set(k, fs3);
|
|
11336
11330
|
const nu = p.nullable?.get(k);
|
|
11337
11331
|
if (nu !== void 0) nullable2.set(k, nu);
|
|
11338
11332
|
}
|
|
@@ -11355,25 +11349,26 @@ function fusedBody(pieces) {
|
|
|
11355
11349
|
for (let iter = 0; iter <= recipes.size; iter++) {
|
|
11356
11350
|
let changed = false;
|
|
11357
11351
|
for (const [k, r] of recipes) {
|
|
11358
|
-
const
|
|
11359
|
-
if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(
|
|
11360
|
-
finalFS.set(k,
|
|
11352
|
+
const fs3 = resolveRecipe(r);
|
|
11353
|
+
if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(fs3)) {
|
|
11354
|
+
finalFS.set(k, fs3);
|
|
11361
11355
|
changed = true;
|
|
11362
11356
|
}
|
|
11363
11357
|
}
|
|
11364
11358
|
if (!changed) break;
|
|
11365
11359
|
}
|
|
11366
|
-
const
|
|
11360
|
+
const resolveFS = (src) => src.replace(
|
|
11367
11361
|
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
11368
11362
|
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
11369
11363
|
// matches every well-formed placeholder.
|
|
11370
11364
|
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
11371
11365
|
(_m, name, codevar) => {
|
|
11372
|
-
const
|
|
11373
|
-
if (!
|
|
11374
|
-
return `(${firstSetCond(codevar,
|
|
11366
|
+
const fs3 = finalFS.get(name);
|
|
11367
|
+
if (!fs3 || fs3.kind === "any" || fs3.kind === "empty") return "true";
|
|
11368
|
+
return `(${firstSetCond(codevar, fs3)})`;
|
|
11375
11369
|
}
|
|
11376
11370
|
);
|
|
11371
|
+
const body = hoist === void 0 ? resolveFS(rawBody) : [...hoist.claim(lines.map(resolveFS)), resolveFS(tailSrc)].join("\n");
|
|
11377
11372
|
const env = {};
|
|
11378
11373
|
for (const p of contributingPieces) {
|
|
11379
11374
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -11391,8 +11386,8 @@ function hostModeOfPieces(pieces) {
|
|
|
11391
11386
|
}
|
|
11392
11387
|
return { mode, elided: elided.length > 0 };
|
|
11393
11388
|
}
|
|
11394
|
-
function emitFusedSource(pieces) {
|
|
11395
|
-
const { body, env } = fusedBody(pieces);
|
|
11389
|
+
function emitFusedSource(pieces, hoist) {
|
|
11390
|
+
const { body, env } = fusedBody(pieces, hoist);
|
|
11396
11391
|
if (Object.keys(env).length > 0) {
|
|
11397
11392
|
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
11398
11393
|
}
|
|
@@ -11403,17 +11398,6 @@ ${body}
|
|
|
11403
11398
|
function isIRPiece(p) {
|
|
11404
11399
|
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
11405
11400
|
}
|
|
11406
|
-
function once(fn) {
|
|
11407
|
-
let done = false;
|
|
11408
|
-
let value;
|
|
11409
|
-
return () => {
|
|
11410
|
-
if (!done) {
|
|
11411
|
-
value = fn();
|
|
11412
|
-
done = true;
|
|
11413
|
-
}
|
|
11414
|
-
return value;
|
|
11415
|
-
};
|
|
11416
|
-
}
|
|
11417
11401
|
function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
|
|
11418
11402
|
if (!isIRPiece(p)) return p;
|
|
11419
11403
|
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
|
|
@@ -11426,6 +11410,34 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
|
|
|
11426
11410
|
return pieces;
|
|
11427
11411
|
}
|
|
11428
11412
|
|
|
11413
|
+
// src/functional/run.ts
|
|
11414
|
+
var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
|
|
11415
|
+
var REMOVED_RUN_RESULT_FIELDS = [
|
|
11416
|
+
[
|
|
11417
|
+
"triviaMap",
|
|
11418
|
+
`It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse capture, because most grammars paid for an index they never read. Migration: name the trivia labels your grammar defines \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia\`, whose \`.index\` carries the labels and gap lookups \`triviaMap\` exposed.`
|
|
11419
|
+
],
|
|
11420
|
+
[
|
|
11421
|
+
"triviaLog",
|
|
11422
|
+
`It was the flat \`start, end[, kindIndex]\` log every other trivia view was derived from, and it is no longer allocated at all \u2014 not empty, absent. Migration: request the labels you need \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia.rows\`. Do NOT reuse a stride-2/3 reader: rows are a different width and a different unit (one row per SELECTED marker, not one per trivia chunk), so an old loop reads confidently wrong offsets rather than failing.`
|
|
11423
|
+
],
|
|
11424
|
+
[
|
|
11425
|
+
"triviaKindLabels",
|
|
11426
|
+
`It was already optional, so nothing ever flagged its disappearance \u2014 and \`triviaKindMask(undefined, \u2026)\` means "capture everything", so reading it now silently WIDENS capture instead of erroring. Migration: request labels \u2014 ${SELECT_EXAMPLE} \u2014 then the set you asked for is \`RunResult.rootTrivia.select\` and the resolved table is \`RunResult.rootTrivia.index.labels\`.`
|
|
11427
|
+
]
|
|
11428
|
+
];
|
|
11429
|
+
|
|
11430
|
+
// src/analysis/choice-cost-render.ts
|
|
11431
|
+
var ESC = "\x1B";
|
|
11432
|
+
var ANSI = {
|
|
11433
|
+
dim: `${ESC}[2m`,
|
|
11434
|
+
bold: `${ESC}[1m`,
|
|
11435
|
+
red: `${ESC}[31m`,
|
|
11436
|
+
yellow: `${ESC}[33m`,
|
|
11437
|
+
cyan: `${ESC}[36m`,
|
|
11438
|
+
reset: `${ESC}[0m`
|
|
11439
|
+
};
|
|
11440
|
+
|
|
11429
11441
|
// src/combinators/gate.ts
|
|
11430
11442
|
function gate(predicate) {
|
|
11431
11443
|
const meta = {
|
|
@@ -11445,9 +11457,6 @@ function gate(predicate) {
|
|
|
11445
11457
|
};
|
|
11446
11458
|
}
|
|
11447
11459
|
|
|
11448
|
-
// src/cst/offset-model.ts
|
|
11449
|
-
var { isFinite } = Number;
|
|
11450
|
-
|
|
11451
11460
|
// src/index.ts
|
|
11452
11461
|
registerRegexAnalyzer(firstSetFromRegex);
|
|
11453
11462
|
|
|
@@ -11640,6 +11649,12 @@ function stripTsFromSource(node2, code) {
|
|
|
11640
11649
|
}
|
|
11641
11650
|
return out + code.slice(cur, end);
|
|
11642
11651
|
}
|
|
11652
|
+
var _reducers = null;
|
|
11653
|
+
var _entrySource = null;
|
|
11654
|
+
function setReducerResolver(r, entrySource = null) {
|
|
11655
|
+
_reducers = r;
|
|
11656
|
+
_entrySource = entrySource;
|
|
11657
|
+
}
|
|
11643
11658
|
function isWordFactory(v2) {
|
|
11644
11659
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
11645
11660
|
}
|
|
@@ -11871,9 +11886,12 @@ function staticNodeOptionsFromValue(value) {
|
|
|
11871
11886
|
} else if (name === "tags") {
|
|
11872
11887
|
if (!Array.isArray(v2) || !v2.every((item) => typeof item === "string")) return STATIC_NODE_OPTIONS_FAILED;
|
|
11873
11888
|
opts.tags = v2;
|
|
11889
|
+
} else if (name === "buildArity") {
|
|
11890
|
+
if (typeof v2 !== "number" || !Number.isInteger(v2) || v2 < 0 || v2 > 6) return STATIC_NODE_OPTIONS_FAILED;
|
|
11891
|
+
opts.buildArity = v2;
|
|
11874
11892
|
}
|
|
11875
11893
|
}
|
|
11876
|
-
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
|
|
11894
|
+
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 || opts.buildArity !== void 0 ? opts : void 0;
|
|
11877
11895
|
}
|
|
11878
11896
|
function staticNodeOptions(expr, scope) {
|
|
11879
11897
|
const unwrapped = unwrapStaticExpr(expr);
|
|
@@ -11899,9 +11917,13 @@ function staticNodeOptions(expr, scope) {
|
|
|
11899
11917
|
const tags = staticStringArray(p.value, scope);
|
|
11900
11918
|
if (tags === void 0) return STATIC_NODE_OPTIONS_FAILED;
|
|
11901
11919
|
opts.tags = tags;
|
|
11920
|
+
} else if (name === "buildArity") {
|
|
11921
|
+
const arity = staticLiteralValue(p.value);
|
|
11922
|
+
if (typeof arity !== "number" || !Number.isInteger(arity) || arity < 0 || arity > 6) return STATIC_NODE_OPTIONS_FAILED;
|
|
11923
|
+
opts.buildArity = arity;
|
|
11902
11924
|
}
|
|
11903
11925
|
}
|
|
11904
|
-
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
|
|
11926
|
+
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 || opts.buildArity !== void 0 ? opts : void 0;
|
|
11905
11927
|
}
|
|
11906
11928
|
function exprToCombi(node2, scope, code, mfs) {
|
|
11907
11929
|
if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
|
|
@@ -11997,6 +12019,18 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
11997
12019
|
const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
|
|
11998
12020
|
if (combi._def.tag === "node" && buildSrc !== void 0) {
|
|
11999
12021
|
combi._def.buildSrc = buildSrc;
|
|
12022
|
+
if (explicitType !== void 0 && firstArg.type === "Identifier") {
|
|
12023
|
+
combi._def.typeSrc = firstArg.name;
|
|
12024
|
+
}
|
|
12025
|
+
const resolved = _reducers?.resolve(buildSrc, be.start, code);
|
|
12026
|
+
if (resolved) {
|
|
12027
|
+
if (resolved.src !== null) combi._def.buildSigSrc = resolved.src;
|
|
12028
|
+
if (_entrySource !== null && code !== _entrySource && resolved.src !== null && be.type === "Identifier") {
|
|
12029
|
+
combi._def.buildSrc = resolved.src;
|
|
12030
|
+
}
|
|
12031
|
+
if (combi._def.buildArity === void 0 && resolved.arity !== null) combi._def.buildArity = resolved.arity;
|
|
12032
|
+
if (resolved.reason !== void 0) combi._def.buildArityUnresolved = resolved.reason;
|
|
12033
|
+
}
|
|
12000
12034
|
const staticError = directBuilderUnsupportedBindings(buildSrc);
|
|
12001
12035
|
if (staticError.length > 0) combi._def.buildStaticError = staticError;
|
|
12002
12036
|
}
|
|
@@ -12414,9 +12448,9 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
12414
12448
|
r._ruleName = name;
|
|
12415
12449
|
if (r._def.tag === "node" && r._def.type === void 0) r._def.type = name;
|
|
12416
12450
|
};
|
|
12417
|
-
const
|
|
12451
|
+
const ruleNameOf4 = (r) => r._ruleName;
|
|
12418
12452
|
const isNamedRuleRefForAnotherRule2 = (r, key) => {
|
|
12419
|
-
const name = r._def.tag === "lazy" ?
|
|
12453
|
+
const name = r._def.tag === "lazy" ? ruleNameOf4(r) : void 0;
|
|
12420
12454
|
return name !== void 0 && name !== key;
|
|
12421
12455
|
};
|
|
12422
12456
|
for (const key of keys) {
|
|
@@ -12507,6 +12541,692 @@ function referencesAny(node2, names, scope) {
|
|
|
12507
12541
|
return false;
|
|
12508
12542
|
}
|
|
12509
12543
|
|
|
12544
|
+
// src/plugin/reducer-resolver.ts
|
|
12545
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
12546
|
+
var path = __toESM(require("node:path"), 1);
|
|
12547
|
+
var import_node_module = require("node:module");
|
|
12548
|
+
var import_oxc_parser2 = require("oxc-parser");
|
|
12549
|
+
var FUNCTION_TYPES = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
|
|
12550
|
+
var SCOPE_TYPES = /* @__PURE__ */ new Set([
|
|
12551
|
+
...FUNCTION_TYPES,
|
|
12552
|
+
"BlockStatement",
|
|
12553
|
+
"ForStatement",
|
|
12554
|
+
"ForInStatement",
|
|
12555
|
+
"ForOfStatement",
|
|
12556
|
+
"CatchClause",
|
|
12557
|
+
"ClassDeclaration",
|
|
12558
|
+
"ClassExpression",
|
|
12559
|
+
"StaticBlock"
|
|
12560
|
+
]);
|
|
12561
|
+
function newScope(start, end, parent, isFunction) {
|
|
12562
|
+
const s = { start, end, bindings: /* @__PURE__ */ new Map(), children: [], parent, isFunction };
|
|
12563
|
+
parent?.children.push(s);
|
|
12564
|
+
return s;
|
|
12565
|
+
}
|
|
12566
|
+
function patternNames(pat, out) {
|
|
12567
|
+
if (!pat || typeof pat !== "object") return;
|
|
12568
|
+
const n = pat;
|
|
12569
|
+
switch (n.type) {
|
|
12570
|
+
case "Identifier":
|
|
12571
|
+
out.push(n.name);
|
|
12572
|
+
return;
|
|
12573
|
+
case "AssignmentPattern":
|
|
12574
|
+
return patternNames(n.left, out);
|
|
12575
|
+
case "RestElement":
|
|
12576
|
+
return patternNames(n.argument, out);
|
|
12577
|
+
case "ArrayPattern":
|
|
12578
|
+
for (const el of n.elements ?? []) patternNames(el, out);
|
|
12579
|
+
return;
|
|
12580
|
+
case "ObjectPattern":
|
|
12581
|
+
for (const p of n.properties ?? []) patternNames(p.type === "RestElement" ? p.argument : p.value, out);
|
|
12582
|
+
return;
|
|
12583
|
+
// A TS parameter property / assignment target wrapper.
|
|
12584
|
+
case "TSParameterProperty":
|
|
12585
|
+
return patternNames(n.parameter, out);
|
|
12586
|
+
default:
|
|
12587
|
+
return;
|
|
12588
|
+
}
|
|
12589
|
+
}
|
|
12590
|
+
function declare(scope, name, b) {
|
|
12591
|
+
if (!scope.bindings.has(name)) scope.bindings.set(name, b);
|
|
12592
|
+
}
|
|
12593
|
+
function functionScope(scope) {
|
|
12594
|
+
let s = scope;
|
|
12595
|
+
while (!s.isFunction && s.parent) s = s.parent;
|
|
12596
|
+
return s;
|
|
12597
|
+
}
|
|
12598
|
+
function buildScopeTree(body, moduleEnd) {
|
|
12599
|
+
const root = newScope(0, moduleEnd, null, true);
|
|
12600
|
+
const declarePattern = (scope, pat, kind, init) => {
|
|
12601
|
+
const names = [];
|
|
12602
|
+
patternNames(pat, names);
|
|
12603
|
+
const target = kind === "var" ? functionScope(scope) : scope;
|
|
12604
|
+
for (const name of names) {
|
|
12605
|
+
declare(target, name, init !== void 0 && names.length === 1 ? { kind, init } : { kind });
|
|
12606
|
+
}
|
|
12607
|
+
};
|
|
12608
|
+
const walk = (n, scope) => {
|
|
12609
|
+
if (!n || typeof n !== "object") return;
|
|
12610
|
+
if (Array.isArray(n)) {
|
|
12611
|
+
for (const item of n) walk(item, scope);
|
|
12612
|
+
return;
|
|
12613
|
+
}
|
|
12614
|
+
const node2 = n;
|
|
12615
|
+
if (typeof node2.type !== "string") return;
|
|
12616
|
+
switch (node2.type) {
|
|
12617
|
+
case "VariableDeclaration": {
|
|
12618
|
+
const kind = node2.kind ?? "var";
|
|
12619
|
+
for (const d of node2.declarations ?? []) {
|
|
12620
|
+
declarePattern(scope, d.id, kind, d.init ?? void 0);
|
|
12621
|
+
}
|
|
12622
|
+
break;
|
|
12623
|
+
}
|
|
12624
|
+
case "FunctionDeclaration":
|
|
12625
|
+
if (node2.id) declare(functionScope(scope), node2.id.name, { kind: "function", init: node2 });
|
|
12626
|
+
break;
|
|
12627
|
+
case "ClassDeclaration":
|
|
12628
|
+
if (node2.id) declare(scope, node2.id.name, { kind: "class" });
|
|
12629
|
+
break;
|
|
12630
|
+
case "ImportDeclaration": {
|
|
12631
|
+
const specifier = node2.source?.value ?? "";
|
|
12632
|
+
for (const sp of node2.specifiers ?? []) {
|
|
12633
|
+
const local = sp.local?.name;
|
|
12634
|
+
if (!local) continue;
|
|
12635
|
+
const imported = sp.type === "ImportDefaultSpecifier" ? "default" : sp.type === "ImportNamespaceSpecifier" ? "*" : sp.imported?.name ?? local;
|
|
12636
|
+
declare(scope, local, { kind: "import", importRef: { specifier, imported } });
|
|
12637
|
+
}
|
|
12638
|
+
break;
|
|
12639
|
+
}
|
|
12640
|
+
default:
|
|
12641
|
+
break;
|
|
12642
|
+
}
|
|
12643
|
+
if (SCOPE_TYPES.has(node2.type)) {
|
|
12644
|
+
const isFn = FUNCTION_TYPES.has(node2.type);
|
|
12645
|
+
const inner = newScope(node2.start, node2.end, scope, isFn);
|
|
12646
|
+
if (isFn) {
|
|
12647
|
+
for (const p of node2.params ?? []) declarePattern(inner, p, "param");
|
|
12648
|
+
if (node2.type === "FunctionExpression" && node2.id) {
|
|
12649
|
+
declare(inner, node2.id.name, { kind: "function", init: node2 });
|
|
12650
|
+
}
|
|
12651
|
+
}
|
|
12652
|
+
if (node2.type === "CatchClause" && node2.param) declarePattern(inner, node2.param, "catch");
|
|
12653
|
+
for (const key of Object.keys(node2)) {
|
|
12654
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12655
|
+
walk(node2[key], inner);
|
|
12656
|
+
}
|
|
12657
|
+
return;
|
|
12658
|
+
}
|
|
12659
|
+
for (const key of Object.keys(node2)) {
|
|
12660
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12661
|
+
walk(node2[key], scope);
|
|
12662
|
+
}
|
|
12663
|
+
};
|
|
12664
|
+
for (const stmt of body) walk(stmt, root);
|
|
12665
|
+
return root;
|
|
12666
|
+
}
|
|
12667
|
+
function scopeAt(root, offset) {
|
|
12668
|
+
let cur = root;
|
|
12669
|
+
for (; ; ) {
|
|
12670
|
+
const next = cur.children.find((c) => offset >= c.start && offset <= c.end);
|
|
12671
|
+
if (!next) return cur;
|
|
12672
|
+
cur = next;
|
|
12673
|
+
}
|
|
12674
|
+
}
|
|
12675
|
+
function lookup(scope, name) {
|
|
12676
|
+
for (let s = scope; s; s = s.parent) {
|
|
12677
|
+
const b = s.bindings.get(name);
|
|
12678
|
+
if (b) return { binding: b, scope: s };
|
|
12679
|
+
}
|
|
12680
|
+
return null;
|
|
12681
|
+
}
|
|
12682
|
+
function markReassignments(body, root) {
|
|
12683
|
+
const walk = (n) => {
|
|
12684
|
+
if (!n || typeof n !== "object") return;
|
|
12685
|
+
if (Array.isArray(n)) {
|
|
12686
|
+
for (const item of n) walk(item);
|
|
12687
|
+
return;
|
|
12688
|
+
}
|
|
12689
|
+
const node2 = n;
|
|
12690
|
+
if (typeof node2.type !== "string") return;
|
|
12691
|
+
if (node2.type === "AssignmentExpression" || node2.type === "UpdateExpression") {
|
|
12692
|
+
const target = node2.type === "UpdateExpression" ? node2.argument : node2.left;
|
|
12693
|
+
const names = [];
|
|
12694
|
+
if (target) patternNames(target, names);
|
|
12695
|
+
for (const name of names) {
|
|
12696
|
+
const hit = lookup(scopeAt(root, node2.start), name);
|
|
12697
|
+
if (hit) hit.binding.reassigned = true;
|
|
12698
|
+
}
|
|
12699
|
+
}
|
|
12700
|
+
for (const key of Object.keys(node2)) {
|
|
12701
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12702
|
+
walk(node2[key]);
|
|
12703
|
+
}
|
|
12704
|
+
};
|
|
12705
|
+
for (const stmt of body) walk(stmt);
|
|
12706
|
+
}
|
|
12707
|
+
function functionArity(fn) {
|
|
12708
|
+
const params = fn.params ?? [];
|
|
12709
|
+
let count = 0;
|
|
12710
|
+
for (const p of params) {
|
|
12711
|
+
if (p.type === "RestElement") return { arity: null, reason: "rest-parameter" };
|
|
12712
|
+
if (p.type === "Identifier" && p.name === "this") continue;
|
|
12713
|
+
count++;
|
|
12714
|
+
}
|
|
12715
|
+
if (fn.type !== "ArrowFunctionExpression" && referencesArguments(fn.body)) {
|
|
12716
|
+
return { arity: null, reason: "arguments" };
|
|
12717
|
+
}
|
|
12718
|
+
return { arity: count };
|
|
12719
|
+
}
|
|
12720
|
+
function referencesArguments(n) {
|
|
12721
|
+
if (!n || typeof n !== "object") return false;
|
|
12722
|
+
if (Array.isArray(n)) return n.some(referencesArguments);
|
|
12723
|
+
const node2 = n;
|
|
12724
|
+
if (typeof node2.type !== "string") return false;
|
|
12725
|
+
if (node2.type === "Identifier" && node2.name === "arguments") return true;
|
|
12726
|
+
if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression") return false;
|
|
12727
|
+
for (const key of Object.keys(node2)) {
|
|
12728
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12729
|
+
if (referencesArguments(node2[key])) return true;
|
|
12730
|
+
}
|
|
12731
|
+
return false;
|
|
12732
|
+
}
|
|
12733
|
+
var _moduleCache = /* @__PURE__ */ new Map();
|
|
12734
|
+
function analyseModuleFile(file) {
|
|
12735
|
+
let mtimeMs;
|
|
12736
|
+
try {
|
|
12737
|
+
mtimeMs = fs.statSync(file).mtimeMs;
|
|
12738
|
+
} catch {
|
|
12739
|
+
return null;
|
|
12740
|
+
}
|
|
12741
|
+
const hit = _moduleCache.get(file);
|
|
12742
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.mod;
|
|
12743
|
+
let mod = null;
|
|
12744
|
+
try {
|
|
12745
|
+
const src = fs.readFileSync(file, "utf8");
|
|
12746
|
+
const r = (0, import_oxc_parser2.parseSync)(file, src);
|
|
12747
|
+
if (r.errors.length === 0) {
|
|
12748
|
+
const body = r.program.body;
|
|
12749
|
+
const scopes = buildScopeTree(body, src.length);
|
|
12750
|
+
markReassignments(body, scopes);
|
|
12751
|
+
mod = { file, src, body, scopes };
|
|
12752
|
+
}
|
|
12753
|
+
} catch {
|
|
12754
|
+
}
|
|
12755
|
+
_moduleCache.set(file, { mtimeMs, mod });
|
|
12756
|
+
return mod;
|
|
12757
|
+
}
|
|
12758
|
+
var SOURCE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".jsx"];
|
|
12759
|
+
function resolveImport(fromFile, specifier) {
|
|
12760
|
+
if (specifier.startsWith(".") || path.isAbsolute(specifier)) {
|
|
12761
|
+
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
12762
|
+
const ext = path.extname(base);
|
|
12763
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
12764
|
+
const candidates = [
|
|
12765
|
+
...ext ? [base] : [],
|
|
12766
|
+
// `./x.js` in TS source usually means `./x.ts`.
|
|
12767
|
+
...SOURCE_EXTS.map((e) => `${stem}${e}`),
|
|
12768
|
+
...SOURCE_EXTS.map((e) => path.join(base, `index${e}`))
|
|
12769
|
+
];
|
|
12770
|
+
for (const c of candidates) {
|
|
12771
|
+
try {
|
|
12772
|
+
if (fs.statSync(c).isFile()) return fs.realpathSync(c);
|
|
12773
|
+
} catch {
|
|
12774
|
+
}
|
|
12775
|
+
}
|
|
12776
|
+
return null;
|
|
12777
|
+
}
|
|
12778
|
+
try {
|
|
12779
|
+
return fs.realpathSync((0, import_node_module.createRequire)(fromFile).resolve(specifier));
|
|
12780
|
+
} catch {
|
|
12781
|
+
return null;
|
|
12782
|
+
}
|
|
12783
|
+
}
|
|
12784
|
+
function declarationBinds(decl, name) {
|
|
12785
|
+
if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration") {
|
|
12786
|
+
return decl.id?.name === name;
|
|
12787
|
+
}
|
|
12788
|
+
if (decl.type === "VariableDeclaration") {
|
|
12789
|
+
const names = [];
|
|
12790
|
+
for (const d of decl.declarations ?? []) patternNames(d.id, names);
|
|
12791
|
+
return names.includes(name);
|
|
12792
|
+
}
|
|
12793
|
+
return false;
|
|
12794
|
+
}
|
|
12795
|
+
function exportedBinding(mod, name, seen) {
|
|
12796
|
+
for (const stmt of mod.body) {
|
|
12797
|
+
if (stmt.type === "ExportDefaultDeclaration" && name === "default") {
|
|
12798
|
+
const d = stmt.declaration;
|
|
12799
|
+
if (FUNCTION_TYPES.has(d.type)) return { mod, binding: { kind: "const", init: d } };
|
|
12800
|
+
if (d.type === "Identifier") {
|
|
12801
|
+
const hit2 = lookup(mod.scopes, d.name);
|
|
12802
|
+
return hit2 ? { mod, binding: hit2.binding } : null;
|
|
12803
|
+
}
|
|
12804
|
+
return null;
|
|
12805
|
+
}
|
|
12806
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
12807
|
+
const decl = stmt.declaration;
|
|
12808
|
+
if (decl && declarationBinds(decl, name)) {
|
|
12809
|
+
const hit2 = lookup(mod.scopes, name);
|
|
12810
|
+
if (hit2 && hit2.scope === mod.scopes) return { mod, binding: hit2.binding };
|
|
12811
|
+
}
|
|
12812
|
+
for (const sp of stmt.specifiers ?? []) {
|
|
12813
|
+
const exported = sp.exported?.name ?? "";
|
|
12814
|
+
if (exported !== name) continue;
|
|
12815
|
+
const local = sp.local?.name ?? exported;
|
|
12816
|
+
const from = stmt.source?.value;
|
|
12817
|
+
if (from !== void 0) return followReExport(mod, from, local, seen);
|
|
12818
|
+
const hit2 = lookup(mod.scopes, local);
|
|
12819
|
+
return hit2 ? { mod, binding: hit2.binding } : null;
|
|
12820
|
+
}
|
|
12821
|
+
}
|
|
12822
|
+
if (stmt.type === "ExportAllDeclaration" && !stmt.exported) {
|
|
12823
|
+
const from = stmt.source?.value;
|
|
12824
|
+
if (from === void 0) continue;
|
|
12825
|
+
const found = followReExport(mod, from, name, seen);
|
|
12826
|
+
if (found) return found;
|
|
12827
|
+
}
|
|
12828
|
+
}
|
|
12829
|
+
const hit = lookup(mod.scopes, name);
|
|
12830
|
+
return hit && hit.scope === mod.scopes ? { mod, binding: hit.binding } : null;
|
|
12831
|
+
}
|
|
12832
|
+
function followReExport(mod, specifier, name, seen) {
|
|
12833
|
+
const file = resolveImport(mod.file, specifier);
|
|
12834
|
+
if (!file || seen.has(`${file}\0${name}`)) return null;
|
|
12835
|
+
seen.add(`${file}\0${name}`);
|
|
12836
|
+
const next = analyseModuleFile(file);
|
|
12837
|
+
return next ? exportedBinding(next, name, seen) : null;
|
|
12838
|
+
}
|
|
12839
|
+
var MAX_HOPS = 12;
|
|
12840
|
+
var IDENT_RE = /^[A-Za-z_$][\w$]*$/;
|
|
12841
|
+
var MEMBER_RE = /^([A-Za-z_$][\w$]*)\s*\.\s*([A-Za-z_$][\w$]*)$/;
|
|
12842
|
+
function createReducerResolver(entryFile, body, src) {
|
|
12843
|
+
const scopes = buildScopeTree(body, src.length);
|
|
12844
|
+
markReassignments(body, scopes);
|
|
12845
|
+
const entry = { file: entryFile, src, body, scopes };
|
|
12846
|
+
const registered = /* @__PURE__ */ new Map();
|
|
12847
|
+
const ambiguous = /* @__PURE__ */ new Set();
|
|
12848
|
+
const fromBinding = (mod, binding, memberName, hops, seen) => {
|
|
12849
|
+
if (hops > MAX_HOPS) return { arity: null, src: null, reason: "not-found" };
|
|
12850
|
+
if (binding.reassigned) return { arity: null, src: null, reason: "reassigned" };
|
|
12851
|
+
if (binding.kind === "param" || binding.kind === "catch" || binding.kind === "class") {
|
|
12852
|
+
return { arity: null, src: null, reason: "not-a-function" };
|
|
12853
|
+
}
|
|
12854
|
+
if (binding.kind === "import" && binding.importRef) {
|
|
12855
|
+
const { specifier, imported } = binding.importRef;
|
|
12856
|
+
if (imported === "*" && memberName === void 0) return { arity: null, src: null, reason: "not-a-function" };
|
|
12857
|
+
const file = resolveImport(mod.file, specifier);
|
|
12858
|
+
if (!file) return { arity: null, src: null, reason: "unresolved-import" };
|
|
12859
|
+
const next = analyseModuleFile(file);
|
|
12860
|
+
if (!next) return { arity: null, src: null, reason: "unresolved-import" };
|
|
12861
|
+
const wanted = imported === "*" ? memberName : imported;
|
|
12862
|
+
if (wanted === void 0) return { arity: null, src: null, reason: "not-a-function" };
|
|
12863
|
+
const found = exportedBinding(next, wanted, seen);
|
|
12864
|
+
if (!found) return { arity: null, src: null, reason: "not-found" };
|
|
12865
|
+
return fromBinding(found.mod, found.binding, void 0, hops + 1, seen);
|
|
12866
|
+
}
|
|
12867
|
+
const init = binding.init;
|
|
12868
|
+
if (!init) return { arity: null, src: null, reason: "not-a-function" };
|
|
12869
|
+
if (init.type === "Identifier") {
|
|
12870
|
+
const hit = lookup(scopeAt(mod.scopes, init.start), init.name);
|
|
12871
|
+
if (!hit) return { arity: null, src: null, reason: "not-found" };
|
|
12872
|
+
return fromBinding(mod, hit.binding, void 0, hops + 1, seen);
|
|
12873
|
+
}
|
|
12874
|
+
if (init.type === "ComputedMemberExpression" || init.computed === true && init.type.endsWith("MemberExpression")) {
|
|
12875
|
+
return { arity: null, src: null, reason: "computed" };
|
|
12876
|
+
}
|
|
12877
|
+
if (init.type === "StaticMemberExpression" || init.type === "MemberExpression") {
|
|
12878
|
+
const obj = init.object;
|
|
12879
|
+
const prop = init.property;
|
|
12880
|
+
if (obj?.type === "Identifier" && prop?.type === "Identifier") {
|
|
12881
|
+
const hit = lookup(scopeAt(mod.scopes, init.start), obj.name);
|
|
12882
|
+
if (!hit) return { arity: null, src: null, reason: "not-found" };
|
|
12883
|
+
return fromBinding(mod, hit.binding, prop.name, hops + 1, seen);
|
|
12884
|
+
}
|
|
12885
|
+
return { arity: null, src: null, reason: "computed" };
|
|
12886
|
+
}
|
|
12887
|
+
if (!FUNCTION_TYPES.has(init.type)) return { arity: null, src: null, reason: "not-a-function" };
|
|
12888
|
+
const { arity, reason } = functionArity(init);
|
|
12889
|
+
return { arity, src: mod.src.slice(init.start, init.end), ...reason ? { reason } : {} };
|
|
12890
|
+
};
|
|
12891
|
+
return {
|
|
12892
|
+
register(file) {
|
|
12893
|
+
const mod = analyseModuleFile(file);
|
|
12894
|
+
if (!mod) return false;
|
|
12895
|
+
if (mod.src === src) return true;
|
|
12896
|
+
const prior = registered.get(mod.src);
|
|
12897
|
+
if (prior !== void 0 && prior.file !== mod.file) {
|
|
12898
|
+
ambiguous.add(mod.src);
|
|
12899
|
+
registered.delete(mod.src);
|
|
12900
|
+
return false;
|
|
12901
|
+
}
|
|
12902
|
+
if (!ambiguous.has(mod.src)) registered.set(mod.src, mod);
|
|
12903
|
+
return !ambiguous.has(mod.src);
|
|
12904
|
+
},
|
|
12905
|
+
resolve(exprSrc, offset, offsetSrc) {
|
|
12906
|
+
const text = exprSrc.trim();
|
|
12907
|
+
const ident = IDENT_RE.test(text) ? text : null;
|
|
12908
|
+
const member = ident ? null : MEMBER_RE.exec(text);
|
|
12909
|
+
if (!ident && !member) return null;
|
|
12910
|
+
const mod = offsetSrc === src ? entry : registered.get(offsetSrc);
|
|
12911
|
+
if (!mod) {
|
|
12912
|
+
return { arity: null, src: null, reason: ambiguous.has(offsetSrc) ? "ambiguous-source" : "foreign-source" };
|
|
12913
|
+
}
|
|
12914
|
+
const name = ident ?? member[1];
|
|
12915
|
+
const memberName = ident ? void 0 : member[2];
|
|
12916
|
+
const hit = lookup(scopeAt(mod.scopes, offset), name);
|
|
12917
|
+
if (!hit) return { arity: null, src: null, reason: "not-found" };
|
|
12918
|
+
return fromBinding(mod, hit.binding, memberName, 0, /* @__PURE__ */ new Set());
|
|
12919
|
+
}
|
|
12920
|
+
};
|
|
12921
|
+
}
|
|
12922
|
+
|
|
12923
|
+
// src/plugin/free-identifiers.ts
|
|
12924
|
+
var import_oxc_parser3 = require("oxc-parser");
|
|
12925
|
+
var isNode = (v2) => typeof v2 === "object" && v2 !== null && typeof v2.type === "string";
|
|
12926
|
+
var TYPE_KEYS = /* @__PURE__ */ new Set([
|
|
12927
|
+
"typeAnnotation",
|
|
12928
|
+
"returnType",
|
|
12929
|
+
"typeParameters",
|
|
12930
|
+
"typeArguments",
|
|
12931
|
+
"superTypeArguments",
|
|
12932
|
+
"typeName",
|
|
12933
|
+
"implements",
|
|
12934
|
+
"extends",
|
|
12935
|
+
"accessibility"
|
|
12936
|
+
]);
|
|
12937
|
+
var TS_VALUE_WRAPPERS = /* @__PURE__ */ new Set([
|
|
12938
|
+
"TSAsExpression",
|
|
12939
|
+
"TSSatisfiesExpression",
|
|
12940
|
+
"TSNonNullExpression",
|
|
12941
|
+
"TSInstantiationExpression",
|
|
12942
|
+
"TSTypeAssertion"
|
|
12943
|
+
]);
|
|
12944
|
+
var HOST_GLOBALS = /* @__PURE__ */ new Set([
|
|
12945
|
+
...Object.getOwnPropertyNames(globalThis),
|
|
12946
|
+
"undefined",
|
|
12947
|
+
"NaN",
|
|
12948
|
+
"Infinity",
|
|
12949
|
+
"globalThis",
|
|
12950
|
+
"arguments",
|
|
12951
|
+
"window",
|
|
12952
|
+
"document",
|
|
12953
|
+
"self",
|
|
12954
|
+
"navigator",
|
|
12955
|
+
"location",
|
|
12956
|
+
"top",
|
|
12957
|
+
"parent",
|
|
12958
|
+
"require",
|
|
12959
|
+
"module",
|
|
12960
|
+
"exports",
|
|
12961
|
+
"__dirname",
|
|
12962
|
+
"__filename"
|
|
12963
|
+
]);
|
|
12964
|
+
function bindPattern(pattern, out) {
|
|
12965
|
+
if (!isNode(pattern)) return;
|
|
12966
|
+
switch (pattern.type) {
|
|
12967
|
+
case "Identifier":
|
|
12968
|
+
case "BindingIdentifier":
|
|
12969
|
+
out.add(String(pattern.name));
|
|
12970
|
+
return;
|
|
12971
|
+
case "ObjectPattern":
|
|
12972
|
+
for (const prop of pattern.properties ?? []) {
|
|
12973
|
+
if (!isNode(prop)) continue;
|
|
12974
|
+
if (prop.type === "RestElement" || prop.type === "BindingRestElement") bindPattern(prop.argument, out);
|
|
12975
|
+
else bindPattern(prop.value, out);
|
|
12976
|
+
}
|
|
12977
|
+
return;
|
|
12978
|
+
case "ArrayPattern":
|
|
12979
|
+
for (const el of pattern.elements ?? []) bindPattern(el, out);
|
|
12980
|
+
return;
|
|
12981
|
+
case "AssignmentPattern":
|
|
12982
|
+
bindPattern(pattern.left, out);
|
|
12983
|
+
return;
|
|
12984
|
+
case "RestElement":
|
|
12985
|
+
case "BindingRestElement":
|
|
12986
|
+
bindPattern(pattern.argument, out);
|
|
12987
|
+
return;
|
|
12988
|
+
case "TSParameterProperty":
|
|
12989
|
+
bindPattern(pattern.parameter, out);
|
|
12990
|
+
return;
|
|
12991
|
+
default:
|
|
12992
|
+
return;
|
|
12993
|
+
}
|
|
12994
|
+
}
|
|
12995
|
+
function collectBindings(node2, out) {
|
|
12996
|
+
if (Array.isArray(node2)) {
|
|
12997
|
+
for (const child of node2) collectBindings(child, out);
|
|
12998
|
+
return;
|
|
12999
|
+
}
|
|
13000
|
+
if (!isNode(node2)) return;
|
|
13001
|
+
switch (node2.type) {
|
|
13002
|
+
case "ImportSpecifier":
|
|
13003
|
+
case "ImportDefaultSpecifier":
|
|
13004
|
+
case "ImportNamespaceSpecifier":
|
|
13005
|
+
if (isNode(node2.local)) out.add(String(node2.local.name));
|
|
13006
|
+
break;
|
|
13007
|
+
case "VariableDeclarator":
|
|
13008
|
+
bindPattern(node2.id, out);
|
|
13009
|
+
break;
|
|
13010
|
+
case "FunctionDeclaration":
|
|
13011
|
+
case "FunctionExpression":
|
|
13012
|
+
case "TSDeclareFunction":
|
|
13013
|
+
case "ArrowFunctionExpression":
|
|
13014
|
+
if (isNode(node2.id)) out.add(String(node2.id.name));
|
|
13015
|
+
for (const p of node2.params?.items ?? node2.params ?? []) bindPattern(p, out);
|
|
13016
|
+
if (isNode(node2.params) && node2.params.rest) bindPattern(node2.params.rest, out);
|
|
13017
|
+
break;
|
|
13018
|
+
case "ClassDeclaration":
|
|
13019
|
+
case "ClassExpression":
|
|
13020
|
+
if (isNode(node2.id)) out.add(String(node2.id.name));
|
|
13021
|
+
break;
|
|
13022
|
+
case "CatchClause":
|
|
13023
|
+
if (node2.param) {
|
|
13024
|
+
bindPattern(isNode(node2.param) && node2.param.pattern ? node2.param.pattern : node2.param, out);
|
|
13025
|
+
}
|
|
13026
|
+
break;
|
|
13027
|
+
default:
|
|
13028
|
+
break;
|
|
13029
|
+
}
|
|
13030
|
+
for (const key of Object.keys(node2)) {
|
|
13031
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
13032
|
+
collectBindings(node2[key], out);
|
|
13033
|
+
}
|
|
13034
|
+
}
|
|
13035
|
+
var FUNCTION_TYPES2 = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression", "FunctionDeclaration"]);
|
|
13036
|
+
function describeTopLevel(stmt) {
|
|
13037
|
+
const declared = /* @__PURE__ */ new Set();
|
|
13038
|
+
let target = stmt;
|
|
13039
|
+
let prefix = "";
|
|
13040
|
+
let exported = false;
|
|
13041
|
+
if (stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration") {
|
|
13042
|
+
exported = true;
|
|
13043
|
+
prefix = "export ";
|
|
13044
|
+
if (!isNode(stmt.declaration)) return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}{ \u2026 }` };
|
|
13045
|
+
target = stmt.declaration;
|
|
13046
|
+
}
|
|
13047
|
+
if (target.type === "VariableDeclaration") {
|
|
13048
|
+
const decls = (target.declarations ?? []).filter(isNode);
|
|
13049
|
+
for (const d of decls) bindPattern(d.id, declared);
|
|
13050
|
+
const declaresOnlyFunctions = decls.length > 0 && decls.every((d) => isNode(d.init) && FUNCTION_TYPES2.has(d.init.type));
|
|
13051
|
+
const name = [...declared][0];
|
|
13052
|
+
return {
|
|
13053
|
+
declared,
|
|
13054
|
+
exported,
|
|
13055
|
+
declaresOnlyFunctions,
|
|
13056
|
+
label: name ? `${prefix}${String(target.kind)} ${name}` : null
|
|
13057
|
+
};
|
|
13058
|
+
}
|
|
13059
|
+
if (target.type === "FunctionDeclaration" && isNode(target.id)) {
|
|
13060
|
+
declared.add(String(target.id.name));
|
|
13061
|
+
return { declared, exported, declaresOnlyFunctions: true, label: `${prefix}function ${String(target.id.name)}` };
|
|
13062
|
+
}
|
|
13063
|
+
if (target.type === "ClassDeclaration" && isNode(target.id)) {
|
|
13064
|
+
declared.add(String(target.id.name));
|
|
13065
|
+
return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}class ${String(target.id.name)}` };
|
|
13066
|
+
}
|
|
13067
|
+
return { declared, exported, declaresOnlyFunctions: false, label: null };
|
|
13068
|
+
}
|
|
13069
|
+
function findFreeIdentifiers(code, filename) {
|
|
13070
|
+
let parsed;
|
|
13071
|
+
try {
|
|
13072
|
+
parsed = (0, import_oxc_parser3.parseSync)(filename, code);
|
|
13073
|
+
} catch {
|
|
13074
|
+
return [];
|
|
13075
|
+
}
|
|
13076
|
+
if (parsed.errors.length > 0) return [];
|
|
13077
|
+
const bound = /* @__PURE__ */ new Set();
|
|
13078
|
+
collectBindings(parsed.program.body, bound);
|
|
13079
|
+
const lineStarts = [0];
|
|
13080
|
+
for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) lineStarts.push(i + 1);
|
|
13081
|
+
const positionOf = (offset) => {
|
|
13082
|
+
let lo = 0;
|
|
13083
|
+
let hi = lineStarts.length - 1;
|
|
13084
|
+
while (lo < hi) {
|
|
13085
|
+
const mid = lo + hi + 1 >> 1;
|
|
13086
|
+
if (lineStarts[mid] <= offset) lo = mid;
|
|
13087
|
+
else hi = mid - 1;
|
|
13088
|
+
}
|
|
13089
|
+
return { line: lo + 1, column: offset - lineStarts[lo] + 1 };
|
|
13090
|
+
};
|
|
13091
|
+
const statements = parsed.program.body.filter(isNode);
|
|
13092
|
+
const tops = statements.map(describeTopLevel);
|
|
13093
|
+
const candidates = [];
|
|
13094
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13095
|
+
const readIn = /* @__PURE__ */ new Map();
|
|
13096
|
+
const visit = (node2, stmt, insideFunction) => {
|
|
13097
|
+
if (Array.isArray(node2)) {
|
|
13098
|
+
for (const child of node2) visit(child, stmt, insideFunction);
|
|
13099
|
+
return;
|
|
13100
|
+
}
|
|
13101
|
+
if (!isNode(node2)) return;
|
|
13102
|
+
const type = node2.type;
|
|
13103
|
+
if (type === "ImportDeclaration" || type === "ExportAllDeclaration") return;
|
|
13104
|
+
if (type === "MetaProperty" || type === "PrivateIdentifier") return;
|
|
13105
|
+
if (type === "ExportNamedDeclaration" && node2.source) return;
|
|
13106
|
+
if (type.startsWith("TS")) {
|
|
13107
|
+
if (TS_VALUE_WRAPPERS.has(type)) visit(node2.expression, stmt, insideFunction);
|
|
13108
|
+
return;
|
|
13109
|
+
}
|
|
13110
|
+
if (type === "Identifier" || type === "IdentifierReference") {
|
|
13111
|
+
const name = String(node2.name);
|
|
13112
|
+
let readers = readIn.get(name);
|
|
13113
|
+
if (!readers) readIn.set(name, readers = /* @__PURE__ */ new Set());
|
|
13114
|
+
readers.add(stmt);
|
|
13115
|
+
if (bound.has(name) || HOST_GLOBALS.has(name)) return;
|
|
13116
|
+
const offset = typeof node2.start === "number" ? node2.start : 0;
|
|
13117
|
+
const { line, column } = positionOf(offset);
|
|
13118
|
+
const key = `${name}@${line}:${column}`;
|
|
13119
|
+
if (seen.has(key)) return;
|
|
13120
|
+
seen.add(key);
|
|
13121
|
+
candidates.push({ name, line, column, enclosing: tops[stmt].label, stmt, insideFunction });
|
|
13122
|
+
return;
|
|
13123
|
+
}
|
|
13124
|
+
const nowInsideFunction = insideFunction || FUNCTION_TYPES2.has(type);
|
|
13125
|
+
for (const key of Object.keys(node2)) {
|
|
13126
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
13127
|
+
if (TYPE_KEYS.has(key)) continue;
|
|
13128
|
+
if (key === "key" && node2.computed !== true) continue;
|
|
13129
|
+
if (key === "property" && node2.computed !== true) continue;
|
|
13130
|
+
if (key === "label") continue;
|
|
13131
|
+
if (key === "exported") continue;
|
|
13132
|
+
if (key === "imported") continue;
|
|
13133
|
+
visit(node2[key], stmt, nowInsideFunction);
|
|
13134
|
+
}
|
|
13135
|
+
};
|
|
13136
|
+
for (let i = 0; i < statements.length; i++) visit(statements[i], i, false);
|
|
13137
|
+
return candidates.filter((c) => {
|
|
13138
|
+
const top = tops[c.stmt];
|
|
13139
|
+
if (!c.insideFunction || top.exported || !top.declaresOnlyFunctions || top.declared.size === 0) return true;
|
|
13140
|
+
for (const name of top.declared) {
|
|
13141
|
+
const readers = readIn.get(name);
|
|
13142
|
+
if (readers && [...readers].some((i) => i !== c.stmt)) return true;
|
|
13143
|
+
}
|
|
13144
|
+
return false;
|
|
13145
|
+
}).map(({ name, line, column, enclosing }) => ({ name, line, column, enclosing }));
|
|
13146
|
+
}
|
|
13147
|
+
|
|
13148
|
+
// src/compiler/module-hoist.ts
|
|
13149
|
+
var marker = (id) => `/*@pmh:${id}@*/`;
|
|
13150
|
+
var MARKER_RE = /\/\*@pmh:(\d+)@\*\/\n?/g;
|
|
13151
|
+
var HOIST_MARKER_PROBE = /\/\*@pmh:\d+@\*\//;
|
|
13152
|
+
var DECL_RE = /^(?:const|let|var|function\*?)\s+([A-Za-z_$][\w$]*)/;
|
|
13153
|
+
var ALWAYS_LOCAL = /* @__PURE__ */ new Set(["_map", "_k", "_env"]);
|
|
13154
|
+
function declaredNames(text) {
|
|
13155
|
+
const out = [];
|
|
13156
|
+
for (const line of text.split("\n")) {
|
|
13157
|
+
const m = DECL_RE.exec(line);
|
|
13158
|
+
if (m) out.push(m[1]);
|
|
13159
|
+
}
|
|
13160
|
+
return out;
|
|
13161
|
+
}
|
|
13162
|
+
function createModuleHoist() {
|
|
13163
|
+
const texts = [];
|
|
13164
|
+
const claim = (decls) => decls.map((text) => {
|
|
13165
|
+
const id = texts.length;
|
|
13166
|
+
texts.push(text);
|
|
13167
|
+
return marker(id);
|
|
13168
|
+
});
|
|
13169
|
+
const finalize = () => {
|
|
13170
|
+
const nameTexts = /* @__PURE__ */ new Map();
|
|
13171
|
+
const textCount = /* @__PURE__ */ new Map();
|
|
13172
|
+
const textNames = /* @__PURE__ */ new Map();
|
|
13173
|
+
const order = [];
|
|
13174
|
+
for (const text of texts) {
|
|
13175
|
+
const seen = textCount.get(text);
|
|
13176
|
+
textCount.set(text, (seen ?? 0) + 1);
|
|
13177
|
+
if (seen === void 0) {
|
|
13178
|
+
order.push(text);
|
|
13179
|
+
const names = declaredNames(text);
|
|
13180
|
+
textNames.set(text, names);
|
|
13181
|
+
for (const n of names) {
|
|
13182
|
+
const s = nameTexts.get(n);
|
|
13183
|
+
if (s) s.add(text);
|
|
13184
|
+
else nameTexts.set(n, /* @__PURE__ */ new Set([text]));
|
|
13185
|
+
}
|
|
13186
|
+
}
|
|
13187
|
+
}
|
|
13188
|
+
const hoisted = new Set(
|
|
13189
|
+
order.filter(
|
|
13190
|
+
(text) => (textCount.get(text) ?? 0) >= 2 && textNames.get(text).length > 0 && textNames.get(text).every((n) => !ALWAYS_LOCAL.has(n) && nameTexts.get(n).size === 1)
|
|
13191
|
+
)
|
|
13192
|
+
);
|
|
13193
|
+
for (; ; ) {
|
|
13194
|
+
const localNames = [];
|
|
13195
|
+
for (const [n, ts] of nameTexts) {
|
|
13196
|
+
if (ts.size !== 1 || !hoisted.has([...ts][0])) localNames.push(n);
|
|
13197
|
+
}
|
|
13198
|
+
let changed = false;
|
|
13199
|
+
for (const text of [...hoisted]) {
|
|
13200
|
+
if (localNames.some((n) => new RegExp(`\\b${escapeName(n)}\\b`).test(text))) {
|
|
13201
|
+
hoisted.delete(text);
|
|
13202
|
+
changed = true;
|
|
13203
|
+
}
|
|
13204
|
+
}
|
|
13205
|
+
if (!changed) break;
|
|
13206
|
+
}
|
|
13207
|
+
const preludeTexts = order.filter((t) => hoisted.has(t));
|
|
13208
|
+
const prelude = preludeTexts.length === 0 ? "" : `${preludeTexts.join("\n")}
|
|
13209
|
+
`;
|
|
13210
|
+
const removed = texts.reduce((n, t) => hoisted.has(t) ? n + t.length + 1 : n, 0);
|
|
13211
|
+
return {
|
|
13212
|
+
prelude,
|
|
13213
|
+
savedBytes: removed - prelude.length,
|
|
13214
|
+
hoistedNames: preludeTexts.flatMap((t) => textNames.get(t)),
|
|
13215
|
+
resolve: (src) => src.replace(MARKER_RE, (m, idStr) => {
|
|
13216
|
+
const text = texts[Number(idStr)];
|
|
13217
|
+
if (text === void 0) throw new Error(`parseman: unknown hoist marker ${m}`);
|
|
13218
|
+
if (hoisted.has(text)) return "";
|
|
13219
|
+
return m.endsWith("\n") ? `${text}
|
|
13220
|
+
` : text;
|
|
13221
|
+
})
|
|
13222
|
+
};
|
|
13223
|
+
};
|
|
13224
|
+
return { claim, finalize };
|
|
13225
|
+
}
|
|
13226
|
+
function escapeName(n) {
|
|
13227
|
+
return n.replace(/\$/g, "\\$");
|
|
13228
|
+
}
|
|
13229
|
+
|
|
12510
13230
|
// src/plugin/index.ts
|
|
12511
13231
|
var import_node_crypto = require("node:crypto");
|
|
12512
13232
|
var PARSEMAN_MODULE = "parseman";
|
|
@@ -12532,37 +13252,37 @@ function getCompiledResolver() {
|
|
|
12532
13252
|
}
|
|
12533
13253
|
var PRIVATE_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
|
|
12534
13254
|
function nearestPackageRoot(file) {
|
|
12535
|
-
let dir =
|
|
13255
|
+
let dir = path2.dirname(file);
|
|
12536
13256
|
for (; ; ) {
|
|
12537
13257
|
try {
|
|
12538
|
-
if (
|
|
13258
|
+
if (fs2.statSync(path2.join(dir, "package.json")).isFile()) return fs2.realpathSync(dir);
|
|
12539
13259
|
} catch {
|
|
12540
13260
|
}
|
|
12541
|
-
const parent =
|
|
13261
|
+
const parent = path2.dirname(dir);
|
|
12542
13262
|
if (parent === dir) return null;
|
|
12543
13263
|
dir = parent;
|
|
12544
13264
|
}
|
|
12545
13265
|
}
|
|
12546
13266
|
function isWithinPackageRoot(file, root) {
|
|
12547
|
-
const relative2 =
|
|
12548
|
-
return relative2 !== ".." && !relative2.startsWith(`..${
|
|
13267
|
+
const relative2 = path2.relative(root, file);
|
|
13268
|
+
return relative2 !== ".." && !relative2.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative2);
|
|
12549
13269
|
}
|
|
12550
13270
|
function resolvePrivateSourceModule(from, specifier) {
|
|
12551
|
-
if (!specifier.startsWith(".") && !
|
|
13271
|
+
if (!specifier.startsWith(".") && !path2.isAbsolute(specifier)) return null;
|
|
12552
13272
|
const packageRoot = nearestPackageRoot(from);
|
|
12553
13273
|
if (!packageRoot) return null;
|
|
12554
|
-
const base =
|
|
12555
|
-
const ext =
|
|
13274
|
+
const base = path2.resolve(path2.dirname(from), specifier);
|
|
13275
|
+
const ext = path2.extname(base);
|
|
12556
13276
|
const candidates = [
|
|
12557
13277
|
base,
|
|
12558
13278
|
...ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base.slice(0, -ext.length)}${next}`) : [],
|
|
12559
13279
|
...!ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base}${next}`) : [],
|
|
12560
|
-
...!ext ? ["index.ts", "index.tsx", "index.mts", "index.cts"].map((next) =>
|
|
13280
|
+
...!ext ? ["index.ts", "index.tsx", "index.mts", "index.cts"].map((next) => path2.join(base, next)) : []
|
|
12561
13281
|
];
|
|
12562
13282
|
for (const candidate of candidates) {
|
|
12563
13283
|
try {
|
|
12564
|
-
if (!
|
|
12565
|
-
const real =
|
|
13284
|
+
if (!fs2.statSync(candidate).isFile()) continue;
|
|
13285
|
+
const real = fs2.realpathSync(candidate);
|
|
12566
13286
|
if (isWithinPackageRoot(real, packageRoot)) return real;
|
|
12567
13287
|
} catch {
|
|
12568
13288
|
}
|
|
@@ -12570,7 +13290,7 @@ function resolvePrivateSourceModule(from, specifier) {
|
|
|
12570
13290
|
return null;
|
|
12571
13291
|
}
|
|
12572
13292
|
function resolveGrammarModule(from, specifier) {
|
|
12573
|
-
if (PRIVATE_SOURCE_EXTENSIONS.has(
|
|
13293
|
+
if (PRIVATE_SOURCE_EXTENSIONS.has(path2.extname(specifier))) {
|
|
12574
13294
|
const file2 = resolvePrivateSourceModule(from, specifier);
|
|
12575
13295
|
return file2 ? { file: file2, source: true } : null;
|
|
12576
13296
|
}
|
|
@@ -12591,7 +13311,7 @@ var _moduleParseCache = /* @__PURE__ */ new Map();
|
|
|
12591
13311
|
function parseModuleCached(filePath) {
|
|
12592
13312
|
let mtimeMs;
|
|
12593
13313
|
try {
|
|
12594
|
-
mtimeMs =
|
|
13314
|
+
mtimeMs = fs2.statSync(filePath).mtimeMs;
|
|
12595
13315
|
} catch {
|
|
12596
13316
|
return null;
|
|
12597
13317
|
}
|
|
@@ -12599,8 +13319,8 @@ function parseModuleCached(filePath) {
|
|
|
12599
13319
|
if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
|
|
12600
13320
|
let parsed = null;
|
|
12601
13321
|
try {
|
|
12602
|
-
const src =
|
|
12603
|
-
const r = (0,
|
|
13322
|
+
const src = fs2.readFileSync(filePath, "utf8");
|
|
13323
|
+
const r = (0, import_oxc_parser4.parseSync)(filePath, src);
|
|
12604
13324
|
if (r.errors.length === 0) parsed = { body: r.program.body, src };
|
|
12605
13325
|
} catch {
|
|
12606
13326
|
}
|
|
@@ -12728,7 +13448,7 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
|
|
|
12728
13448
|
if (_sourceLowering.has(file)) return null;
|
|
12729
13449
|
let mtimeMs;
|
|
12730
13450
|
try {
|
|
12731
|
-
mtimeMs =
|
|
13451
|
+
mtimeMs = fs2.statSync(file).mtimeMs;
|
|
12732
13452
|
} catch {
|
|
12733
13453
|
return null;
|
|
12734
13454
|
}
|
|
@@ -12738,10 +13458,10 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
|
|
|
12738
13458
|
_sourceLowering.add(file);
|
|
12739
13459
|
let parsedModule = null;
|
|
12740
13460
|
try {
|
|
12741
|
-
const source =
|
|
13461
|
+
const source = fs2.readFileSync(file, "utf8");
|
|
12742
13462
|
const transformed = transformMacro(source, file, moduleAliases, warnUnloweredRegex, recovery);
|
|
12743
13463
|
if (!transformed || transformed.warnings.length > 0) return null;
|
|
12744
|
-
const parsed = (0,
|
|
13464
|
+
const parsed = (0, import_oxc_parser4.parseSync)(file, transformed.code);
|
|
12745
13465
|
parsedModule = parsed.errors.length === 0 ? { body: parsed.program.body, src: transformed.code } : null;
|
|
12746
13466
|
return parsedModule;
|
|
12747
13467
|
} catch {
|
|
@@ -12752,9 +13472,20 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
|
|
|
12752
13472
|
}
|
|
12753
13473
|
}
|
|
12754
13474
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false, grammarCoverage = false) {
|
|
13475
|
+
const depth = degradationCaptureDepth();
|
|
13476
|
+
try {
|
|
13477
|
+
return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage);
|
|
13478
|
+
} finally {
|
|
13479
|
+
setReducerResolver(null);
|
|
13480
|
+
endLoweringCapture();
|
|
13481
|
+
endInlineCapCapture();
|
|
13482
|
+
for (const d of unwindDegradationCapture(depth)) console.warn(formatDegradation(d));
|
|
13483
|
+
}
|
|
13484
|
+
}
|
|
13485
|
+
function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage) {
|
|
12755
13486
|
let result;
|
|
12756
13487
|
try {
|
|
12757
|
-
result = (0,
|
|
13488
|
+
result = (0, import_oxc_parser4.parseSync)(id, code);
|
|
12758
13489
|
} catch {
|
|
12759
13490
|
return null;
|
|
12760
13491
|
}
|
|
@@ -12811,6 +13542,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12811
13542
|
}
|
|
12812
13543
|
}
|
|
12813
13544
|
}
|
|
13545
|
+
const reducerResolver = createReducerResolver(id, body, code);
|
|
13546
|
+
setReducerResolver(reducerResolver, code);
|
|
12814
13547
|
const topLevelFunction = (moduleBody, name) => {
|
|
12815
13548
|
for (const st of moduleBody) {
|
|
12816
13549
|
const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
|
|
@@ -12905,13 +13638,18 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12905
13638
|
scope: factoryScope,
|
|
12906
13639
|
imported: true
|
|
12907
13640
|
});
|
|
13641
|
+
reducerResolver.register(file);
|
|
12908
13642
|
}
|
|
12909
13643
|
const scope = /* @__PURE__ */ new Map();
|
|
12910
13644
|
const replacements = [];
|
|
12911
13645
|
const warnings = [];
|
|
13646
|
+
const moduleHoist = grammarCoverage ? void 0 : createModuleHoist();
|
|
12912
13647
|
beginLoweringCapture();
|
|
13648
|
+
beginInlineCapCapture();
|
|
13649
|
+
beginDegradationCapture();
|
|
12913
13650
|
let anyUnresolved = false;
|
|
12914
13651
|
let keepMacroImport = false;
|
|
13652
|
+
const exportedFactories = [];
|
|
12915
13653
|
let runtimeComposeFallback = false;
|
|
12916
13654
|
let ruleMapHolderCounter = 0;
|
|
12917
13655
|
const lineOf = (pos) => {
|
|
@@ -13025,7 +13763,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13025
13763
|
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
13026
13764
|
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
13027
13765
|
).join(", ")}])`;
|
|
13028
|
-
return `{ v: ${JSON.stringify(p.v)}, ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, nullable: ${p.nullable ? mapLit(p.nullable) : "new Map()"}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, nodeMeta: ${mapLit(p.nodeMeta)}, hostMode: ${JSON.stringify(p.hostMode ?? "ast")}, hostBranchElided: ${p.hostBranchElided === true}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
|
|
13766
|
+
return `{ v: ${JSON.stringify(p.v)}, ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, nullable: ${p.nullable ? mapLit(p.nullable) : "new Map()"}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, nodeMeta: ${mapLit(p.nodeMeta)}, hostMode: ${JSON.stringify(p.hostMode ?? "ast")}, hostBranchElided: ${p.hostBranchElided === true}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, needsRawEntry: ${p.needsRawEntry}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
|
|
13029
13767
|
};
|
|
13030
13768
|
const isSpread = (it) => "__spreadLocal" in it;
|
|
13031
13769
|
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
@@ -13036,9 +13774,19 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13036
13774
|
const withHostMode = (grammarExpr, mode, elided) => `/* @__PURE__ */ (m => { for (const k of Object.keys(m)) { Object.defineProperty(m[k], Symbol.for('parseman.fusedHostMode'), { value: ${JSON.stringify(mode)}, enumerable: false }); Object.defineProperty(m[k], Symbol.for('parseman.fusedHostElided'), { value: ${JSON.stringify(elided)}, enumerable: false }) } Object.defineProperty(m, Symbol.for('parseman.fusedHostMode'), { value: ${JSON.stringify(mode)}, enumerable: false }); Object.defineProperty(m, Symbol.for('parseman.fusedHostElided'), { value: ${JSON.stringify(elided)}, enumerable: false }); return m })(${grammarExpr})`;
|
|
13037
13775
|
const withGrammarReflection = (grammarExpr, reflection) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`;
|
|
13038
13776
|
const withCoverageDefinitions = (grammarExpr, definitions) => !grammarCoverage ? grammarExpr : `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarCoverageDefinitions'), { value: Object.freeze(${JSON.stringify(definitions)}.map(Object.freeze)), enumerable: false })`;
|
|
13039
|
-
const emittedCoverageDefinitions = (source) => {
|
|
13777
|
+
const emittedCoverageDefinitions = (source, where) => {
|
|
13040
13778
|
const ids = /* @__PURE__ */ new Set();
|
|
13041
13779
|
for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
|
|
13780
|
+
if (grammarCoverage && ids.size === 0) {
|
|
13781
|
+
recordDegradation({
|
|
13782
|
+
code: "coverage-definitions-unavailable",
|
|
13783
|
+
severity: "warn",
|
|
13784
|
+
where,
|
|
13785
|
+
subject: "generated coverage hooks",
|
|
13786
|
+
fellBackTo: "no coverage definitions could be read out of the generated source, so the grammar carries an EMPTY definition set \u2014 which is no measurement, not full coverage",
|
|
13787
|
+
otherwise: "the emitted hook IDs would form the coverage denominator"
|
|
13788
|
+
});
|
|
13789
|
+
}
|
|
13042
13790
|
return [...ids].sort().map((id2) => ({
|
|
13043
13791
|
id: id2,
|
|
13044
13792
|
kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : id2.startsWith("dispatch:") ? "dispatch-arm" : "choice-arm"
|
|
@@ -13107,26 +13855,6 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13107
13855
|
}
|
|
13108
13856
|
return out;
|
|
13109
13857
|
};
|
|
13110
|
-
const carriedRuleMapsDetailed = (items) => {
|
|
13111
|
-
const maps = [];
|
|
13112
|
-
const opaque = [];
|
|
13113
|
-
const add = (it) => {
|
|
13114
|
-
if (isIR(it)) {
|
|
13115
|
-
maps.push(evalRuleMapIR(it.ir));
|
|
13116
|
-
return;
|
|
13117
|
-
}
|
|
13118
|
-
const o = it;
|
|
13119
|
-
opaque.push({
|
|
13120
|
-
ns: o.ns ?? "<unknown>",
|
|
13121
|
-
ruleNames: o.ruleFns instanceof Map ? [...o.ruleFns.keys()] : []
|
|
13122
|
-
});
|
|
13123
|
-
};
|
|
13124
|
-
for (const it of items) {
|
|
13125
|
-
if (isSpread(it)) for (const p of importedPieces(it.__spreadLocal) ?? []) add(p);
|
|
13126
|
-
else add(it);
|
|
13127
|
-
}
|
|
13128
|
-
return { maps, opaque };
|
|
13129
|
-
};
|
|
13130
13858
|
const materializeLeafCoverage = (items, localRules, localNs, composing, captureTerminals = false) => {
|
|
13131
13859
|
const sources = [];
|
|
13132
13860
|
const add = (item) => {
|
|
@@ -13277,16 +14005,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13277
14005
|
carried.push(...r.carried);
|
|
13278
14006
|
importedFactories.push(...r.importedFactories ?? []);
|
|
13279
14007
|
}
|
|
13280
|
-
const detailed = once(() => carriedRuleMapsDetailed(carried));
|
|
13281
|
-
runFusedGatingDiagnostic(
|
|
13282
|
-
() => detailed().maps,
|
|
13283
|
-
void 0,
|
|
13284
|
-
() => detailed().opaque
|
|
13285
|
-
);
|
|
13286
14008
|
const pieces = materializeCarried(carried, composing, false, cHostMode);
|
|
13287
14009
|
try {
|
|
13288
14010
|
return {
|
|
13289
|
-
replacement: emitFusedSource(pieces),
|
|
14011
|
+
replacement: emitFusedSource(pieces, moduleHoist),
|
|
13290
14012
|
carried,
|
|
13291
14013
|
...composing ? { trivia: composing } : {},
|
|
13292
14014
|
...importedFactories.length ? { importedFactories } : {}
|
|
@@ -13339,12 +14061,6 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13339
14061
|
}
|
|
13340
14062
|
try {
|
|
13341
14063
|
const localNs = nsFor(`composeLeaf${init.start}`);
|
|
13342
|
-
const detailedLeaf = once(() => carriedRuleMapsDetailed(carried));
|
|
13343
|
-
runFusedGatingDiagnostic(
|
|
13344
|
-
() => [...detailedLeaf().maps, [...localRules]],
|
|
13345
|
-
void 0,
|
|
13346
|
-
() => detailedLeaf().opaque
|
|
13347
|
-
);
|
|
13348
14064
|
const plainLocalPiece = compileLinkable([...localRules], localNs, { ...composing ? { trivia: composing } : {}, ...localScanSkip ? { scanSkip: localScanSkip } : {}, recovery });
|
|
13349
14065
|
if (!plainLocalPiece) {
|
|
13350
14066
|
warn(init.start, "composeLeaf(): local rules could not be statically compiled");
|
|
@@ -13360,9 +14076,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13360
14076
|
warn(init.start, "composeLeaf(): every pre-final grammar must explicitly prove recognition-only");
|
|
13361
14077
|
return null;
|
|
13362
14078
|
}
|
|
13363
|
-
const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
|
|
14079
|
+
const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece], moduleHoist));
|
|
13364
14080
|
return {
|
|
13365
|
-
replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)),
|
|
14081
|
+
replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement, `${id} composeLeaf()`)),
|
|
13366
14082
|
...importedFactories.length ? { importedFactories } : {}
|
|
13367
14083
|
};
|
|
13368
14084
|
} catch (e) {
|
|
@@ -13442,7 +14158,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13442
14158
|
const refEntry = scope.get(varName);
|
|
13443
14159
|
const refCombi = refEntry?.combi ?? null;
|
|
13444
14160
|
if (refCombi) {
|
|
13445
|
-
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage
|
|
14161
|
+
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage });
|
|
13446
14162
|
if (compiled2.inlineExpression === null) {
|
|
13447
14163
|
warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
|
|
13448
14164
|
continue;
|
|
@@ -13473,7 +14189,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13473
14189
|
const source = compiledRules.replacement ?? code.slice(init.start, init.end);
|
|
13474
14190
|
let replacement = withCoverageDefinitions(
|
|
13475
14191
|
source,
|
|
13476
|
-
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(source)
|
|
14192
|
+
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(source, `${id} rules()`)
|
|
13477
14193
|
);
|
|
13478
14194
|
if (compiledRules.replacement !== null) {
|
|
13479
14195
|
replacement = withHostMode(replacement, compiledRules.hostMode, compiledRules.hostBranchElided);
|
|
@@ -13498,7 +14214,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13498
14214
|
localComposedCarried.set(varName, fused.carried);
|
|
13499
14215
|
if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
|
|
13500
14216
|
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
13501
|
-
replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
|
|
14217
|
+
replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement, `${id} compose()`)) });
|
|
13502
14218
|
markUsedImportedFactories(fused.importedFactories);
|
|
13503
14219
|
continue;
|
|
13504
14220
|
}
|
|
@@ -13545,11 +14261,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13545
14261
|
scope.set(varName, staticValue);
|
|
13546
14262
|
continue;
|
|
13547
14263
|
}
|
|
13548
|
-
if (factoryDecls.has(varName))
|
|
14264
|
+
if (factoryDecls.has(varName)) {
|
|
14265
|
+
if (exportPrefix) exportedFactories.push({ name: varName, pos: init.start });
|
|
14266
|
+
continue;
|
|
14267
|
+
}
|
|
13549
14268
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
13550
14269
|
continue;
|
|
13551
14270
|
}
|
|
13552
|
-
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage
|
|
14271
|
+
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage });
|
|
13553
14272
|
if (compiled.inlineExpression === null) {
|
|
13554
14273
|
warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
|
|
13555
14274
|
continue;
|
|
@@ -13616,6 +14335,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13616
14335
|
for (const imp of macroImports) {
|
|
13617
14336
|
imp.fullyResolved = !anyUnresolved && !keepMacroImport;
|
|
13618
14337
|
}
|
|
14338
|
+
if (exportedFactories.length > 0 && macroImports.some((imp) => imp.fullyResolved)) {
|
|
14339
|
+
const which = exportedFactories.map((f) => ` - ${id}:${lineOf(f.pos)} \u2014 export const ${f.name}`).join("\n");
|
|
14340
|
+
throw new Error(
|
|
14341
|
+
`${id} \u2014 a rules() factory cannot be exported; lowering leaves its body verbatim, so the export would ship a binding that throws ReferenceError on first call:
|
|
14342
|
+
${which}
|
|
14343
|
+
Drop the \`export\`. The factory is macro-only; \`rules(${exportedFactories[0].name})\` is lowered in place and needs no runtime value.`
|
|
14344
|
+
);
|
|
14345
|
+
}
|
|
13619
14346
|
const ms = new import_magic_string.default(code);
|
|
13620
14347
|
const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
|
|
13621
14348
|
const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
|
|
@@ -13664,8 +14391,21 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13664
14391
|
}
|
|
13665
14392
|
}
|
|
13666
14393
|
const applied = runtimeComposeFallback ? [] : replacements;
|
|
14394
|
+
const hoisted = applied.length > 0 ? moduleHoist?.finalize() : void 0;
|
|
13667
14395
|
for (const { start, end, replacement } of applied.slice().sort((a, b) => b.start - a.start)) {
|
|
13668
|
-
ms.overwrite(start, end, replacement);
|
|
14396
|
+
ms.overwrite(start, end, hoisted ? hoisted.resolve(replacement) : replacement);
|
|
14397
|
+
}
|
|
14398
|
+
if (hoisted !== void 0 && hoisted.prelude !== "") {
|
|
14399
|
+
const claiming = applied.filter((r) => HOIST_MARKER_PROBE.test(r.replacement));
|
|
14400
|
+
if (claiming.length === 0) {
|
|
14401
|
+
throw new Error(`${id} \u2014 internal: a module-hoist prelude was produced but no applied replacement carries a hoist marker`);
|
|
14402
|
+
}
|
|
14403
|
+
const firstStart = Math.min(...claiming.map((r) => r.start));
|
|
14404
|
+
const anchor = body.find((s) => s.start <= firstStart && firstStart < s.end);
|
|
14405
|
+
if (anchor === void 0) {
|
|
14406
|
+
throw new Error(`${id} \u2014 internal: no top-level statement encloses macro replacement at ${firstStart}`);
|
|
14407
|
+
}
|
|
14408
|
+
ms.appendLeft(anchor.start, hoisted.prelude);
|
|
13669
14409
|
}
|
|
13670
14410
|
if (applied.length > 0) {
|
|
13671
14411
|
ms.prepend(
|
|
@@ -13675,12 +14415,38 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13675
14415
|
`
|
|
13676
14416
|
);
|
|
13677
14417
|
}
|
|
14418
|
+
if (applied.length > 0) {
|
|
14419
|
+
let sourceFree = null;
|
|
14420
|
+
const freeInSource = () => sourceFree ??= new Set(findFreeIdentifiers(code, id).map((f) => f.name));
|
|
14421
|
+
const free = findFreeIdentifiers(ms.toString(), id).filter((f) => !freeInSource().has(f.name));
|
|
14422
|
+
if (free.length > 0) {
|
|
14423
|
+
const macroProvided = free.filter((f) => allNames.has(f.name));
|
|
14424
|
+
const where = (f) => ` - ${id}:${f.line}:${f.column} \u2014 \`${f.name}\`` + (f.enclosing ? `, inside \`${f.enclosing}\`` : "");
|
|
14425
|
+
const cause = macroProvided.length > 0 ? `
|
|
14426
|
+
${macroProvided.length === free.length ? "All" : `${macroProvided.length}`} of these came from the \`with { type: 'macro' }\` import, which was removed because every macro declaration lowered. Something still names them verbatim \u2014 a declaration parseman left as text rather than compiling. Make that declaration macro-buildable, or stop exporting it so lowering can drop it.` : "";
|
|
14427
|
+
throw new Error(
|
|
14428
|
+
`${id} \u2014 parseman will not emit this module: ${free.length} identifier(s) are read but bound by nothing, so it would throw ReferenceError at runtime (positions are in the EMITTED module):
|
|
14429
|
+
${free.map(where).join("\n")}${cause}`
|
|
14430
|
+
);
|
|
14431
|
+
}
|
|
14432
|
+
}
|
|
14433
|
+
for (const line of formatInlineCapSites(endInlineCapCapture(), resolveInlineMax())) warnings.push(`${id}: ${line}`);
|
|
13678
14434
|
const unlowered = endLoweringCapture();
|
|
13679
14435
|
if (warnUnloweredRegex) {
|
|
13680
14436
|
for (const src of unlowered) {
|
|
13681
14437
|
warnings.push(`${id}: regex ${src} did not lower to a fast charCodeAt scan (RegExp.exec fallback)`);
|
|
13682
14438
|
}
|
|
13683
14439
|
}
|
|
14440
|
+
setReducerResolver(null);
|
|
14441
|
+
const degradations = endDegradationCapture();
|
|
14442
|
+
if (degradations.length > 0) {
|
|
14443
|
+
const lines = formatDegradations(degradations);
|
|
14444
|
+
if (resolveDegradationLevel() === "error") {
|
|
14445
|
+
throw new Error(`parseman: ${degradations.length} degraded compilation path(s) in ${id}
|
|
14446
|
+
${lines.join("\n")}`);
|
|
14447
|
+
}
|
|
14448
|
+
for (const l of lines) warnings.push(`${id}: ${l}`);
|
|
14449
|
+
}
|
|
13684
14450
|
return {
|
|
13685
14451
|
code: ms.toString(),
|
|
13686
14452
|
map: ms.generateMap({ hires: true }),
|