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.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// src/plugin/index.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as fs2 from "node:fs";
|
|
3
|
+
import * as path2 from "node:path";
|
|
4
4
|
import { createUnplugin } from "unplugin";
|
|
5
|
-
import { parseSync as
|
|
5
|
+
import { parseSync as parseSync4 } from "oxc-parser";
|
|
6
6
|
import { ResolverFactory } from "oxc-resolver";
|
|
7
7
|
import MagicString from "magic-string";
|
|
8
8
|
|
|
9
9
|
// src/combinators/first-set.ts
|
|
10
|
-
function resolveNamedRef(p,
|
|
11
|
-
if (
|
|
10
|
+
function resolveNamedRef(p, resolve3) {
|
|
11
|
+
if (resolve3 === void 0) return void 0;
|
|
12
12
|
const name = p._ruleName;
|
|
13
|
-
return name === void 0 ? void 0 :
|
|
13
|
+
return name === void 0 ? void 0 : resolve3(name);
|
|
14
14
|
}
|
|
15
15
|
function union(a, b) {
|
|
16
16
|
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
@@ -32,12 +32,12 @@ function fromChar(code) {
|
|
|
32
32
|
return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
|
|
33
33
|
}
|
|
34
34
|
function startsFirstSet(combinator, input, pos) {
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
if (
|
|
35
|
+
const fs3 = combinator._meta.firstSet;
|
|
36
|
+
if (fs3.kind === "any") return true;
|
|
37
|
+
if (fs3.kind === "empty") return false;
|
|
38
38
|
const code = input.codePointAt(pos);
|
|
39
39
|
if (code === void 0) return false;
|
|
40
|
-
for (const r of
|
|
40
|
+
for (const r of fs3.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
function fromRange(lo, hi) {
|
|
@@ -49,18 +49,19 @@ function any() {
|
|
|
49
49
|
function empty() {
|
|
50
50
|
return { kind: "empty" };
|
|
51
51
|
}
|
|
52
|
-
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(),
|
|
52
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve3) {
|
|
53
53
|
if (seen.has(p)) return true;
|
|
54
54
|
seen.add(p);
|
|
55
|
-
const me = (c) => matchesEmpty(c, seen,
|
|
55
|
+
const me = (c) => matchesEmpty(c, seen, resolve3);
|
|
56
56
|
const d = p._def;
|
|
57
57
|
switch (d.tag) {
|
|
58
58
|
case "literal":
|
|
59
59
|
return d.value.length === 0;
|
|
60
60
|
case "keywords":
|
|
61
61
|
return false;
|
|
62
|
+
// The routed token itself is the selector's match, never empty; a fallback can be.
|
|
62
63
|
case "routed":
|
|
63
|
-
return false;
|
|
64
|
+
return d.fallback === void 0 ? false : me(d.fallback);
|
|
64
65
|
case "regex":
|
|
65
66
|
try {
|
|
66
67
|
const m = new RegExp(d.source).exec("");
|
|
@@ -106,7 +107,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
106
107
|
try {
|
|
107
108
|
return me(d.thunk());
|
|
108
109
|
} catch {
|
|
109
|
-
const t = resolveNamedRef(p,
|
|
110
|
+
const t = resolveNamedRef(p, resolve3);
|
|
110
111
|
return t ? me(t) : true;
|
|
111
112
|
}
|
|
112
113
|
default:
|
|
@@ -132,29 +133,29 @@ function narrowBy(acc, constraint) {
|
|
|
132
133
|
}
|
|
133
134
|
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
134
135
|
}
|
|
135
|
-
function applyAssertion(
|
|
136
|
-
if (assertion === null) return
|
|
137
|
-
if (
|
|
138
|
-
return narrowBy(
|
|
136
|
+
function applyAssertion(fs3, assertion) {
|
|
137
|
+
if (assertion === null) return fs3;
|
|
138
|
+
if (fs3.kind === "empty") return assertion;
|
|
139
|
+
return narrowBy(fs3, assertion) ?? fs3;
|
|
139
140
|
}
|
|
140
141
|
function sequenceFirstSet(parsers) {
|
|
141
|
-
let
|
|
142
|
+
let fs3 = empty();
|
|
142
143
|
let assertion = null;
|
|
143
144
|
for (const p of parsers) {
|
|
144
145
|
if (isPositiveLookahead(p)) {
|
|
145
146
|
assertion = narrowBy(assertion, p._meta.firstSet);
|
|
146
147
|
continue;
|
|
147
148
|
}
|
|
148
|
-
if (!isZeroWidthAssertion(p))
|
|
149
|
-
if (!matchesEmpty(p)) return applyAssertion(
|
|
149
|
+
if (!isZeroWidthAssertion(p)) fs3 = union(fs3, p._meta.firstSet);
|
|
150
|
+
if (!matchesEmpty(p)) return applyAssertion(fs3, assertion);
|
|
150
151
|
}
|
|
151
|
-
return applyAssertion(
|
|
152
|
+
return applyAssertion(fs3, assertion);
|
|
152
153
|
}
|
|
153
|
-
function firstSetOf(p, seen = /* @__PURE__ */ new Set(),
|
|
154
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve3) {
|
|
154
155
|
if (seen.has(p)) return any();
|
|
155
156
|
seen.add(p);
|
|
156
|
-
const
|
|
157
|
-
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(),
|
|
157
|
+
const fs3 = (c) => firstSetOf(c, seen, resolve3);
|
|
158
|
+
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve3);
|
|
158
159
|
const d = p._def;
|
|
159
160
|
switch (d.tag) {
|
|
160
161
|
case "literal":
|
|
@@ -163,17 +164,17 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
163
164
|
return p._meta.firstSet;
|
|
164
165
|
// terminals: no refs, cached set is exact
|
|
165
166
|
case "dispatch":
|
|
166
|
-
return
|
|
167
|
+
return fs3(d.selector);
|
|
167
168
|
case "lazy":
|
|
168
169
|
try {
|
|
169
|
-
return
|
|
170
|
+
return fs3(d.thunk());
|
|
170
171
|
} catch {
|
|
171
|
-
const t = resolveNamedRef(p,
|
|
172
|
-
return t ?
|
|
172
|
+
const t = resolveNamedRef(p, resolve3);
|
|
173
|
+
return t ? fs3(t) : any();
|
|
173
174
|
}
|
|
174
175
|
case "choice": {
|
|
175
176
|
let out = empty();
|
|
176
|
-
for (const arm of d.parsers) out = union(out,
|
|
177
|
+
for (const arm of d.parsers) out = union(out, fs3(arm));
|
|
177
178
|
return out;
|
|
178
179
|
}
|
|
179
180
|
case "sequence": {
|
|
@@ -181,17 +182,17 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
181
182
|
let assertion = null;
|
|
182
183
|
for (const term of d.parsers) {
|
|
183
184
|
if (isPositiveLookahead(term)) {
|
|
184
|
-
assertion = narrowBy(assertion,
|
|
185
|
+
assertion = narrowBy(assertion, fs3(term));
|
|
185
186
|
continue;
|
|
186
187
|
}
|
|
187
|
-
if (!isZeroWidthAssertion(term)) out = union(out,
|
|
188
|
+
if (!isZeroWidthAssertion(term)) out = union(out, fs3(term));
|
|
188
189
|
if (!empties(term)) return applyAssertion(out, assertion);
|
|
189
190
|
}
|
|
190
191
|
return applyAssertion(out, assertion);
|
|
191
192
|
}
|
|
192
193
|
case "peek": {
|
|
193
194
|
const inner = d.parser;
|
|
194
|
-
return empties(inner) ? any() :
|
|
195
|
+
return empties(inner) ? any() : fs3(inner);
|
|
195
196
|
}
|
|
196
197
|
case "oneOrMore":
|
|
197
198
|
case "many":
|
|
@@ -204,12 +205,12 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
204
205
|
case "node":
|
|
205
206
|
case "grammar":
|
|
206
207
|
case "expect":
|
|
207
|
-
return
|
|
208
|
+
return fs3(d.parser);
|
|
208
209
|
case "sepBy":
|
|
209
|
-
return
|
|
210
|
+
return fs3(d.parser);
|
|
210
211
|
// both min 0 and min 1 start with the item
|
|
211
212
|
case "skip":
|
|
212
|
-
return
|
|
213
|
+
return fs3(d.main);
|
|
213
214
|
default:
|
|
214
215
|
return p._meta.firstSet;
|
|
215
216
|
}
|
|
@@ -680,13 +681,13 @@ function shortScanner(source, flags) {
|
|
|
680
681
|
return minOne && end === pos ? null : end;
|
|
681
682
|
};
|
|
682
683
|
}
|
|
683
|
-
function asciiCaseFold(
|
|
684
|
-
if (
|
|
685
|
-
const ranges =
|
|
684
|
+
function asciiCaseFold(fs3) {
|
|
685
|
+
if (fs3.kind !== "ranges") return fs3;
|
|
686
|
+
const ranges = fs3.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
|
|
686
687
|
const add = (lo, hi) => {
|
|
687
688
|
if (lo <= hi) ranges.push({ lo, hi });
|
|
688
689
|
};
|
|
689
|
-
for (const r of
|
|
690
|
+
for (const r of fs3.ranges) {
|
|
690
691
|
const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
|
|
691
692
|
add(uLo + 32, uHi + 32);
|
|
692
693
|
const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
|
|
@@ -744,9 +745,9 @@ function charNode(set, nl) {
|
|
|
744
745
|
return { t: "char", set, nl };
|
|
745
746
|
}
|
|
746
747
|
function rangesToSet(ranges) {
|
|
747
|
-
let
|
|
748
|
-
for (const [lo, hi] of ranges)
|
|
749
|
-
return
|
|
748
|
+
let fs3 = empty();
|
|
749
|
+
for (const [lo, hi] of ranges) fs3 = union(fs3, fromRange(lo, hi));
|
|
750
|
+
return fs3;
|
|
750
751
|
}
|
|
751
752
|
function rangesHaveNewline(ranges) {
|
|
752
753
|
return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
|
|
@@ -952,17 +953,17 @@ function firstSet(n) {
|
|
|
952
953
|
case "rep":
|
|
953
954
|
return firstSet(n.node);
|
|
954
955
|
case "alt": {
|
|
955
|
-
let
|
|
956
|
-
for (const arm of n.arms)
|
|
957
|
-
return
|
|
956
|
+
let fs3 = empty();
|
|
957
|
+
for (const arm of n.arms) fs3 = union(fs3, firstSet(arm));
|
|
958
|
+
return fs3;
|
|
958
959
|
}
|
|
959
960
|
case "seq": {
|
|
960
|
-
let
|
|
961
|
+
let fs3 = empty();
|
|
961
962
|
for (const part of n.parts) {
|
|
962
|
-
|
|
963
|
+
fs3 = union(fs3, firstSet(part));
|
|
963
964
|
if (!nullable(part)) break;
|
|
964
965
|
}
|
|
965
|
-
return
|
|
966
|
+
return fs3;
|
|
966
967
|
}
|
|
967
968
|
}
|
|
968
969
|
}
|
|
@@ -1491,8 +1492,8 @@ function classifyTriviaArm(arm) {
|
|
|
1491
1492
|
}
|
|
1492
1493
|
const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
|
|
1493
1494
|
if (lc) {
|
|
1494
|
-
const
|
|
1495
|
-
return { kind: "untilLineBreak", code: (
|
|
1495
|
+
const marker2 = lc[1];
|
|
1496
|
+
return { kind: "untilLineBreak", code: (marker2.length === 2 ? marker2[1] : marker2[0]).charCodeAt(0) };
|
|
1496
1497
|
}
|
|
1497
1498
|
return null;
|
|
1498
1499
|
}
|
|
@@ -1637,12 +1638,12 @@ function orSentinel(a, b) {
|
|
|
1637
1638
|
}
|
|
1638
1639
|
};
|
|
1639
1640
|
}
|
|
1640
|
-
function firstSetSentinel(
|
|
1641
|
-
if (
|
|
1642
|
-
const ranges =
|
|
1641
|
+
function firstSetSentinel(fs3) {
|
|
1642
|
+
if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return null;
|
|
1643
|
+
const ranges = fs3.ranges;
|
|
1643
1644
|
return {
|
|
1644
1645
|
_tag: "firstSetSentinel",
|
|
1645
|
-
_meta: { firstSet:
|
|
1646
|
+
_meta: { firstSet: fs3, canMatchNewline: false, isTrivia: false },
|
|
1646
1647
|
_def: { tag: "unknown" },
|
|
1647
1648
|
parse(input, pos) {
|
|
1648
1649
|
const code = pos < input.length ? input.codePointAt(pos) : void 0;
|
|
@@ -1684,11 +1685,11 @@ function sequence(...parsers) {
|
|
|
1684
1685
|
let followSentinels;
|
|
1685
1686
|
function parseTolerant(input, pos, ctx) {
|
|
1686
1687
|
followSentinels ??= parsers.map((_, i) => {
|
|
1687
|
-
const
|
|
1688
|
+
const fs3 = parsers.slice(i + 1).reduce(
|
|
1688
1689
|
(acc, p) => union(acc, firstSetOf(p)),
|
|
1689
1690
|
{ kind: "empty" }
|
|
1690
1691
|
);
|
|
1691
|
-
return firstSetSentinel(
|
|
1692
|
+
return firstSetSentinel(fs3);
|
|
1692
1693
|
});
|
|
1693
1694
|
const values = def.valueUnused ? void 0 : [];
|
|
1694
1695
|
let cur = pos;
|
|
@@ -1831,8 +1832,11 @@ function derive(c, seen) {
|
|
|
1831
1832
|
case "not":
|
|
1832
1833
|
case "peek":
|
|
1833
1834
|
return deriveExpected2(def.parser);
|
|
1835
|
+
// A bare routed() can only fail as "there is no routed token here". With a
|
|
1836
|
+
// fallback, THAT is the branch that can fail, so its expectations are the useful
|
|
1837
|
+
// ones — the routed path either succeeds or is not taken.
|
|
1834
1838
|
case "routed":
|
|
1835
|
-
return ["routed()"];
|
|
1839
|
+
return def.fallback === void 0 ? ["routed()"] : deriveExpected2(def.fallback);
|
|
1836
1840
|
case "lazy": {
|
|
1837
1841
|
const name = c._ruleName;
|
|
1838
1842
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -2019,7 +2023,7 @@ function detectStrategy(parsers) {
|
|
|
2019
2023
|
function bareLeadingTermKey(term) {
|
|
2020
2024
|
const d = term._def;
|
|
2021
2025
|
if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
|
|
2022
|
-
if (d.tag === "regex") return `R:${d.source
|
|
2026
|
+
if (d.tag === "regex") return `R:${JSON.stringify([d.source, d.flags])}`;
|
|
2023
2027
|
return null;
|
|
2024
2028
|
}
|
|
2025
2029
|
function detectSharedPrefix(parsers) {
|
|
@@ -2135,10 +2139,10 @@ function codesToFirstSet(codes) {
|
|
|
2135
2139
|
ranges.push({ lo, hi });
|
|
2136
2140
|
return { kind: "ranges", ranges };
|
|
2137
2141
|
}
|
|
2138
|
-
function inFirstSet(code,
|
|
2139
|
-
if (
|
|
2140
|
-
if (
|
|
2141
|
-
for (const r of
|
|
2142
|
+
function inFirstSet(code, fs3) {
|
|
2143
|
+
if (fs3.kind === "any") return true;
|
|
2144
|
+
if (fs3.kind === "empty") return false;
|
|
2145
|
+
for (const r of fs3.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
2142
2146
|
return false;
|
|
2143
2147
|
}
|
|
2144
2148
|
function areDisjoint(sets) {
|
|
@@ -2151,9 +2155,9 @@ function areDisjoint(sets) {
|
|
|
2151
2155
|
function buildAsciiDispatch(parsers) {
|
|
2152
2156
|
const table = Array(128).fill(-1);
|
|
2153
2157
|
for (let i = 0; i < parsers.length; i++) {
|
|
2154
|
-
const
|
|
2155
|
-
if (
|
|
2156
|
-
for (const { lo, hi } of
|
|
2158
|
+
const fs3 = parsers[i]._meta.firstSet;
|
|
2159
|
+
if (fs3.kind !== "ranges") continue;
|
|
2160
|
+
for (const { lo, hi } of fs3.ranges) {
|
|
2157
2161
|
for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
|
|
2158
2162
|
table[code] = i;
|
|
2159
2163
|
}
|
|
@@ -2274,17 +2278,21 @@ function when(key, parser2, opts = {}) {
|
|
|
2274
2278
|
function otherwise(parser2) {
|
|
2275
2279
|
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2276
2280
|
}
|
|
2277
|
-
function routed() {
|
|
2281
|
+
function routed(fallback) {
|
|
2278
2282
|
return {
|
|
2279
2283
|
_tag: "routed",
|
|
2280
|
-
_meta: {
|
|
2281
|
-
|
|
2284
|
+
_meta: {
|
|
2285
|
+
// The routed token is whatever the selector matched, so the first set stays
|
|
2286
|
+
// `any` even with a fallback — `any` already subsumes the fallback's.
|
|
2287
|
+
firstSet: { kind: "any" },
|
|
2288
|
+
canMatchNewline: fallback?._meta.canMatchNewline ?? false,
|
|
2289
|
+
isTrivia: false
|
|
2290
|
+
},
|
|
2291
|
+
_def: fallback === void 0 ? { tag: "routed" } : { tag: "routed", fallback },
|
|
2282
2292
|
parse(_input, pos, ctx) {
|
|
2283
2293
|
const item = ctx._routed;
|
|
2284
|
-
if (item === void 0) {
|
|
2285
|
-
|
|
2286
|
-
}
|
|
2287
|
-
if (pos !== item.span.start) {
|
|
2294
|
+
if (item === void 0 || pos !== item.span.start) {
|
|
2295
|
+
if (fallback !== void 0) return fallback.parse(_input, pos, ctx);
|
|
2288
2296
|
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2289
2297
|
}
|
|
2290
2298
|
if (cstCaptureActive(ctx)) {
|
|
@@ -2791,12 +2799,12 @@ function markUnusedValues(root) {
|
|
|
2791
2799
|
}
|
|
2792
2800
|
};
|
|
2793
2801
|
const resolveRoot = (c) => {
|
|
2794
|
-
const
|
|
2802
|
+
const guard = /* @__PURE__ */ new Set();
|
|
2795
2803
|
let cur = c;
|
|
2796
|
-
while (!
|
|
2804
|
+
while (!guard.has(cur)) {
|
|
2797
2805
|
const d = cur._def;
|
|
2798
2806
|
if (d.tag !== "lazy") return cur;
|
|
2799
|
-
|
|
2807
|
+
guard.add(cur);
|
|
2800
2808
|
try {
|
|
2801
2809
|
cur = d.thunk();
|
|
2802
2810
|
} catch {
|
|
@@ -2938,10 +2946,11 @@ function childrenOf(def) {
|
|
|
2938
2946
|
return [def.main, def.skipped];
|
|
2939
2947
|
case "scanTo":
|
|
2940
2948
|
return [def.sentinel, ...def.skip];
|
|
2949
|
+
case "routed":
|
|
2950
|
+
return def.fallback ? [def.fallback] : [];
|
|
2941
2951
|
case "literal":
|
|
2942
2952
|
case "regex":
|
|
2943
2953
|
case "keywords":
|
|
2944
|
-
case "routed":
|
|
2945
2954
|
case "guard":
|
|
2946
2955
|
case "lazy":
|
|
2947
2956
|
case "unknown":
|
|
@@ -3164,54 +3173,276 @@ function peek(combinator) {
|
|
|
3164
3173
|
};
|
|
3165
3174
|
}
|
|
3166
3175
|
|
|
3176
|
+
// src/compiler/degradation.ts
|
|
3177
|
+
function formatDegradation(d) {
|
|
3178
|
+
return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
|
|
3179
|
+
}
|
|
3180
|
+
var DETAIL_CAP = 8;
|
|
3181
|
+
function formatDegradations(list) {
|
|
3182
|
+
const byCode = /* @__PURE__ */ new Map();
|
|
3183
|
+
for (const d of list) {
|
|
3184
|
+
const bucket = byCode.get(d.code);
|
|
3185
|
+
if (bucket) bucket.push(d);
|
|
3186
|
+
else byCode.set(d.code, [d]);
|
|
3187
|
+
}
|
|
3188
|
+
const out = [];
|
|
3189
|
+
for (const [code, items] of byCode) {
|
|
3190
|
+
for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
|
|
3191
|
+
if (items.length > DETAIL_CAP) {
|
|
3192
|
+
out.push(
|
|
3193
|
+
`[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.`
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
return out;
|
|
3198
|
+
}
|
|
3199
|
+
var _sinks = [];
|
|
3200
|
+
function beginDegradationCapture() {
|
|
3201
|
+
_sinks.push(/* @__PURE__ */ new Map());
|
|
3202
|
+
}
|
|
3203
|
+
function endDegradationCapture() {
|
|
3204
|
+
const sink = _sinks.pop();
|
|
3205
|
+
return sink ? [...sink.values()] : [];
|
|
3206
|
+
}
|
|
3207
|
+
function degradationCaptureDepth() {
|
|
3208
|
+
return _sinks.length;
|
|
3209
|
+
}
|
|
3210
|
+
function unwindDegradationCapture(depth) {
|
|
3211
|
+
const found = [];
|
|
3212
|
+
while (_sinks.length > depth) {
|
|
3213
|
+
const sink = _sinks.pop();
|
|
3214
|
+
found.unshift(...sink.values());
|
|
3215
|
+
}
|
|
3216
|
+
return found;
|
|
3217
|
+
}
|
|
3218
|
+
function resolveDegradationLevel(explicit) {
|
|
3219
|
+
if (explicit !== void 0) return explicit;
|
|
3220
|
+
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
|
|
3221
|
+
if (env === "off" || env === "warn" || env === "error") return env;
|
|
3222
|
+
return "warn";
|
|
3223
|
+
}
|
|
3224
|
+
function degradationKey(d) {
|
|
3225
|
+
return JSON.stringify([d.code, d.where, d.subject]);
|
|
3226
|
+
}
|
|
3227
|
+
function recordDegradation(d) {
|
|
3228
|
+
const level = resolveDegradationLevel();
|
|
3229
|
+
if (level === "off") return;
|
|
3230
|
+
const key = degradationKey(d);
|
|
3231
|
+
const sink = _sinks[_sinks.length - 1];
|
|
3232
|
+
if (sink) {
|
|
3233
|
+
if (!sink.has(key)) sink.set(key, d);
|
|
3234
|
+
return;
|
|
3235
|
+
}
|
|
3236
|
+
if (_immediate.has(key)) return;
|
|
3237
|
+
_immediate.add(key);
|
|
3238
|
+
if (level === "error") throw new Error(`parseman: degraded compilation path
|
|
3239
|
+
${formatDegradation(d)}`);
|
|
3240
|
+
console.warn(formatDegradation(d));
|
|
3241
|
+
}
|
|
3242
|
+
var _immediate = /* @__PURE__ */ new Set();
|
|
3243
|
+
function beginCompileDegradationDrain() {
|
|
3244
|
+
if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
|
|
3245
|
+
};
|
|
3246
|
+
const depth = degradationCaptureDepth();
|
|
3247
|
+
beginDegradationCapture();
|
|
3248
|
+
return (report) => {
|
|
3249
|
+
const found = unwindDegradationCapture(depth);
|
|
3250
|
+
if (!report || found.length === 0) return;
|
|
3251
|
+
const fresh = found.filter((d) => {
|
|
3252
|
+
const k = degradationKey(d);
|
|
3253
|
+
if (_immediate.has(k)) return false;
|
|
3254
|
+
_immediate.add(k);
|
|
3255
|
+
return true;
|
|
3256
|
+
});
|
|
3257
|
+
if (fresh.length === 0) return;
|
|
3258
|
+
const lines = formatDegradations(fresh);
|
|
3259
|
+
if (resolveDegradationLevel() === "error") {
|
|
3260
|
+
throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
|
|
3261
|
+
${lines.join("\n")}`);
|
|
3262
|
+
}
|
|
3263
|
+
for (const l of lines) console.warn(l);
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3167
3267
|
// src/compiler/build-arity.ts
|
|
3168
3268
|
function cstOutputHost(build) {
|
|
3169
3269
|
return build?._parsemanCstOutput === true;
|
|
3170
3270
|
}
|
|
3171
|
-
var
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
|
|
3271
|
+
var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
|
|
3272
|
+
var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
|
|
3273
|
+
var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
|
|
3274
|
+
var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
|
|
3176
3275
|
function confirmedBuildArity(src) {
|
|
3177
3276
|
const s = src.trim();
|
|
3178
|
-
|
|
3179
|
-
if (
|
|
3180
|
-
if (
|
|
3181
|
-
const inner = (
|
|
3182
|
-
if (inner ===
|
|
3183
|
-
|
|
3277
|
+
if (NATIVE_CODE_RE.test(s)) return null;
|
|
3278
|
+
if (/\barguments\b/.test(s)) return null;
|
|
3279
|
+
if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
|
|
3280
|
+
const inner = paramListText(s);
|
|
3281
|
+
if (inner === null) return null;
|
|
3282
|
+
if (inner.trim() === "") return 0;
|
|
3283
|
+
const parts = splitTopLevel(inner);
|
|
3284
|
+
if (parts === null) return null;
|
|
3184
3285
|
for (const part of parts) {
|
|
3185
|
-
if (!
|
|
3286
|
+
if (!isConfirmableParam(part.trim())) return null;
|
|
3186
3287
|
}
|
|
3187
|
-
if (/\barguments\b/.test(s)) return null;
|
|
3188
3288
|
return parts.length;
|
|
3189
3289
|
}
|
|
3290
|
+
function paramListText(s) {
|
|
3291
|
+
const open = PARAM_LIST_OPEN_RE.exec(s);
|
|
3292
|
+
if (!open) return null;
|
|
3293
|
+
const start = open[0].length;
|
|
3294
|
+
const end = matchingParen(s, start);
|
|
3295
|
+
if (end === null) return null;
|
|
3296
|
+
if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
|
|
3297
|
+
return s.slice(start, end);
|
|
3298
|
+
}
|
|
3299
|
+
function matchingParen(s, start) {
|
|
3300
|
+
let depth = 0;
|
|
3301
|
+
for (let i = start; i < s.length; i++) {
|
|
3302
|
+
const c = s[i];
|
|
3303
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
3304
|
+
const close = skipString(s, i);
|
|
3305
|
+
if (close === null) return null;
|
|
3306
|
+
i = close;
|
|
3307
|
+
continue;
|
|
3308
|
+
}
|
|
3309
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
3310
|
+
else if (c === "]" || c === "}") {
|
|
3311
|
+
if (--depth < 0) return null;
|
|
3312
|
+
} else if (c === ")") {
|
|
3313
|
+
if (depth === 0) return i;
|
|
3314
|
+
depth--;
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
return null;
|
|
3318
|
+
}
|
|
3319
|
+
function skipString(s, open) {
|
|
3320
|
+
const quote = s[open];
|
|
3321
|
+
for (let i = open + 1; i < s.length; i++) {
|
|
3322
|
+
if (s[i] === "\\") {
|
|
3323
|
+
i++;
|
|
3324
|
+
continue;
|
|
3325
|
+
}
|
|
3326
|
+
if (s[i] === quote) return i;
|
|
3327
|
+
}
|
|
3328
|
+
return null;
|
|
3329
|
+
}
|
|
3330
|
+
function splitTopLevel(inner) {
|
|
3331
|
+
const parts = [];
|
|
3332
|
+
let depth = 0;
|
|
3333
|
+
let angle = 0;
|
|
3334
|
+
let last = 0;
|
|
3335
|
+
for (let i = 0; i < inner.length; i++) {
|
|
3336
|
+
const c = inner[i];
|
|
3337
|
+
if (c === "=" && inner[i + 1] === ">") {
|
|
3338
|
+
i++;
|
|
3339
|
+
continue;
|
|
3340
|
+
}
|
|
3341
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
3342
|
+
const close = skipString(inner, i);
|
|
3343
|
+
if (close === null) return null;
|
|
3344
|
+
i = close;
|
|
3345
|
+
continue;
|
|
3346
|
+
}
|
|
3347
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
3348
|
+
else if (c === ")" || c === "]" || c === "}") {
|
|
3349
|
+
if (--depth < 0) return null;
|
|
3350
|
+
} else if (c === "<") angle++;
|
|
3351
|
+
else if (c === ">") {
|
|
3352
|
+
if (--angle < 0) return null;
|
|
3353
|
+
} else if (c === "," && depth === 0 && angle === 0) {
|
|
3354
|
+
parts.push(inner.slice(last, i));
|
|
3355
|
+
last = i + 1;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
if (depth !== 0 || angle !== 0) return null;
|
|
3359
|
+
parts.push(inner.slice(last));
|
|
3360
|
+
return parts;
|
|
3361
|
+
}
|
|
3362
|
+
function isConfirmableParam(part) {
|
|
3363
|
+
const head = PARAM_HEAD_RE.exec(part);
|
|
3364
|
+
if (!head) return false;
|
|
3365
|
+
const rest = part.slice(head[0].length);
|
|
3366
|
+
if (rest === "") return true;
|
|
3367
|
+
if (rest[0] !== ":") return false;
|
|
3368
|
+
return !hasDefaultAssign(rest.slice(1));
|
|
3369
|
+
}
|
|
3370
|
+
function hasDefaultAssign(annotation) {
|
|
3371
|
+
for (let i = 0; i < annotation.length; i++) {
|
|
3372
|
+
if (annotation[i] !== "=") continue;
|
|
3373
|
+
if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
|
|
3374
|
+
i++;
|
|
3375
|
+
continue;
|
|
3376
|
+
}
|
|
3377
|
+
const prev = annotation[i - 1];
|
|
3378
|
+
if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
|
|
3379
|
+
return true;
|
|
3380
|
+
}
|
|
3381
|
+
return false;
|
|
3382
|
+
}
|
|
3383
|
+
function buildAnalysisSrc(def) {
|
|
3384
|
+
return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
|
|
3385
|
+
}
|
|
3386
|
+
var RESOLVER_REASONS = {
|
|
3387
|
+
"rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
|
|
3388
|
+
"arguments": "its body references `arguments`",
|
|
3389
|
+
"reassigned": "the binding it names is reassigned, so the function read here is not decidable",
|
|
3390
|
+
"not-a-function": "the binding it names is not a function declaration",
|
|
3391
|
+
"unresolved-import": "its module could not be resolved or parsed",
|
|
3392
|
+
"not-found": "the name did not resolve to any binding",
|
|
3393
|
+
"computed": "it is a computed expression",
|
|
3394
|
+
"foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
|
|
3395
|
+
"ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
|
|
3396
|
+
};
|
|
3397
|
+
function unconfirmableReason(def, src) {
|
|
3398
|
+
const declared = def.buildArityUnresolved;
|
|
3399
|
+
if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
|
|
3400
|
+
const s = src.trim();
|
|
3401
|
+
const params = s.slice(0, s.indexOf(")") + 1);
|
|
3402
|
+
if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
|
|
3403
|
+
if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
|
|
3404
|
+
if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
|
|
3405
|
+
return "it names a binding this build could not resolve to a function declaration";
|
|
3406
|
+
}
|
|
3407
|
+
return "its parameter list could not be parsed";
|
|
3408
|
+
}
|
|
3409
|
+
function confirmedArityForDef(def) {
|
|
3410
|
+
if (def.buildArity !== void 0) return def.buildArity;
|
|
3411
|
+
const src = buildAnalysisSrc(def);
|
|
3412
|
+
const arity = confirmedBuildArity(src);
|
|
3413
|
+
if (arity !== null) return arity;
|
|
3414
|
+
const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
|
|
3415
|
+
recordDegradation({
|
|
3416
|
+
code: "build-arity-unconfirmed",
|
|
3417
|
+
severity: "warn",
|
|
3418
|
+
where: `node("${def.type ?? "<inferred>"}")`,
|
|
3419
|
+
subject,
|
|
3420
|
+
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`,
|
|
3421
|
+
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"
|
|
3422
|
+
});
|
|
3423
|
+
return null;
|
|
3424
|
+
}
|
|
3190
3425
|
function buildReadsChildren(def) {
|
|
3191
3426
|
if (!def.build) return true;
|
|
3192
|
-
const
|
|
3193
|
-
const arity = confirmedBuildArity(src);
|
|
3427
|
+
const arity = confirmedArityForDef(def);
|
|
3194
3428
|
if (arity === null) return true;
|
|
3195
3429
|
return arity >= 1;
|
|
3196
3430
|
}
|
|
3197
3431
|
function buildReadsRaw(def) {
|
|
3198
3432
|
if (!def.build) return true;
|
|
3199
|
-
const
|
|
3200
|
-
const arity = confirmedBuildArity(src);
|
|
3433
|
+
const arity = confirmedArityForDef(def);
|
|
3201
3434
|
if (arity === null) return true;
|
|
3202
3435
|
return arity >= 4;
|
|
3203
3436
|
}
|
|
3204
3437
|
function buildReadsTrivia(def) {
|
|
3205
3438
|
if (!def.build) return true;
|
|
3206
|
-
const
|
|
3207
|
-
const arity = confirmedBuildArity(src);
|
|
3439
|
+
const arity = confirmedArityForDef(def);
|
|
3208
3440
|
if (arity === null) return true;
|
|
3209
3441
|
return arity >= 5;
|
|
3210
3442
|
}
|
|
3211
3443
|
function buildReadsState(def) {
|
|
3212
3444
|
if (!def.build) return true;
|
|
3213
|
-
const
|
|
3214
|
-
const arity = confirmedBuildArity(src);
|
|
3445
|
+
const arity = confirmedArityForDef(def);
|
|
3215
3446
|
if (arity === null) return true;
|
|
3216
3447
|
return arity >= 6;
|
|
3217
3448
|
}
|
|
@@ -3219,8 +3450,7 @@ function buildReadsState(def) {
|
|
|
3219
3450
|
// src/compiler/fields.ts
|
|
3220
3451
|
function buildReadsFields(def) {
|
|
3221
3452
|
if (!def.build) return true;
|
|
3222
|
-
const
|
|
3223
|
-
const arity = confirmedBuildArity(src);
|
|
3453
|
+
const arity = confirmedArityForDef(def);
|
|
3224
3454
|
if (arity === null) return true;
|
|
3225
3455
|
return arity >= 2;
|
|
3226
3456
|
}
|
|
@@ -3401,6 +3631,13 @@ function isCstChild(value) {
|
|
|
3401
3631
|
function missingInferredType() {
|
|
3402
3632
|
throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
|
|
3403
3633
|
}
|
|
3634
|
+
function normalizeBuildArity(arity) {
|
|
3635
|
+
if (arity === void 0) return void 0;
|
|
3636
|
+
if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
|
|
3637
|
+
throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
|
|
3638
|
+
}
|
|
3639
|
+
return arity;
|
|
3640
|
+
}
|
|
3404
3641
|
function normalizeProject(project) {
|
|
3405
3642
|
if (project === void 0) return void 0;
|
|
3406
3643
|
if (!Number.isInteger(project) || project < 0) {
|
|
@@ -3436,13 +3673,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
3436
3673
|
const captureTrivia = opts?.captureTrivia === true;
|
|
3437
3674
|
const trailingTrivia = opts?.trailingTrivia === true;
|
|
3438
3675
|
const tags = opts?.tags;
|
|
3676
|
+
const buildArity = normalizeBuildArity(opts?.buildArity);
|
|
3439
3677
|
if (unwrap && collapse) {
|
|
3440
3678
|
throw new Error("node() options cannot set both unwrap and collapse");
|
|
3441
3679
|
}
|
|
3442
3680
|
if (project !== void 0 && (unwrap || collapse)) {
|
|
3443
3681
|
throw new Error("node() options cannot combine project with unwrap or collapse");
|
|
3444
3682
|
}
|
|
3445
|
-
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;
|
|
3683
|
+
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;
|
|
3446
3684
|
const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
|
|
3447
3685
|
const clonesState = build ? buildReadsState(def) : project === void 0;
|
|
3448
3686
|
const hasOwnFields = parserHasOwnFields(combinator);
|
|
@@ -3488,7 +3726,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
3488
3726
|
const cstOutput = cstOutputHost(ctx.build);
|
|
3489
3727
|
const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
|
|
3490
3728
|
const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
3491
|
-
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 };
|
|
3729
|
+
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 };
|
|
3492
3730
|
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 };
|
|
3493
3731
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
3494
3732
|
pushCstChild(ctx, built, rawEntry);
|
|
@@ -3683,7 +3921,7 @@ function leaf(root, fn) {
|
|
|
3683
3921
|
}
|
|
3684
3922
|
|
|
3685
3923
|
// src/version.ts
|
|
3686
|
-
var PARSEMAN_VERSION = "0.
|
|
3924
|
+
var PARSEMAN_VERSION = "0.46.0";
|
|
3687
3925
|
|
|
3688
3926
|
// src/cst/host-mode.ts
|
|
3689
3927
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3703,346 +3941,11 @@ function assertHostModeCompatible(mode, build, hostBranchElided = true) {
|
|
|
3703
3941
|
}
|
|
3704
3942
|
|
|
3705
3943
|
// src/analysis/gating.ts
|
|
3706
|
-
var isAny = (fs2) => fs2.kind === "any";
|
|
3707
|
-
function intersects2(a, b) {
|
|
3708
|
-
if (a.kind === "any" || b.kind === "any") return true;
|
|
3709
|
-
if (a.kind === "empty" || b.kind === "empty") return false;
|
|
3710
|
-
for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true;
|
|
3711
|
-
return false;
|
|
3712
|
-
}
|
|
3713
|
-
function intersection(a, b) {
|
|
3714
|
-
if (a.kind === "any") return b;
|
|
3715
|
-
if (b.kind === "any") return a;
|
|
3716
|
-
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
3717
|
-
const ranges = [];
|
|
3718
|
-
for (const ra of a.ranges) for (const rb of b.ranges) {
|
|
3719
|
-
const lo = Math.max(ra.lo, rb.lo);
|
|
3720
|
-
const hi = Math.min(ra.hi, rb.hi);
|
|
3721
|
-
if (lo <= hi) ranges.push({ lo, hi });
|
|
3722
|
-
}
|
|
3723
|
-
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
3724
|
-
}
|
|
3725
|
-
function combine(sets) {
|
|
3726
|
-
let acc = { kind: "empty" };
|
|
3727
|
-
for (const s of sets) {
|
|
3728
|
-
if (s.kind === "any") return { kind: "any" };
|
|
3729
|
-
if (s.kind === "empty") continue;
|
|
3730
|
-
acc = acc.kind === "ranges" ? { kind: "ranges", ranges: [...acc.ranges, ...s.ranges] } : s;
|
|
3731
|
-
}
|
|
3732
|
-
return acc;
|
|
3733
|
-
}
|
|
3734
3944
|
var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
|
|
3735
|
-
function firstSetToString(
|
|
3736
|
-
if (
|
|
3737
|
-
if (
|
|
3738
|
-
return
|
|
3739
|
-
}
|
|
3740
|
-
var ruleNameOf2 = (p) => p._ruleName;
|
|
3741
|
-
var SUGGESTIONS = {
|
|
3742
|
-
"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.",
|
|
3743
|
-
"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.",
|
|
3744
|
-
"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.",
|
|
3745
|
-
"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.",
|
|
3746
|
-
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
3747
|
-
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
3748
|
-
};
|
|
3749
|
-
function classifyBroadArm(arm, resolve2) {
|
|
3750
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3751
|
-
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
3752
|
-
const walk = (p) => {
|
|
3753
|
-
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
3754
|
-
seen.add(p);
|
|
3755
|
-
const d = p._def;
|
|
3756
|
-
switch (d.tag) {
|
|
3757
|
-
case "literal":
|
|
3758
|
-
case "regex":
|
|
3759
|
-
case "keywords":
|
|
3760
|
-
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
3761
|
-
case "not":
|
|
3762
|
-
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
3763
|
-
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
3764
|
-
// (or the body is nullable) — walk in for the real cause.
|
|
3765
|
-
case "peek":
|
|
3766
|
-
return walk(d.parser);
|
|
3767
|
-
case "scanTo":
|
|
3768
|
-
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
3769
|
-
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
3770
|
-
// its own — it is the poison. `withCtx`/`recover` FORWARD their inner
|
|
3771
|
-
// first-set (see firstSetOf), so a broad result comes from the inner parser:
|
|
3772
|
-
// walk into it for a precise cause.
|
|
3773
|
-
case "guard":
|
|
3774
|
-
return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
|
|
3775
|
-
// attempt/withCtx/recover FORWARD their inner first-set (transparent for
|
|
3776
|
-
// dispatch), so a broad result comes from the inner parser — walk into it.
|
|
3777
|
-
case "attempt":
|
|
3778
|
-
case "withCtx":
|
|
3779
|
-
case "recover":
|
|
3780
|
-
return walk(d.parser);
|
|
3781
|
-
case "lazy": {
|
|
3782
|
-
const name = p._ruleName;
|
|
3783
|
-
let target;
|
|
3784
|
-
try {
|
|
3785
|
-
target = d.thunk();
|
|
3786
|
-
} catch {
|
|
3787
|
-
const bound = name !== void 0 ? resolve2?.(name) : void 0;
|
|
3788
|
-
if (bound === void 0)
|
|
3789
|
-
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
3790
|
-
target = bound;
|
|
3791
|
-
}
|
|
3792
|
-
const inner = walk(target);
|
|
3793
|
-
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
3794
|
-
return inner;
|
|
3795
|
-
}
|
|
3796
|
-
case "optional":
|
|
3797
|
-
case "many": {
|
|
3798
|
-
const inner = walk(d.parser);
|
|
3799
|
-
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
3800
|
-
}
|
|
3801
|
-
case "oneOrMore":
|
|
3802
|
-
case "transform":
|
|
3803
|
-
case "label":
|
|
3804
|
-
case "field":
|
|
3805
|
-
case "trivia":
|
|
3806
|
-
case "token":
|
|
3807
|
-
case "leaf":
|
|
3808
|
-
case "node":
|
|
3809
|
-
case "grammar":
|
|
3810
|
-
case "expect":
|
|
3811
|
-
return walk(d.parser);
|
|
3812
|
-
case "skip":
|
|
3813
|
-
return walk(d.main);
|
|
3814
|
-
case "sequence": {
|
|
3815
|
-
let sawNullablePrefix = false;
|
|
3816
|
-
for (const t of d.parsers) {
|
|
3817
|
-
if (t._def.tag === "not") {
|
|
3818
|
-
sawNullablePrefix = true;
|
|
3819
|
-
continue;
|
|
3820
|
-
}
|
|
3821
|
-
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
|
|
3822
|
-
const inner = walk(t);
|
|
3823
|
-
if (sawNullablePrefix)
|
|
3824
|
-
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
3825
|
-
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
3826
|
-
}
|
|
3827
|
-
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve2)) {
|
|
3828
|
-
sawNullablePrefix = true;
|
|
3829
|
-
continue;
|
|
3830
|
-
}
|
|
3831
|
-
return null;
|
|
3832
|
-
}
|
|
3833
|
-
return { cause: "broad-recognizer", detail: "sequence of only nullable/zero-width terms" };
|
|
3834
|
-
}
|
|
3835
|
-
case "choice": {
|
|
3836
|
-
for (const a of d.parsers) {
|
|
3837
|
-
const r = walk(a);
|
|
3838
|
-
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
3839
|
-
}
|
|
3840
|
-
return null;
|
|
3841
|
-
}
|
|
3842
|
-
case "dispatch":
|
|
3843
|
-
return walk(d.selector);
|
|
3844
|
-
default:
|
|
3845
|
-
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
3846
|
-
}
|
|
3847
|
-
};
|
|
3848
|
-
return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
|
|
3849
|
-
}
|
|
3850
|
-
function peelToLeading(arm) {
|
|
3851
|
-
let d = arm._def;
|
|
3852
|
-
let cur = arm;
|
|
3853
|
-
for (; ; ) {
|
|
3854
|
-
if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label" || d.tag === "attempt") {
|
|
3855
|
-
cur = d.parser;
|
|
3856
|
-
d = cur._def;
|
|
3857
|
-
continue;
|
|
3858
|
-
}
|
|
3859
|
-
break;
|
|
3860
|
-
}
|
|
3861
|
-
if (d.tag === "sequence" && d.parsers.length >= 1) return d.parsers[0];
|
|
3862
|
-
return cur;
|
|
3863
|
-
}
|
|
3864
|
-
var KEYWORD_REGEX_RE = /^\^?[@#.-]?[A-Za-z][\w-]*(\(\?![^)]*\))?\$?$/;
|
|
3865
|
-
function detectAntiPatterns(rule, arms) {
|
|
3866
|
-
const out = [];
|
|
3867
|
-
arms.forEach((arm, i) => {
|
|
3868
|
-
const lead = peelToLeading(arm);
|
|
3869
|
-
const ld = lead._def;
|
|
3870
|
-
if (ld.tag === "not") {
|
|
3871
|
-
const inner = ld.parser;
|
|
3872
|
-
if (inner._def.tag === "not") {
|
|
3873
|
-
out.push({
|
|
3874
|
-
kind: "double-not",
|
|
3875
|
-
rule,
|
|
3876
|
-
armIndex: i,
|
|
3877
|
-
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."
|
|
3878
|
-
});
|
|
3879
|
-
} else {
|
|
3880
|
-
out.push({
|
|
3881
|
-
kind: "leading-not",
|
|
3882
|
-
rule,
|
|
3883
|
-
armIndex: i,
|
|
3884
|
-
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."
|
|
3885
|
-
});
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
if (ld.tag === "regex" && KEYWORD_REGEX_RE.test(ld.source)) {
|
|
3889
|
-
out.push({
|
|
3890
|
-
kind: "keyword-regex",
|
|
3891
|
-
rule,
|
|
3892
|
-
armIndex: i,
|
|
3893
|
-
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).`
|
|
3894
|
-
});
|
|
3895
|
-
}
|
|
3896
|
-
});
|
|
3897
|
-
return out;
|
|
3898
|
-
}
|
|
3899
|
-
function analyzeGating(entry, opts) {
|
|
3900
|
-
return analyzeGatingRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3901
|
-
}
|
|
3902
|
-
function analyzeGatingRules(ruleMap, opts) {
|
|
3903
|
-
const raw = [];
|
|
3904
|
-
const antiPatterns = [];
|
|
3905
|
-
const unanalysable = [];
|
|
3906
|
-
const seenUnanalysable = /* @__PURE__ */ new Set();
|
|
3907
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3908
|
-
const noteUnanalysable = (u) => {
|
|
3909
|
-
const key = `${u.rule}\0${u.kind}`;
|
|
3910
|
-
if (seenUnanalysable.has(key)) return;
|
|
3911
|
-
seenUnanalysable.add(key);
|
|
3912
|
-
unanalysable.push(u);
|
|
3913
|
-
};
|
|
3914
|
-
const visit = (p, enclosingRule) => {
|
|
3915
|
-
if (visited.has(p)) return;
|
|
3916
|
-
visited.add(p);
|
|
3917
|
-
const def = p?._def;
|
|
3918
|
-
if (def === null || typeof def !== "object") {
|
|
3919
|
-
noteUnanalysable(
|
|
3920
|
-
typeof p === "function" ? {
|
|
3921
|
-
rule: enclosingRule,
|
|
3922
|
-
kind: "fused-rule",
|
|
3923
|
-
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."
|
|
3924
|
-
} : {
|
|
3925
|
-
rule: enclosingRule,
|
|
3926
|
-
kind: "not-a-combinator",
|
|
3927
|
-
reason: `value of type ${p === null ? "null" : typeof p} is not a combinator and carries no _def descriptor.`
|
|
3928
|
-
}
|
|
3929
|
-
);
|
|
3930
|
-
return;
|
|
3931
|
-
}
|
|
3932
|
-
const d = def;
|
|
3933
|
-
const rule = ruleNameOf2(p) ?? enclosingRule;
|
|
3934
|
-
if (d.tag === "choice") {
|
|
3935
|
-
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
3936
|
-
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
3937
|
-
}
|
|
3938
|
-
const rec = d;
|
|
3939
|
-
const kids = [];
|
|
3940
|
-
if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
|
|
3941
|
-
for (const k of ["parser", "main", "skipped", "separator", "sentinel"])
|
|
3942
|
-
if (rec[k]) kids.push(rec[k]);
|
|
3943
|
-
if (d.tag === "dispatch") {
|
|
3944
|
-
kids.push(d.selector);
|
|
3945
|
-
for (const c of d.cases) kids.push(c.parser);
|
|
3946
|
-
if (d.matchers) for (const c of d.matchers) kids.push(c.parser);
|
|
3947
|
-
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3948
|
-
}
|
|
3949
|
-
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
3950
|
-
if (d.tag === "lazy") {
|
|
3951
|
-
try {
|
|
3952
|
-
kids.push(d.thunk());
|
|
3953
|
-
} catch {
|
|
3954
|
-
}
|
|
3955
|
-
}
|
|
3956
|
-
for (const k of kids) visit(k, rule);
|
|
3957
|
-
};
|
|
3958
|
-
for (const [name, root] of ruleMap) visit(root, ruleNameOf2(root) ?? name);
|
|
3959
|
-
const perRule = /* @__PURE__ */ new Map();
|
|
3960
|
-
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3961
|
-
const seenInRule = /* @__PURE__ */ new Map();
|
|
3962
|
-
const accept = new Set(opts?.accept ?? []);
|
|
3963
|
-
const usedAccept = /* @__PURE__ */ new Set();
|
|
3964
|
-
const choices = raw.map(({ g, rule }) => {
|
|
3965
|
-
const n = seenInRule.get(rule) ?? 0;
|
|
3966
|
-
seenInRule.set(rule, n + 1);
|
|
3967
|
-
const id = (perRule.get(rule) ?? 1) === 1 ? rule : `${rule}#${n}`;
|
|
3968
|
-
const isAccepted = g.gates === "no" && accept.has(id);
|
|
3969
|
-
if (isAccepted) usedAccept.add(id);
|
|
3970
|
-
return { ...g, id, accepted: isAccepted };
|
|
3971
|
-
});
|
|
3972
|
-
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
3973
|
-
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
3974
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
3975
|
-
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
3976
|
-
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
3977
|
-
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
3978
|
-
return {
|
|
3979
|
-
totalChoices: choices.length,
|
|
3980
|
-
gated,
|
|
3981
|
-
recoverable,
|
|
3982
|
-
unanalysable,
|
|
3983
|
-
ungated,
|
|
3984
|
-
accepted,
|
|
3985
|
-
deferred,
|
|
3986
|
-
acceptedUnused,
|
|
3987
|
-
choices,
|
|
3988
|
-
antiPatterns
|
|
3989
|
-
};
|
|
3990
|
-
}
|
|
3991
|
-
function analyzeChoice(p, d, rule, resolve2) {
|
|
3992
|
-
const arms = d.parsers;
|
|
3993
|
-
const shallow = arms.map((a) => a._meta.firstSet);
|
|
3994
|
-
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve2));
|
|
3995
|
-
const anyArms = deep.map((fs2, index) => ({ fs: fs2, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
3996
|
-
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve2);
|
|
3997
|
-
return { index, cause, detail, shallowAnyOnly: false, unresolvedExternal: unresolvedExternal === true, suggestion: SUGGESTIONS[cause] };
|
|
3998
|
-
});
|
|
3999
|
-
const overlaps = [];
|
|
4000
|
-
for (let i = 0; i < deep.length; i++)
|
|
4001
|
-
for (let j = i + 1; j < deep.length; j++)
|
|
4002
|
-
if (!isAny(deep[i]) && !isAny(deep[j]) && intersects2(deep[i], deep[j]))
|
|
4003
|
-
overlaps.push({
|
|
4004
|
-
a: i,
|
|
4005
|
-
b: j,
|
|
4006
|
-
on: intersection(deep[i], deep[j]),
|
|
4007
|
-
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."
|
|
4008
|
-
});
|
|
4009
|
-
let gates;
|
|
4010
|
-
if (d.disjoint) gates = "yes";
|
|
4011
|
-
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
4012
|
-
else gates = "no";
|
|
4013
|
-
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
4014
|
-
return {
|
|
4015
|
-
rule,
|
|
4016
|
-
strategy: d.strategy?.tag ?? "firstMatch",
|
|
4017
|
-
gates,
|
|
4018
|
-
deferred,
|
|
4019
|
-
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
4020
|
-
anyArms,
|
|
4021
|
-
overlaps
|
|
4022
|
-
};
|
|
4023
|
-
}
|
|
4024
|
-
function formatGatingWarnings(report) {
|
|
4025
|
-
const lines = [];
|
|
4026
|
-
if (report.unanalysable.length > 0) {
|
|
4027
|
-
lines.push(
|
|
4028
|
-
`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.`
|
|
4029
|
-
);
|
|
4030
|
-
for (const u of report.unanalysable)
|
|
4031
|
-
lines.push(` \xB7 ${u.rule} [${u.kind}]: ${u.reason}`);
|
|
4032
|
-
}
|
|
4033
|
-
for (const c of report.ungated) {
|
|
4034
|
-
lines.push(`parseman gating: choice @ ${c.id} is UNGATED [${c.strategy}] \u2014 no first-char dispatch; every position speculatively enters doomed arms.`);
|
|
4035
|
-
for (const a of c.anyArms)
|
|
4036
|
-
lines.push(` \xB7 arm[${a.index}] first-set ANY (${a.cause}): ${a.detail}
|
|
4037
|
-
fix: ${a.suggestion}`);
|
|
4038
|
-
for (const o of c.overlaps)
|
|
4039
|
-
lines.push(` \xB7 arm[${o.a}] \u2229 arm[${o.b}] overlap on ${firstSetToString(o.on)}
|
|
4040
|
-
fix: ${o.suggestion}`);
|
|
4041
|
-
lines.push(` (intentional? accept it in the gating snapshot: { accept: ['${c.id}'] }.)`);
|
|
4042
|
-
}
|
|
4043
|
-
for (const ap of report.antiPatterns)
|
|
4044
|
-
lines.push(`parseman anti-pattern [${ap.kind}] @ ${ap.rule} arm[${ap.armIndex}]: ${ap.message}`);
|
|
4045
|
-
return lines;
|
|
3945
|
+
function firstSetToString(fs3) {
|
|
3946
|
+
if (fs3.kind === "any") return "ANY";
|
|
3947
|
+
if (fs3.kind === "empty") return "(empty)";
|
|
3948
|
+
return fs3.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
|
|
4046
3949
|
}
|
|
4047
3950
|
|
|
4048
3951
|
// src/analysis/duplication.ts
|
|
@@ -4085,7 +3988,7 @@ function childrenOf2(d) {
|
|
|
4085
3988
|
}
|
|
4086
3989
|
}
|
|
4087
3990
|
}
|
|
4088
|
-
var
|
|
3991
|
+
var ruleNameOf2 = (p) => p._ruleName;
|
|
4089
3992
|
var fnKey = (src, fn) => src ?? (typeof fn === "function" ? String(fn) : "");
|
|
4090
3993
|
function payloadKey(p, d) {
|
|
4091
3994
|
switch (d.tag) {
|
|
@@ -4096,7 +3999,7 @@ function payloadKey(p, d) {
|
|
|
4096
3999
|
case "keywords":
|
|
4097
4000
|
return `keywords\0${[...d.words].join("")}\0${d.caseInsensitive}\0${d.boundary ?? ""}`;
|
|
4098
4001
|
case "lazy": {
|
|
4099
|
-
const n =
|
|
4002
|
+
const n = ruleNameOf2(p);
|
|
4100
4003
|
return n === void 0 ? `ref\0#${anonId(p)}` : `ref\0${n}`;
|
|
4101
4004
|
}
|
|
4102
4005
|
case "label":
|
|
@@ -4165,7 +4068,7 @@ var Interner = class {
|
|
|
4165
4068
|
var MAX_RENDER = 160;
|
|
4166
4069
|
function render(p, depth = 3) {
|
|
4167
4070
|
const d = p._def;
|
|
4168
|
-
const name =
|
|
4071
|
+
const name = ruleNameOf2(p);
|
|
4169
4072
|
switch (d.tag) {
|
|
4170
4073
|
case "literal":
|
|
4171
4074
|
return `literal('${d.value}')`;
|
|
@@ -4353,7 +4256,7 @@ function keywordAlternationHazards(words, boundary) {
|
|
|
4353
4256
|
return out;
|
|
4354
4257
|
}
|
|
4355
4258
|
function analyzeDuplication(entry, opts) {
|
|
4356
|
-
return analyzeDuplicationRules([[
|
|
4259
|
+
return analyzeDuplicationRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
4357
4260
|
}
|
|
4358
4261
|
function assertAnalyzable(ruleMap) {
|
|
4359
4262
|
for (const [name, rule] of ruleMap) {
|
|
@@ -4391,11 +4294,11 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
4391
4294
|
return n;
|
|
4392
4295
|
};
|
|
4393
4296
|
const visited = /* @__PURE__ */ new Map();
|
|
4394
|
-
const visit = (p, rule,
|
|
4297
|
+
const visit = (p, rule, path3, enclosingNode, enclosingReducer) => {
|
|
4395
4298
|
if (visited.has(p)) return;
|
|
4396
4299
|
const d = p._def;
|
|
4397
|
-
const own =
|
|
4398
|
-
const here = own !== void 0 && own !== rule ? { rule: own, path: "" } : { rule, path:
|
|
4300
|
+
const own = ruleNameOf2(p);
|
|
4301
|
+
const here = own !== void 0 && own !== rule ? { rule: own, path: "" } : { rule, path: path3 };
|
|
4399
4302
|
visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
|
|
4400
4303
|
const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
|
|
4401
4304
|
const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
|
|
@@ -4407,12 +4310,12 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
4407
4310
|
if (d.tag === "lazy") {
|
|
4408
4311
|
try {
|
|
4409
4312
|
const target = d.thunk();
|
|
4410
|
-
visit(target,
|
|
4313
|
+
visit(target, ruleNameOf2(target) ?? ruleNameOf2(p) ?? here.rule, "", null, false);
|
|
4411
4314
|
} catch {
|
|
4412
4315
|
}
|
|
4413
4316
|
}
|
|
4414
4317
|
};
|
|
4415
|
-
for (const [name, root] of ruleMap) visit(root,
|
|
4318
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf2(root) ?? name, "", null, false);
|
|
4416
4319
|
const accept = new Set(opts?.accept ?? []);
|
|
4417
4320
|
const used = /* @__PURE__ */ new Set();
|
|
4418
4321
|
const keep = (f) => {
|
|
@@ -4429,10 +4332,10 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
4429
4332
|
const divergentNodes = findDivergentNodes(visited, shapeOf);
|
|
4430
4333
|
const structureLoss = findStructureLoss(visited, opts?.resolveRef);
|
|
4431
4334
|
const keywordRegexes = findKeywordRegexes(visited);
|
|
4432
|
-
const subsume = (
|
|
4335
|
+
const subsume = (fs3) => {
|
|
4433
4336
|
const covered = /* @__PURE__ */ new Set();
|
|
4434
4337
|
const surviving = /* @__PURE__ */ new Set();
|
|
4435
|
-
for (const f of [...
|
|
4338
|
+
for (const f of [...fs3].sort((a, b) => b.savings - a.savings)) {
|
|
4436
4339
|
if (f._members.every((m) => covered.has(m))) continue;
|
|
4437
4340
|
surviving.add(f.id);
|
|
4438
4341
|
for (const m of f._members) markSubtree(m, covered);
|
|
@@ -4555,10 +4458,10 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
|
|
|
4555
4458
|
return out.sort((a, b) => b.savings - a.savings);
|
|
4556
4459
|
}
|
|
4557
4460
|
function findDivergentSlot(members, shapeOf) {
|
|
4558
|
-
const
|
|
4461
|
+
const path3 = [];
|
|
4559
4462
|
const defs = [];
|
|
4560
4463
|
let cur = [...members];
|
|
4561
|
-
for (let
|
|
4464
|
+
for (let guard = 0; guard < 64; guard++) {
|
|
4562
4465
|
const d0 = cur[0]._def;
|
|
4563
4466
|
const kidLists = cur.map((c) => childrenOf2(c._def));
|
|
4564
4467
|
const arity = kidLists[0].length;
|
|
@@ -4570,14 +4473,14 @@ function findDivergentSlot(members, shapeOf) {
|
|
|
4570
4473
|
}
|
|
4571
4474
|
if (diff.length !== 1) break;
|
|
4572
4475
|
const i = diff[0];
|
|
4573
|
-
|
|
4476
|
+
path3.push(i);
|
|
4574
4477
|
defs.push(d0);
|
|
4575
4478
|
cur = kidLists.map((k) => k[i]);
|
|
4576
4479
|
const nextArity = childrenOf2(cur[0]._def).length;
|
|
4577
4480
|
const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
|
|
4578
4481
|
if (!samePayload || nextArity === 0) break;
|
|
4579
4482
|
}
|
|
4580
|
-
if (
|
|
4483
|
+
if (path3.length === 0) return null;
|
|
4581
4484
|
let slotSize = 0;
|
|
4582
4485
|
const sizeOf = (p) => {
|
|
4583
4486
|
let n = 1;
|
|
@@ -4585,14 +4488,14 @@ function findDivergentSlot(members, shapeOf) {
|
|
|
4585
4488
|
return n;
|
|
4586
4489
|
};
|
|
4587
4490
|
for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
|
|
4588
|
-
return { path:
|
|
4491
|
+
return { path: path3, defs, variants: cur, slotSize };
|
|
4589
4492
|
}
|
|
4590
|
-
function renderWithHole(p,
|
|
4591
|
-
if (depth ===
|
|
4493
|
+
function renderWithHole(p, path3, depth = 0) {
|
|
4494
|
+
if (depth === path3.length) return "\u2039slot\u203A";
|
|
4592
4495
|
const d = p._def;
|
|
4593
4496
|
const kids = childrenOf2(d);
|
|
4594
|
-
const idx =
|
|
4595
|
-
const parts = kids.map((k, i) => i === idx ? renderWithHole(k,
|
|
4497
|
+
const idx = path3[depth];
|
|
4498
|
+
const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path3, depth + 1) : render(k, 2));
|
|
4596
4499
|
switch (d.tag) {
|
|
4597
4500
|
case "sequence":
|
|
4598
4501
|
return `sequence(${parts.join(", ")})`;
|
|
@@ -4796,13 +4699,13 @@ function findRegexClasses(visited) {
|
|
|
4796
4699
|
}
|
|
4797
4700
|
return out.sort((a, b) => Number(b.drifted) - Number(a.drifted) || b.count - a.count);
|
|
4798
4701
|
}
|
|
4799
|
-
function
|
|
4702
|
+
function intersects2(a, b) {
|
|
4800
4703
|
if (a.kind === "any" || b.kind === "any") return true;
|
|
4801
4704
|
if (a.kind === "empty" || b.kind === "empty") return false;
|
|
4802
4705
|
for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true;
|
|
4803
4706
|
return false;
|
|
4804
4707
|
}
|
|
4805
|
-
function
|
|
4708
|
+
function intersection(a, b) {
|
|
4806
4709
|
if (a.kind === "any") return b;
|
|
4807
4710
|
if (b.kind === "any") return a;
|
|
4808
4711
|
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
@@ -4831,19 +4734,19 @@ function leadingRegex(arm) {
|
|
|
4831
4734
|
const t = termsOf(arm)[0];
|
|
4832
4735
|
return t !== void 0 && t._def.tag === "regex";
|
|
4833
4736
|
}
|
|
4834
|
-
function findOverlaps(visited, shapeOf,
|
|
4737
|
+
function findOverlaps(visited, shapeOf, resolve3) {
|
|
4835
4738
|
const out = [];
|
|
4836
4739
|
for (const v2 of visited.values()) {
|
|
4837
4740
|
const d = v2.d;
|
|
4838
4741
|
if (d.tag !== "choice") continue;
|
|
4839
4742
|
const arms = d.parsers;
|
|
4840
|
-
const
|
|
4743
|
+
const fs3 = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve3));
|
|
4841
4744
|
const strategy = d.strategy;
|
|
4842
4745
|
for (let i = 0; i < arms.length; i++) {
|
|
4843
4746
|
for (let j = i + 1; j < arms.length; j++) {
|
|
4844
|
-
const A =
|
|
4747
|
+
const A = fs3[i], B = fs3[j];
|
|
4845
4748
|
if (A.kind === "any" || B.kind === "any") continue;
|
|
4846
|
-
if (!
|
|
4749
|
+
if (!intersects2(A, B)) continue;
|
|
4847
4750
|
const ta = termsOf(arms[i]), tb = termsOf(arms[j]);
|
|
4848
4751
|
let shared = 0;
|
|
4849
4752
|
while (shared < ta.length && shared < tb.length && shapeOf(ta[shared]) === shapeOf(tb[shared])) shared++;
|
|
@@ -4855,7 +4758,7 @@ function findOverlaps(visited, shapeOf, resolve2) {
|
|
|
4855
4758
|
site: v2.site,
|
|
4856
4759
|
a: i,
|
|
4857
4760
|
b: j,
|
|
4858
|
-
on:
|
|
4761
|
+
on: intersection(A, B),
|
|
4859
4762
|
sharedLeadingTerms: shared,
|
|
4860
4763
|
sharedPrefix: shared > 0 ? clamp(ta.slice(0, shared).map((t) => render(t, 2)).join(", ")) : null,
|
|
4861
4764
|
handledByStrategy: handled,
|
|
@@ -5157,7 +5060,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
|
|
|
5157
5060
|
for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
|
|
5158
5061
|
}
|
|
5159
5062
|
var MAX_LOST_TYPES = 8;
|
|
5160
|
-
function findStructureLoss(visited,
|
|
5063
|
+
function findStructureLoss(visited, resolve3) {
|
|
5161
5064
|
const nestedCache = /* @__PURE__ */ new Map();
|
|
5162
5065
|
const nested = (p) => {
|
|
5163
5066
|
const hit = nestedCache.get(p);
|
|
@@ -5174,18 +5077,18 @@ function findStructureLoss(visited, resolve2) {
|
|
|
5174
5077
|
const arms = d.parsers;
|
|
5175
5078
|
const resolved = arms.map(resolveArm);
|
|
5176
5079
|
const nodeTypes = resolved.map(wrappingNodeType);
|
|
5177
|
-
const
|
|
5080
|
+
const fs3 = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve3));
|
|
5178
5081
|
for (let i = 0; i < arms.length; i++) {
|
|
5179
5082
|
if (d.gates[i] != null) continue;
|
|
5180
5083
|
const type = nodeTypes[i] ?? null;
|
|
5181
5084
|
if (type === null) continue;
|
|
5182
5085
|
if (nested(resolved[i]).size !== 0) continue;
|
|
5183
|
-
const A =
|
|
5086
|
+
const A = fs3[i];
|
|
5184
5087
|
if (A.kind === "any") continue;
|
|
5185
5088
|
for (let j = i + 1; j < arms.length; j++) {
|
|
5186
5089
|
if (nodeTypes[j] !== type) continue;
|
|
5187
|
-
const B =
|
|
5188
|
-
if (B.kind === "any" || !
|
|
5090
|
+
const B = fs3[j];
|
|
5091
|
+
if (B.kind === "any" || !intersects2(A, B)) continue;
|
|
5189
5092
|
const lost = nested(resolved[j]);
|
|
5190
5093
|
if (lost.size === 0) continue;
|
|
5191
5094
|
const lostNodeTypes = [...lost].sort();
|
|
@@ -5196,7 +5099,7 @@ function findStructureLoss(visited, resolve2) {
|
|
|
5196
5099
|
nodeType: type,
|
|
5197
5100
|
earlier: i,
|
|
5198
5101
|
later: j,
|
|
5199
|
-
on:
|
|
5102
|
+
on: intersection(A, B),
|
|
5200
5103
|
lostNodeTypes: lostNodeTypes.slice(0, MAX_LOST_TYPES),
|
|
5201
5104
|
earlierShape: clamp(render(resolved[i], 3)),
|
|
5202
5105
|
laterShape: clamp(render(resolved[j], 3)),
|
|
@@ -5454,11 +5357,11 @@ function buildBalancedInterior(open, close, skips) {
|
|
|
5454
5357
|
return combi;
|
|
5455
5358
|
}
|
|
5456
5359
|
function firstSetClassChars(c) {
|
|
5457
|
-
const
|
|
5458
|
-
if (
|
|
5459
|
-
if (
|
|
5360
|
+
const fs3 = c._meta.firstSet;
|
|
5361
|
+
if (fs3.kind === "empty") return [];
|
|
5362
|
+
if (fs3.kind !== "ranges") return null;
|
|
5460
5363
|
const out = [];
|
|
5461
|
-
for (const { lo, hi } of
|
|
5364
|
+
for (const { lo, hi } of fs3.ranges) {
|
|
5462
5365
|
if (hi - lo > 8) return null;
|
|
5463
5366
|
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
5464
5367
|
}
|
|
@@ -5472,13 +5375,13 @@ function escapeClassChar(ch2) {
|
|
|
5472
5375
|
function idPart(value) {
|
|
5473
5376
|
return encodeURIComponent(value);
|
|
5474
5377
|
}
|
|
5475
|
-
function dispatchArmIds(def,
|
|
5378
|
+
function dispatchArmIds(def, path3) {
|
|
5476
5379
|
const ids = [];
|
|
5477
|
-
for (const entry of def.cases) ids.push(`dispatch:${
|
|
5380
|
+
for (const entry of def.cases) ids.push(`dispatch:${path3}/when:${entry.keys.map(idPart).join("|")}`);
|
|
5478
5381
|
for (const entry of def.matchers ?? []) {
|
|
5479
|
-
ids.push(`dispatch:${
|
|
5382
|
+
ids.push(`dispatch:${path3}/matcher:${entry.kind}:${idPart(entry.value)}${entry.kind === "matches" && entry.flags ? `:${idPart(entry.flags)}` : ""}`);
|
|
5480
5383
|
}
|
|
5481
|
-
if (def.otherwise) ids.push(`dispatch:${
|
|
5384
|
+
if (def.otherwise) ids.push(`dispatch:${path3}/otherwise`);
|
|
5482
5385
|
return ids;
|
|
5483
5386
|
}
|
|
5484
5387
|
function children(def, winners) {
|
|
@@ -5517,7 +5420,15 @@ function children(def, winners) {
|
|
|
5517
5420
|
let resolved;
|
|
5518
5421
|
try {
|
|
5519
5422
|
resolved = def.thunk();
|
|
5520
|
-
} catch {
|
|
5423
|
+
} catch (e) {
|
|
5424
|
+
recordDegradation({
|
|
5425
|
+
code: "coverage-definitions-unavailable",
|
|
5426
|
+
severity: "warn",
|
|
5427
|
+
where: "<lazy rule reference>",
|
|
5428
|
+
subject: `unresolvable reference (${e.message})`,
|
|
5429
|
+
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",
|
|
5430
|
+
otherwise: "the referenced rule and its whole subtree would contribute definitions"
|
|
5431
|
+
});
|
|
5521
5432
|
return [];
|
|
5522
5433
|
}
|
|
5523
5434
|
const name = resolved._ruleName;
|
|
@@ -5547,7 +5458,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5547
5458
|
}
|
|
5548
5459
|
}
|
|
5549
5460
|
const seen = /* @__PURE__ */ new Set();
|
|
5550
|
-
const visit = (parser2,
|
|
5461
|
+
const visit = (parser2, path3) => {
|
|
5551
5462
|
if (seen.has(parser2)) return;
|
|
5552
5463
|
seen.add(parser2);
|
|
5553
5464
|
const rule = winnerNames.get(parser2) ?? parser2._ruleName;
|
|
@@ -5557,22 +5468,22 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5557
5468
|
rules2.set(parser2, id);
|
|
5558
5469
|
}
|
|
5559
5470
|
if (parser2._def.tag === "choice") {
|
|
5560
|
-
const ids = parser2._def.parsers.map((_, index) => `choice:${
|
|
5471
|
+
const ids = parser2._def.parsers.map((_, index) => `choice:${path3}/arm:${index}`);
|
|
5561
5472
|
choices.set(parser2, ids);
|
|
5562
5473
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
5563
5474
|
}
|
|
5564
5475
|
if (parser2._def.tag === "dispatch") {
|
|
5565
|
-
const ids = dispatchArmIds(parser2._def,
|
|
5476
|
+
const ids = dispatchArmIds(parser2._def, path3);
|
|
5566
5477
|
dispatches.set(parser2, ids);
|
|
5567
5478
|
ids.forEach((id) => definitions.set(id, { id, kind: "dispatch-arm" }));
|
|
5568
5479
|
}
|
|
5569
|
-
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${
|
|
5480
|
+
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path3}`);
|
|
5570
5481
|
if (parser2._def.tag === "label") {
|
|
5571
|
-
const id = `label:${
|
|
5482
|
+
const id = `label:${path3}`;
|
|
5572
5483
|
labels.set(parser2, [id]);
|
|
5573
5484
|
definitions.set(id, { id, kind: "label" });
|
|
5574
5485
|
}
|
|
5575
|
-
children(parser2._def, winners).forEach((child, index) => visit(child, `${
|
|
5486
|
+
children(parser2._def, winners).forEach((child, index) => visit(child, `${path3}/${parser2._def.tag}:${index}`));
|
|
5576
5487
|
};
|
|
5577
5488
|
const roots = Array.isArray(entry) ? entry : [entry];
|
|
5578
5489
|
for (const root of roots) {
|
|
@@ -5630,15 +5541,15 @@ function parseStringShape(source) {
|
|
|
5630
5541
|
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
5631
5542
|
const inner = source.slice(1, -1);
|
|
5632
5543
|
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
5633
|
-
const
|
|
5544
|
+
const ESC2 = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
5634
5545
|
let body;
|
|
5635
5546
|
let esc;
|
|
5636
|
-
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${
|
|
5547
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC2}\\)\\*$`).exec(inner);
|
|
5637
5548
|
if (m) {
|
|
5638
5549
|
body = m[1];
|
|
5639
5550
|
esc = m[2];
|
|
5640
5551
|
} else {
|
|
5641
|
-
m = new RegExp(`^\\((?:\\?:)?${
|
|
5552
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC2}\\|${CLS}\\)\\*$`).exec(inner);
|
|
5642
5553
|
if (m) {
|
|
5643
5554
|
esc = m[1];
|
|
5644
5555
|
body = m[2];
|
|
@@ -6344,10 +6255,10 @@ var ALT_SWITCH_MIN_CASES = 3;
|
|
|
6344
6255
|
function planAltSwitch(firsts) {
|
|
6345
6256
|
const cases = [];
|
|
6346
6257
|
let total = 0;
|
|
6347
|
-
for (const
|
|
6348
|
-
if (!
|
|
6258
|
+
for (const fs3 of firsts) {
|
|
6259
|
+
if (!fs3 || fs3.negated) return { kind: "if" };
|
|
6349
6260
|
const pts = [];
|
|
6350
|
-
for (const [lo, hi] of
|
|
6261
|
+
for (const [lo, hi] of fs3.ranges) {
|
|
6351
6262
|
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
6352
6263
|
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
6353
6264
|
}
|
|
@@ -6422,8 +6333,8 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
6422
6333
|
} else {
|
|
6423
6334
|
const c0 = codeAt(start, firstChar);
|
|
6424
6335
|
shape.arms.forEach((arm, k) => {
|
|
6425
|
-
const
|
|
6426
|
-
const cond =
|
|
6336
|
+
const fs3 = shape.firsts[k];
|
|
6337
|
+
const cond = fs3.negated ? `!(${classCond(c0, fs3.ranges)})` : `(${classCond(c0, fs3.ranges)})`;
|
|
6427
6338
|
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
6428
6339
|
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
6429
6340
|
lines.push(...m.setup);
|
|
@@ -6840,27 +6751,53 @@ function emitScannableTerminal(shape, c) {
|
|
|
6840
6751
|
}
|
|
6841
6752
|
|
|
6842
6753
|
// src/compiler/inline-build.ts
|
|
6843
|
-
var
|
|
6754
|
+
var ANN = String.raw`\s*\??\s*(?::[^,)=]+)?`;
|
|
6755
|
+
var ANY_P = String.raw`\w+${ANN}`;
|
|
6756
|
+
var MK_BUILD_RE = new RegExp(
|
|
6757
|
+
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*$`
|
|
6758
|
+
);
|
|
6844
6759
|
function escapeRegExp(s) {
|
|
6845
6760
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6846
6761
|
}
|
|
6762
|
+
function identMkBuildRe(typeIdent) {
|
|
6763
|
+
return new RegExp(
|
|
6764
|
+
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*$`
|
|
6765
|
+
);
|
|
6766
|
+
}
|
|
6847
6767
|
function looseMkBuildRe(type) {
|
|
6848
6768
|
return new RegExp(
|
|
6849
|
-
String.raw`^\(\s*(\w+)\s*,\s
|
|
6769
|
+
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*$`
|
|
6850
6770
|
);
|
|
6851
6771
|
}
|
|
6772
|
+
var LOOKS_LIKE_MK_RE = /=>\s*(?:[\w$.]*\.)?mk\s*\(/;
|
|
6852
6773
|
function analyzeMkInlineBuild(def) {
|
|
6853
6774
|
if (!def.build) return null;
|
|
6854
6775
|
if (def.type === void 0) return null;
|
|
6855
|
-
const src = (def
|
|
6776
|
+
const src = buildAnalysisSrc(def).trim();
|
|
6856
6777
|
const strict = src.match(MK_BUILD_RE);
|
|
6857
6778
|
if (strict) {
|
|
6858
6779
|
const mkType = strict[6];
|
|
6859
|
-
|
|
6780
|
+
if (mkType === def.type) return def.type;
|
|
6781
|
+
reportMkMiss(def.type, src, `it builds a "${mkType}" node, not "${def.type}"`);
|
|
6782
|
+
return null;
|
|
6860
6783
|
}
|
|
6861
6784
|
if (looseMkBuildRe(def.type).test(src)) return def.type;
|
|
6785
|
+
if (def.typeSrc !== void 0 && identMkBuildRe(def.typeSrc).test(src)) return def.type;
|
|
6786
|
+
if (LOOKS_LIKE_MK_RE.test(src)) {
|
|
6787
|
+
reportMkMiss(def.type, src, "its parameter list or argument order does not match `(children, fields, span, rawChildren, triviaLog) => mk(type, children, rawChildren, span, triviaLog)`");
|
|
6788
|
+
}
|
|
6862
6789
|
return null;
|
|
6863
6790
|
}
|
|
6791
|
+
function reportMkMiss(type, src, why) {
|
|
6792
|
+
recordDegradation({
|
|
6793
|
+
code: "mk-inline-missed",
|
|
6794
|
+
severity: "warn",
|
|
6795
|
+
where: `node("${type}")`,
|
|
6796
|
+
subject: `build reducer \`${src.split("\n")[0].slice(0, 80)}\``,
|
|
6797
|
+
fellBackTo: `looks like an inline-\`mk\` builder but ${why}, so every match pays a \`_build[n](...)\` call instead of an inlined object literal`,
|
|
6798
|
+
otherwise: "the node object would be constructed at the call site with no call frame"
|
|
6799
|
+
});
|
|
6800
|
+
}
|
|
6864
6801
|
function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
|
|
6865
6802
|
return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
|
|
6866
6803
|
}
|
|
@@ -7043,7 +6980,30 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
7043
6980
|
seen.delete(p);
|
|
7044
6981
|
}
|
|
7045
6982
|
}
|
|
6983
|
+
function tern(cond, whenTrue, whenFalse) {
|
|
6984
|
+
if (cond === "false") return whenFalse;
|
|
6985
|
+
if (cond === "true") return whenTrue;
|
|
6986
|
+
return `${cond} ? ${whenTrue} : ${whenFalse}`;
|
|
6987
|
+
}
|
|
6988
|
+
function notGate(cond) {
|
|
6989
|
+
if (cond === "false") return "true";
|
|
6990
|
+
if (cond === "true") return "false";
|
|
6991
|
+
return `!(${cond})`;
|
|
6992
|
+
}
|
|
6993
|
+
function orGate(a, b) {
|
|
6994
|
+
if (a === "false") return b;
|
|
6995
|
+
if (b === "false") return a;
|
|
6996
|
+
if (a === "true" || b === "true") return "true";
|
|
6997
|
+
return `${a} || ${b}`;
|
|
6998
|
+
}
|
|
6999
|
+
function andGate(a, b) {
|
|
7000
|
+
if (a === "false" || b === "false") return "false";
|
|
7001
|
+
if (a === "true") return b;
|
|
7002
|
+
if (b === "true") return a;
|
|
7003
|
+
return `${a} && ${b}`;
|
|
7004
|
+
}
|
|
7046
7005
|
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 }";
|
|
7006
|
+
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 } }";
|
|
7047
7007
|
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 }";
|
|
7048
7008
|
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] } }";
|
|
7049
7009
|
var _loweringSink = null;
|
|
@@ -7055,6 +7015,27 @@ function endLoweringCapture() {
|
|
|
7055
7015
|
_loweringSink = null;
|
|
7056
7016
|
return misses;
|
|
7057
7017
|
}
|
|
7018
|
+
var _inlineCapSink = null;
|
|
7019
|
+
function beginInlineCapCapture() {
|
|
7020
|
+
_inlineCapSink = [];
|
|
7021
|
+
}
|
|
7022
|
+
function endInlineCapCapture() {
|
|
7023
|
+
const sites = _inlineCapSink ?? [];
|
|
7024
|
+
_inlineCapSink = null;
|
|
7025
|
+
return sites;
|
|
7026
|
+
}
|
|
7027
|
+
function formatInlineCapSites(sites, max) {
|
|
7028
|
+
if (sites.length === 0) return [];
|
|
7029
|
+
const byFn = /* @__PURE__ */ new Map();
|
|
7030
|
+
for (const s of sites) {
|
|
7031
|
+
const cur = byFn.get(s.fn);
|
|
7032
|
+
if (cur) {
|
|
7033
|
+
cur.count += 1;
|
|
7034
|
+
cur.nodes += s.nodes;
|
|
7035
|
+
} else byFn.set(s.fn, { count: 1, nodes: s.nodes });
|
|
7036
|
+
}
|
|
7037
|
+
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.`);
|
|
7038
|
+
}
|
|
7058
7039
|
function hasSelectedRootTrivia(ctx) {
|
|
7059
7040
|
return ctx.rootTriviaClassified === true;
|
|
7060
7041
|
}
|
|
@@ -7338,10 +7319,10 @@ function ensureTriviaFn(ctx) {
|
|
|
7338
7319
|
function ensureTriviaCaptureFn(ctx) {
|
|
7339
7320
|
return ensureTriviaFn(ctx);
|
|
7340
7321
|
}
|
|
7341
|
-
function firstSetCond(codeVar,
|
|
7342
|
-
if (
|
|
7343
|
-
if (
|
|
7344
|
-
return
|
|
7322
|
+
function firstSetCond(codeVar, fs3) {
|
|
7323
|
+
if (fs3.kind === "any") return "true";
|
|
7324
|
+
if (fs3.kind === "empty") return "false";
|
|
7325
|
+
return fs3.ranges.map(
|
|
7345
7326
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
7346
7327
|
).join(" || ");
|
|
7347
7328
|
}
|
|
@@ -7354,10 +7335,10 @@ function planDisjointDispatch(parsers) {
|
|
|
7354
7335
|
const cases = [];
|
|
7355
7336
|
let total = 0;
|
|
7356
7337
|
for (const p of parsers) {
|
|
7357
|
-
const
|
|
7358
|
-
if (
|
|
7338
|
+
const fs3 = p._meta.firstSet;
|
|
7339
|
+
if (fs3.kind !== "ranges") return { kind: "if" };
|
|
7359
7340
|
const pts = [];
|
|
7360
|
-
for (const r of
|
|
7341
|
+
for (const r of fs3.ranges) {
|
|
7361
7342
|
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
7362
7343
|
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
7363
7344
|
}
|
|
@@ -7750,9 +7731,9 @@ function emitRegex(def, ctx, pos, canMatchNewline = true) {
|
|
|
7750
7731
|
stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
|
|
7751
7732
|
return { stmts, valueVar: vv, endVar };
|
|
7752
7733
|
}
|
|
7753
|
-
function syncSentinelExpr(
|
|
7754
|
-
if (
|
|
7755
|
-
return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(
|
|
7734
|
+
function syncSentinelExpr(fs3) {
|
|
7735
|
+
if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return null;
|
|
7736
|
+
return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(fs3.ranges)} })`;
|
|
7756
7737
|
}
|
|
7757
7738
|
function emitSeqValues(def, ctx, pos) {
|
|
7758
7739
|
const startV = v(ctx, "_start");
|
|
@@ -7766,8 +7747,8 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
7766
7747
|
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${syncInV} = _ctx._sync`);
|
|
7767
7748
|
const publishSync = (i) => {
|
|
7768
7749
|
if (!ctx.recovery) return "";
|
|
7769
|
-
const
|
|
7770
|
-
const ref2 = syncSentinelExpr(
|
|
7750
|
+
const fs3 = def.parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" });
|
|
7751
|
+
const ref2 = syncSentinelExpr(fs3);
|
|
7771
7752
|
return `${ind(ctx)}if (_ctx._tolerant) _ctx._sync = ${ref2 ?? syncInV}`;
|
|
7772
7753
|
};
|
|
7773
7754
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
@@ -7803,9 +7784,11 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
7803
7784
|
const markLog = v(ctx, "_mklg");
|
|
7804
7785
|
const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
|
|
7805
7786
|
const scanEndV = v(ctx, "_sne");
|
|
7806
|
-
const
|
|
7787
|
+
const logV = v(ctx, "_tlg");
|
|
7788
|
+
const capArg = ctx.noHoist ? "0" : hasSelectedRootTrivia(ctx) ? `(${logV} !== undefined || _ctx._rootTriviaLog !== undefined) ? 2 : 0` : `${logV} !== undefined ? 2 : 0`;
|
|
7807
7789
|
stmts.push(
|
|
7808
|
-
`${ind(ctx)}const ${
|
|
7790
|
+
`${ind(ctx)}const ${logV} = _ctx._triviaLog`,
|
|
7791
|
+
`${ind(ctx)}const ${markLog} = ${logV} !== undefined ? ${logV}.length : 0`,
|
|
7809
7792
|
...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
|
|
7810
7793
|
`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
|
|
7811
7794
|
...emitLineTrack(ctx, curV, scanEndV)
|
|
@@ -7815,7 +7798,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
7815
7798
|
const endAfterV = v(ctx, "_sea");
|
|
7816
7799
|
stmts.push(
|
|
7817
7800
|
`${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
|
|
7818
|
-
`${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else { if (
|
|
7801
|
+
`${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};` : ""} }`
|
|
7819
7802
|
);
|
|
7820
7803
|
valueVars.push(r2.valueVar);
|
|
7821
7804
|
continue;
|
|
@@ -7881,8 +7864,8 @@ function canMatchEmptyAtStart(p) {
|
|
|
7881
7864
|
}
|
|
7882
7865
|
}
|
|
7883
7866
|
function needsFirstSetGuard(p) {
|
|
7884
|
-
const
|
|
7885
|
-
return
|
|
7867
|
+
const fs3 = p._meta.firstSet;
|
|
7868
|
+
return fs3.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
7886
7869
|
}
|
|
7887
7870
|
function coverageHit(ctx, id, offset = "_pos", end) {
|
|
7888
7871
|
return id === void 0 ? [] : [
|
|
@@ -8887,9 +8870,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
8887
8870
|
endVar: pos
|
|
8888
8871
|
};
|
|
8889
8872
|
}
|
|
8890
|
-
function emitRouted(ctx, pos) {
|
|
8873
|
+
function emitRouted(ctx, pos, def) {
|
|
8891
8874
|
const local = ctx.routedLocal;
|
|
8875
|
+
const fallback = def.fallback;
|
|
8892
8876
|
if (local !== void 0) {
|
|
8877
|
+
if (fallback !== void 0 && local.startVar !== pos) {
|
|
8878
|
+
return emitRoutedWithFallback(ctx, pos, fallback, {
|
|
8879
|
+
test: `${local.startVar} === ${pos}`,
|
|
8880
|
+
valExpr: local.valueVar,
|
|
8881
|
+
startExpr: local.startVar,
|
|
8882
|
+
endExpr: local.endVar
|
|
8883
|
+
});
|
|
8884
|
+
}
|
|
8893
8885
|
return {
|
|
8894
8886
|
stmts: [
|
|
8895
8887
|
...emitIfFail(ctx, `${local.startVar} !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
@@ -8900,9 +8892,19 @@ function emitRouted(ctx, pos) {
|
|
|
8900
8892
|
};
|
|
8901
8893
|
}
|
|
8902
8894
|
const item = v(ctx, "_rt");
|
|
8895
|
+
const read = `${ind(ctx)}const ${item} = _ctx._routed`;
|
|
8896
|
+
if (fallback !== void 0) {
|
|
8897
|
+
const r = emitRoutedWithFallback(ctx, pos, fallback, {
|
|
8898
|
+
test: `${item} !== undefined && ${item}.span.start === ${pos}`,
|
|
8899
|
+
valExpr: `${item}.value`,
|
|
8900
|
+
startExpr: `${item}.span.start`,
|
|
8901
|
+
endExpr: `${item}.span.end`
|
|
8902
|
+
});
|
|
8903
|
+
return { ...r, stmts: [read, ...r.stmts] };
|
|
8904
|
+
}
|
|
8903
8905
|
return {
|
|
8904
8906
|
stmts: [
|
|
8905
|
-
|
|
8907
|
+
read,
|
|
8906
8908
|
...emitIfFail(ctx, `${item} === undefined || ${item}.span.start !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
8907
8909
|
...emitLeafCapture(ctx, `${item}.value`, `${item}.span.start`, `${item}.span.end`)
|
|
8908
8910
|
],
|
|
@@ -8910,6 +8912,30 @@ function emitRouted(ctx, pos) {
|
|
|
8910
8912
|
endVar: `${item}.span.end`
|
|
8911
8913
|
};
|
|
8912
8914
|
}
|
|
8915
|
+
function emitRoutedWithFallback(ctx, pos, fallback, routed2) {
|
|
8916
|
+
const valV = v(ctx, "_rtv");
|
|
8917
|
+
const endV = v(ctx, "_rte");
|
|
8918
|
+
const i = ind(ctx);
|
|
8919
|
+
ctx.indent++;
|
|
8920
|
+
const capture = emitLeafCapture(ctx, routed2.valExpr, routed2.startExpr, routed2.endExpr);
|
|
8921
|
+
const fb = emit(fallback, ctx, pos);
|
|
8922
|
+
const inner = ind(ctx);
|
|
8923
|
+
ctx.indent--;
|
|
8924
|
+
return {
|
|
8925
|
+
stmts: [
|
|
8926
|
+
`${i}let ${valV}, ${endV}`,
|
|
8927
|
+
`${i}if (${routed2.test}) {`,
|
|
8928
|
+
...capture,
|
|
8929
|
+
`${inner}${valV} = ${routed2.valExpr}; ${endV} = ${routed2.endExpr}`,
|
|
8930
|
+
`${i}} else {`,
|
|
8931
|
+
...fb.stmts,
|
|
8932
|
+
`${inner}${valV} = ${fb.valueVar}; ${endV} = ${fb.endVar}`,
|
|
8933
|
+
`${i}}`
|
|
8934
|
+
],
|
|
8935
|
+
valueVar: valV,
|
|
8936
|
+
endVar: endV
|
|
8937
|
+
};
|
|
8938
|
+
}
|
|
8913
8939
|
function escapeTokenLiteral(value) {
|
|
8914
8940
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
8915
8941
|
}
|
|
@@ -9091,22 +9117,16 @@ function emitNode(def, ctx, pos) {
|
|
|
9091
9117
|
const smk = structural ? v(ctx, "_smk") : null;
|
|
9092
9118
|
if (structural) ctx.needsHostReads = true;
|
|
9093
9119
|
const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
|
|
9094
|
-
const
|
|
9095
|
-
const
|
|
9096
|
-
const profileRecognizer = recV;
|
|
9097
|
-
const profileCapture = capV;
|
|
9098
|
-
const cstMode = ctx.hostMode === "cst";
|
|
9099
|
-
const directCstV = !structural && !cstMode && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
|
|
9100
|
-
const directCstGate = directCstV === null ? "false" : profileCapture;
|
|
9120
|
+
const profileRecognizer = "false";
|
|
9121
|
+
const profileCapture = "false";
|
|
9101
9122
|
const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
|
|
9102
9123
|
const chNeededExpr = def.unwrap || def.collapse ? "true" : `(_ctx._pmReadsCh ??= (_ctx.build === undefined || _ctx.build._parsemanReadsChildren !== false || _ctx.build._parsemanCstCollapse !== undefined))`;
|
|
9103
|
-
const chAlloc = def.unwrap || def.collapse ?
|
|
9104
|
-
const
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
|
|
9124
|
+
const chAlloc = def.unwrap || def.collapse ? tern(profileRecognizer, "undefined", "[]") : tern(profileRecognizer, "undefined", `${tern(orGate(profileCapture, chNeededExpr), "[]", "undefined")}`);
|
|
9125
|
+
const dcstAlloc = "undefined";
|
|
9126
|
+
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))"))}` : ""}
|
|
9127
|
+
${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)}`;
|
|
9108
9128
|
const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
|
|
9109
|
-
const fieldsOn = structural ? capFv ?? "false" :
|
|
9129
|
+
const fieldsOn = structural ? capFv ?? "false" : tern(profileRecognizer, "false", capturesFields ? "true" : "false");
|
|
9110
9130
|
const sf = hasFields ? v(ctx, "_sf") : null;
|
|
9111
9131
|
const fArr = hasFields ? v(ctx, "_fa") : null;
|
|
9112
9132
|
const fObj = hasFields ? v(ctx, "_fields") : "undefined";
|
|
@@ -9115,7 +9135,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9115
9135
|
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}` : ""}` : "";
|
|
9116
9136
|
const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
|
|
9117
9137
|
const preGuard = [];
|
|
9118
|
-
if (!ctx.recovery && !ctx.coverage &&
|
|
9138
|
+
if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
|
|
9119
9139
|
const gcV = v(ctx, "_ngc");
|
|
9120
9140
|
const gExp = armStaticExpected(ctx, def.parser);
|
|
9121
9141
|
const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
|
|
@@ -9126,7 +9146,6 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9126
9146
|
}
|
|
9127
9147
|
const stmts = [
|
|
9128
9148
|
...preGuard,
|
|
9129
|
-
profHoist,
|
|
9130
9149
|
allocStmt,
|
|
9131
9150
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
|
|
9132
9151
|
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}${installTrivia}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
|
|
@@ -9143,21 +9162,13 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9143
9162
|
}
|
|
9144
9163
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}${restoreTrivia}${sf ? `; _ctx._fields = ${sf}` : ""}`);
|
|
9145
9164
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
9146
|
-
stmts.push(
|
|
9147
|
-
`${i}if (_ctx._pmProfile) {`,
|
|
9148
|
-
`${i} _ctx._pmProfile.nodes++`,
|
|
9149
|
-
`${i} if (${profileCapture}) {`,
|
|
9150
|
-
`${i} _ctx._pmProfile.childSlots += ${chV}.length; _ctx._pmProfile.rawSlots += ${rawV}.length; _ctx._pmProfile.triviaSlots += ${tlV}.length${fArr ? `; _ctx._pmProfile.fieldSlots += ${fArr} ? ${fArr}.length : 0` : ""}`,
|
|
9151
|
-
`${i} }`,
|
|
9152
|
-
`${i}}`
|
|
9153
|
-
);
|
|
9154
9165
|
let stV = "undefined";
|
|
9155
9166
|
if (structural) {
|
|
9156
9167
|
stV = v(ctx, "_nst");
|
|
9157
9168
|
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
9158
9169
|
} else if (clonesState) {
|
|
9159
9170
|
stV = v(ctx, "_nst");
|
|
9160
|
-
stmts.push(`${i}const ${stV} =
|
|
9171
|
+
stmts.push(`${i}const ${stV} = ${andGate(notGate(orGate(profileRecognizer, profileCapture)), "_ctx.state !== undefined")} ? Object.assign({}, _ctx.state) : undefined`);
|
|
9161
9172
|
}
|
|
9162
9173
|
if (sf && fArr) {
|
|
9163
9174
|
const fe = v(ctx, "_fe");
|
|
@@ -9181,19 +9192,22 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
|
|
|
9181
9192
|
if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
|
|
9182
9193
|
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})`;
|
|
9183
9194
|
const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
|
|
9184
|
-
const hostBuildExpr = `_ctx.
|
|
9195
|
+
const hostBuildExpr = `_ctx.build(${hostBuildArgs})`;
|
|
9185
9196
|
const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
|
|
9186
|
-
const
|
|
9197
|
+
const hostCollapses = structural || cstOut;
|
|
9198
|
+
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;
|
|
9187
9199
|
const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
|
|
9188
9200
|
const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
|
|
9189
9201
|
const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
);
|
|
9202
|
+
const ndGate = orGate(profileRecognizer, profileCapture);
|
|
9203
|
+
const recGate = notGate(profileRecognizer);
|
|
9204
|
+
const pushIndent = recGate === "true" ? i : `${i} `;
|
|
9205
|
+
const pushCh = `${pushIndent}if (${sc}) ${sc}.push(${ndV})`;
|
|
9206
|
+
ctx.needsRawEntry = true;
|
|
9207
|
+
const pushRaw = `${pushIndent}if (${sr}) ${sr}.push(_rawEntry(${ndV}, input, ${pos}, ${endVar}${ctx.lineTracking ? `, ${nodeSpanV}` : ""}))`;
|
|
9208
|
+
stmts.push(`${i}const ${ndV} = ${tern(ndGate, "undefined", `(${finalExpr})`)}`);
|
|
9209
|
+
if (recGate === "true") stmts.push(pushCh, pushRaw);
|
|
9210
|
+
else stmts.push(`${i}if (${recGate}) {`, pushCh, pushRaw, `${i}}`);
|
|
9197
9211
|
return { stmts, valueVar: ndV, endVar };
|
|
9198
9212
|
}
|
|
9199
9213
|
function emitRuntimeFallback(parser2, ctx, pos) {
|
|
@@ -9222,7 +9236,12 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
9222
9236
|
} catch {
|
|
9223
9237
|
return emitRuntimeFallback(p, ctx, pos);
|
|
9224
9238
|
}
|
|
9225
|
-
|
|
9239
|
+
const cost = usage.sizes.get(resolved) ?? 1;
|
|
9240
|
+
if (cost <= ctx.inlineLeft) {
|
|
9241
|
+
ctx.inlineLeft -= cost;
|
|
9242
|
+
return emit(resolved, ctx, pos);
|
|
9243
|
+
}
|
|
9244
|
+
_inlineCapSink?.push({ fn: ctx.currentFnName ?? "<root>", nodes: cost });
|
|
9226
9245
|
}
|
|
9227
9246
|
if (!ctx.namedParsers.has(p)) {
|
|
9228
9247
|
const fnName2 = canonical2 ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
@@ -9237,6 +9256,9 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
9237
9256
|
const savedIndent = ctx.indent;
|
|
9238
9257
|
const savedFailLabel = ctx.failLabel;
|
|
9239
9258
|
const savedRecord = ctx.recordFail;
|
|
9259
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
9260
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
9261
|
+
ctx.currentFnName = fnName2;
|
|
9240
9262
|
const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
|
|
9241
9263
|
const savedActiveRule = ctx.activeCoverageRuleId;
|
|
9242
9264
|
const savedSuppressFailure = ctx.suppressCoverageFailure;
|
|
@@ -9251,6 +9273,8 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
9251
9273
|
ctx.indent = savedIndent;
|
|
9252
9274
|
ctx.failLabel = savedFailLabel;
|
|
9253
9275
|
ctx.recordFail = savedRecord;
|
|
9276
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
9277
|
+
ctx.currentFnName = savedFnName;
|
|
9254
9278
|
ctx.activeCoverageRuleId = savedActiveRule;
|
|
9255
9279
|
ctx.suppressCoverageFailure = savedSuppressFailure;
|
|
9256
9280
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
|
|
@@ -9345,6 +9369,9 @@ function emit(p, ctx, pos) {
|
|
|
9345
9369
|
const savedIndent = ctx.indent;
|
|
9346
9370
|
const savedFailLabel = ctx.failLabel;
|
|
9347
9371
|
const savedRecord = ctx.recordFail;
|
|
9372
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
9373
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
9374
|
+
ctx.currentFnName = fnName;
|
|
9348
9375
|
ctx.indent = 1;
|
|
9349
9376
|
ctx.failLabel = "_pfail";
|
|
9350
9377
|
ctx.recordFail = true;
|
|
@@ -9352,6 +9379,8 @@ function emit(p, ctx, pos) {
|
|
|
9352
9379
|
ctx.indent = savedIndent;
|
|
9353
9380
|
ctx.failLabel = savedFailLabel;
|
|
9354
9381
|
ctx.recordFail = savedRecord;
|
|
9382
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
9383
|
+
ctx.currentFnName = savedFnName;
|
|
9355
9384
|
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
9356
9385
|
return instrument(emitNamedFnCall(ctx, fnName, pos));
|
|
9357
9386
|
}
|
|
@@ -9545,7 +9574,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
9545
9574
|
case "peek":
|
|
9546
9575
|
return emitPeek(def, ctx, pos);
|
|
9547
9576
|
case "routed":
|
|
9548
|
-
return emitRouted(ctx, pos);
|
|
9577
|
+
return emitRouted(ctx, pos, def);
|
|
9549
9578
|
case "node":
|
|
9550
9579
|
return emitNode(def, ctx, pos);
|
|
9551
9580
|
case "scanTo":
|
|
@@ -9580,6 +9609,9 @@ function emitDispatch(p, ctx, pos) {
|
|
|
9580
9609
|
const savedIndent = ctx.indent;
|
|
9581
9610
|
const savedFailLabel = ctx.failLabel;
|
|
9582
9611
|
const savedRecord = ctx.recordFail;
|
|
9612
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
9613
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
9614
|
+
ctx.currentFnName = fnName;
|
|
9583
9615
|
ctx.indent = 1;
|
|
9584
9616
|
ctx.failLabel = "_pfail";
|
|
9585
9617
|
ctx.recordFail = true;
|
|
@@ -9587,6 +9619,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
9587
9619
|
ctx.indent = savedIndent;
|
|
9588
9620
|
ctx.failLabel = savedFailLabel;
|
|
9589
9621
|
ctx.recordFail = savedRecord;
|
|
9622
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
9623
|
+
ctx.currentFnName = savedFnName;
|
|
9590
9624
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
9591
9625
|
}
|
|
9592
9626
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -9745,12 +9779,16 @@ function childrenOf3(def) {
|
|
|
9745
9779
|
return [def.parser, def.sentinel];
|
|
9746
9780
|
case "scanTo":
|
|
9747
9781
|
return [def.sentinel, ...def.skip];
|
|
9782
|
+
// A `routed()` fallback IS emitted at this site (emitRouted), so the usage
|
|
9783
|
+
// analysis must see the edge — otherwise a fallback shared by several routed()
|
|
9784
|
+
// sites is miscounted as single-use and inlined at each.
|
|
9785
|
+
case "routed":
|
|
9786
|
+
return def.fallback ? [def.fallback] : [];
|
|
9748
9787
|
case "lazy":
|
|
9749
9788
|
case "literal":
|
|
9750
9789
|
case "regex":
|
|
9751
9790
|
case "keywords":
|
|
9752
9791
|
case "guard":
|
|
9753
|
-
case "routed":
|
|
9754
9792
|
case "unknown":
|
|
9755
9793
|
return [];
|
|
9756
9794
|
}
|
|
@@ -9771,6 +9809,17 @@ function isHoistableTag(tag) {
|
|
|
9771
9809
|
return HOISTABLE_TAGS.has(tag);
|
|
9772
9810
|
}
|
|
9773
9811
|
var HOIST_MIN_SUBTREE = 3;
|
|
9812
|
+
var INLINE_MAX_NODES = 1e3;
|
|
9813
|
+
function resolveInlineMax(explicit) {
|
|
9814
|
+
if (explicit !== void 0) return explicit;
|
|
9815
|
+
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_MAX_INLINE : void 0;
|
|
9816
|
+
if (env !== void 0 && env !== "") {
|
|
9817
|
+
if (env === "off" || env === "infinity") return Infinity;
|
|
9818
|
+
const n = Number(env);
|
|
9819
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
9820
|
+
}
|
|
9821
|
+
return INLINE_MAX_NODES;
|
|
9822
|
+
}
|
|
9774
9823
|
function analyzeLazyUsage(root) {
|
|
9775
9824
|
return analyzeLazyUsageMulti([root]);
|
|
9776
9825
|
}
|
|
@@ -9876,102 +9925,6 @@ function ruleDependencies(ruleMap) {
|
|
|
9876
9925
|
}
|
|
9877
9926
|
return deps;
|
|
9878
9927
|
}
|
|
9879
|
-
function resolveGatingLevel(opt) {
|
|
9880
|
-
const explicit = typeof opt === "string" ? opt : opt?.level;
|
|
9881
|
-
if (explicit !== void 0) return explicit;
|
|
9882
|
-
const env = typeof process !== "undefined" ? process.env?.PARSEMAN_GATING : void 0;
|
|
9883
|
-
if (env === "off" || env === "warn" || env === "error") return env;
|
|
9884
|
-
return "warn";
|
|
9885
|
-
}
|
|
9886
|
-
function runGatingDiagnostic(combinator, opt) {
|
|
9887
|
-
return reportGating(opt, (o) => analyzeGating(combinator, o));
|
|
9888
|
-
}
|
|
9889
|
-
function runGatingDiagnosticRules(ruleMap, opt) {
|
|
9890
|
-
return reportGating(opt, (o) => analyzeGatingRules(ruleMap, o));
|
|
9891
|
-
}
|
|
9892
|
-
function runFusedGatingDiagnostic(getRuleMaps, opt, getOpaque) {
|
|
9893
|
-
if (resolveGatingLevel(opt) === "off") return void 0;
|
|
9894
|
-
const ruleMaps = getRuleMaps();
|
|
9895
|
-
const opaque = getOpaque?.() ?? [];
|
|
9896
|
-
const opaqueFindings = opaque.map((o) => ({
|
|
9897
|
-
rule: o.ruleNames.length > 0 ? o.ruleNames.join(", ") : `<artifact ${o.ns}>`,
|
|
9898
|
-
kind: "opaque-artifact",
|
|
9899
|
-
reason: `precompiled artifact "${o.ns}" carries compiled rule functions, not re-lowerable IR \u2014 its choices were not examined at this fuse.`
|
|
9900
|
-
}));
|
|
9901
|
-
if (!ruleMaps.some((map) => hasExternalRuleRef(map))) {
|
|
9902
|
-
if (opaqueFindings.length === 0) return void 0;
|
|
9903
|
-
return reportGating(opt, () => ({
|
|
9904
|
-
totalChoices: 0,
|
|
9905
|
-
gated: 0,
|
|
9906
|
-
recoverable: 0,
|
|
9907
|
-
unanalysable: opaqueFindings,
|
|
9908
|
-
ungated: [],
|
|
9909
|
-
accepted: [],
|
|
9910
|
-
deferred: [],
|
|
9911
|
-
acceptedUnused: [],
|
|
9912
|
-
choices: [],
|
|
9913
|
-
antiPatterns: []
|
|
9914
|
-
}));
|
|
9915
|
-
}
|
|
9916
|
-
const winners = /* @__PURE__ */ new Map();
|
|
9917
|
-
for (const map of ruleMaps) for (const [name, rule] of map) {
|
|
9918
|
-
if (rule._def.tag === "lazy") {
|
|
9919
|
-
try {
|
|
9920
|
-
rule._def.thunk();
|
|
9921
|
-
} catch {
|
|
9922
|
-
continue;
|
|
9923
|
-
}
|
|
9924
|
-
}
|
|
9925
|
-
winners.set(name, rule);
|
|
9926
|
-
}
|
|
9927
|
-
const entries = [...winners];
|
|
9928
|
-
return reportGating(opt, (o) => {
|
|
9929
|
-
const deferredHere = new Set(analyzeGatingRules(entries, o).deferred.map((c) => c.id));
|
|
9930
|
-
const fused = analyzeGatingRules(entries, { ...o, resolveRef: (name) => winners.get(name) });
|
|
9931
|
-
return {
|
|
9932
|
-
...fused,
|
|
9933
|
-
ungated: fused.ungated.filter((c) => deferredHere.has(c.id)),
|
|
9934
|
-
antiPatterns: [],
|
|
9935
|
-
unanalysable: [...fused.unanalysable, ...opaqueFindings]
|
|
9936
|
-
};
|
|
9937
|
-
});
|
|
9938
|
-
}
|
|
9939
|
-
function reportGating(opt, analyze) {
|
|
9940
|
-
const level = resolveGatingLevel(opt);
|
|
9941
|
-
if (level === "off") return void 0;
|
|
9942
|
-
const obj = opt !== null && typeof opt === "object" ? opt : void 0;
|
|
9943
|
-
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;
|
|
9944
|
-
let report;
|
|
9945
|
-
try {
|
|
9946
|
-
report = analyze(analyzeOpts);
|
|
9947
|
-
} catch (e) {
|
|
9948
|
-
const reason = e instanceof Error ? `${e.name}: ${e.message}` : String(e);
|
|
9949
|
-
const failed = {
|
|
9950
|
-
totalChoices: 0,
|
|
9951
|
-
gated: 0,
|
|
9952
|
-
recoverable: 0,
|
|
9953
|
-
unanalysable: [{ rule: "<whole grammar>", kind: "not-a-combinator", reason: `gating analysis threw \u2014 ${reason}` }],
|
|
9954
|
-
ungated: [],
|
|
9955
|
-
accepted: [],
|
|
9956
|
-
deferred: [],
|
|
9957
|
-
acceptedUnused: [],
|
|
9958
|
-
choices: [],
|
|
9959
|
-
antiPatterns: []
|
|
9960
|
-
};
|
|
9961
|
-
const failLines = formatGatingWarnings(failed);
|
|
9962
|
-
if (level === "error") throw new Error(`parseman: gating analysis FAILED (no verdict available)
|
|
9963
|
-
${failLines.join("\n")}`);
|
|
9964
|
-
for (const l of failLines) console.warn(l);
|
|
9965
|
-
return failed;
|
|
9966
|
-
}
|
|
9967
|
-
const lines = formatGatingWarnings(report);
|
|
9968
|
-
if (lines.length > 0) {
|
|
9969
|
-
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)
|
|
9970
|
-
${lines.join("\n")}`);
|
|
9971
|
-
for (const l of lines) console.warn(l);
|
|
9972
|
-
}
|
|
9973
|
-
return report;
|
|
9974
|
-
}
|
|
9975
9928
|
function resolveDuplicationLevel(opt) {
|
|
9976
9929
|
const explicit = typeof opt === "string" ? opt : opt?.level;
|
|
9977
9930
|
if (explicit !== void 0) return explicit;
|
|
@@ -10014,7 +9967,17 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
|
|
|
10014
9967
|
return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
|
|
10015
9968
|
}
|
|
10016
9969
|
function compile(combinator, mapFnSources, opts) {
|
|
10017
|
-
const
|
|
9970
|
+
const drain = beginCompileDegradationDrain();
|
|
9971
|
+
let completed = false;
|
|
9972
|
+
try {
|
|
9973
|
+
const compiled = compileImpl(combinator, mapFnSources, opts);
|
|
9974
|
+
completed = true;
|
|
9975
|
+
return compiled;
|
|
9976
|
+
} finally {
|
|
9977
|
+
drain(completed);
|
|
9978
|
+
}
|
|
9979
|
+
}
|
|
9980
|
+
function compileImpl(combinator, mapFnSources, opts) {
|
|
10018
9981
|
runDuplicationDiagnostic(combinator, opts?.duplication);
|
|
10019
9982
|
markUnusedValues(combinator);
|
|
10020
9983
|
const grammarTrivia = combinator._meta.grammarTrivia;
|
|
@@ -10022,9 +9985,12 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10022
9985
|
const grammarScanSkip = combinator._meta.grammarScanSkip;
|
|
10023
9986
|
const grammarHostMode = combinator._meta.grammarHostMode;
|
|
10024
9987
|
const grammarTrackLines = combinator._meta.grammarTrackLines;
|
|
9988
|
+
const _inlineMax = resolveInlineMax(opts?.maxInline);
|
|
10025
9989
|
const ctx = {
|
|
10026
9990
|
vars: 0,
|
|
10027
9991
|
indent: 1,
|
|
9992
|
+
inlineMax: _inlineMax,
|
|
9993
|
+
inlineLeft: _inlineMax,
|
|
10028
9994
|
regexDecls: [],
|
|
10029
9995
|
regexMap: /* @__PURE__ */ new Map(),
|
|
10030
9996
|
expectedDecls: [],
|
|
@@ -10070,11 +10036,13 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10070
10036
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
10071
10037
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
10072
10038
|
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
10039
|
+
const rawEntryDecls = ctx.needsRawEntry ? [RAW_ENTRY_DECL] : [];
|
|
10073
10040
|
const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
|
|
10074
10041
|
const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
|
|
10075
10042
|
const source = [
|
|
10076
10043
|
...emptyTlDecls,
|
|
10077
10044
|
...hostReadsDecls,
|
|
10045
|
+
...rawEntryDecls,
|
|
10078
10046
|
...lineTrackDecls,
|
|
10079
10047
|
...lineSpanDecls,
|
|
10080
10048
|
...ctx.regexDecls,
|
|
@@ -10089,6 +10057,7 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10089
10057
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
10090
10058
|
...emptyTlDecls,
|
|
10091
10059
|
...hostReadsDecls,
|
|
10060
|
+
...rawEntryDecls,
|
|
10092
10061
|
...lineTrackDecls,
|
|
10093
10062
|
...lineSpanDecls,
|
|
10094
10063
|
...ctx.regexDecls,
|
|
@@ -10138,8 +10107,7 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
10138
10107
|
const compiledMeta = {
|
|
10139
10108
|
source,
|
|
10140
10109
|
inlineExpression,
|
|
10141
|
-
...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
|
|
10142
|
-
...gatingReport === void 0 ? {} : { gating: gatingReport }
|
|
10110
|
+
...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
|
|
10143
10111
|
};
|
|
10144
10112
|
if (!ctx.lineTracking) {
|
|
10145
10113
|
return {
|
|
@@ -10204,7 +10172,6 @@ function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels, ambien
|
|
|
10204
10172
|
return `Object.assign(${fnSource}, { _meta: {${labels === void 0 ? "" : ` triviaKindLabels: ${JSON.stringify(labels)},`}${classified === void 0 ? "" : " rootTriviaClassified: true,"} } })`;
|
|
10205
10173
|
}
|
|
10206
10174
|
function compileRuleMap(ruleMap, opts) {
|
|
10207
|
-
runGatingDiagnosticRules(ruleMap, opts?.gating);
|
|
10208
10175
|
runDuplicationDiagnosticRules(ruleMap, opts?.duplication);
|
|
10209
10176
|
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
10210
10177
|
const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
|
|
@@ -10213,9 +10180,12 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
10213
10180
|
const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
10214
10181
|
const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
|
|
10215
10182
|
const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
|
|
10183
|
+
const _inlineMax = resolveInlineMax(opts?.maxInline);
|
|
10216
10184
|
const ctx = {
|
|
10217
10185
|
vars: 0,
|
|
10218
10186
|
indent: 1,
|
|
10187
|
+
inlineMax: _inlineMax,
|
|
10188
|
+
inlineLeft: _inlineMax,
|
|
10219
10189
|
regexDecls: [],
|
|
10220
10190
|
regexMap: /* @__PURE__ */ new Map(),
|
|
10221
10191
|
expectedDecls: [],
|
|
@@ -10281,6 +10251,7 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
10281
10251
|
const hoistedDecls = [
|
|
10282
10252
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
10283
10253
|
ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
|
|
10254
|
+
ctx.needsRawEntry ? ` ${RAW_ENTRY_DECL}` : "",
|
|
10284
10255
|
ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "",
|
|
10285
10256
|
ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "",
|
|
10286
10257
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
@@ -10353,7 +10324,6 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
10353
10324
|
}
|
|
10354
10325
|
function compileLinkable(ruleMapArg, ns, opts) {
|
|
10355
10326
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
10356
|
-
if (opts?.gating !== void 0) runGatingDiagnosticRules(ruleMapArg, opts.gating);
|
|
10357
10327
|
if (opts?.duplication !== void 0) runDuplicationDiagnosticRules(ruleMapArg, opts.duplication);
|
|
10358
10328
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
10359
10329
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
@@ -10372,9 +10342,12 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10372
10342
|
}
|
|
10373
10343
|
});
|
|
10374
10344
|
const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
|
|
10345
|
+
const _inlineMax = resolveInlineMax(opts?.maxInline);
|
|
10375
10346
|
const ctx = {
|
|
10376
10347
|
vars: 0,
|
|
10377
10348
|
indent: 1,
|
|
10349
|
+
inlineMax: _inlineMax,
|
|
10350
|
+
inlineLeft: _inlineMax,
|
|
10378
10351
|
regexDecls: [],
|
|
10379
10352
|
regexMap: /* @__PURE__ */ new Map(),
|
|
10380
10353
|
expectedDecls: [],
|
|
@@ -10470,6 +10443,9 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10470
10443
|
}
|
|
10471
10444
|
}
|
|
10472
10445
|
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
10446
|
+
const savedInlineLeft = ctx.inlineLeft, savedFnName = ctx.currentFnName;
|
|
10447
|
+
ctx.inlineLeft = ctx.inlineMax;
|
|
10448
|
+
ctx.currentFnName = fn;
|
|
10473
10449
|
ctx.indent = 1;
|
|
10474
10450
|
ctx.failLabel = "_pfail";
|
|
10475
10451
|
ctx.recordFail = true;
|
|
@@ -10485,6 +10461,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10485
10461
|
ctx.indent = savedIndent;
|
|
10486
10462
|
ctx.failLabel = savedFail;
|
|
10487
10463
|
ctx.recordFail = savedRec;
|
|
10464
|
+
ctx.inlineLeft = savedInlineLeft;
|
|
10465
|
+
ctx.currentFnName = savedFnName;
|
|
10488
10466
|
const ruleId = ctx.coverage?.plan.rules.get(rule);
|
|
10489
10467
|
const stmts = ruleId === void 0 ? body.stmts : [
|
|
10490
10468
|
`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
|
|
@@ -10569,6 +10547,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
10569
10547
|
deps: ruleDependencies(ruleMap),
|
|
10570
10548
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
10571
10549
|
needsHostReads: !!ctx.needsHostReads,
|
|
10550
|
+
needsRawEntry: !!ctx.needsRawEntry,
|
|
10572
10551
|
hostMode: ctx.hostMode ?? "ast",
|
|
10573
10552
|
hostBranchElided: !!ctx.hostBranchElided,
|
|
10574
10553
|
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
@@ -10643,14 +10622,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
|
|
|
10643
10622
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
10644
10623
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
10645
10624
|
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
10625
|
+
const rawEntryDecl = ctx.needsRawEntry ? ` ${RAW_ENTRY_DECL}` : "";
|
|
10646
10626
|
const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
|
|
10647
10627
|
const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
|
|
10648
|
-
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
|
|
10628
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!rawEntryDecl || !!lineTrackDecl || !!lineSpanDecl;
|
|
10649
10629
|
if (!needsWrapper) return innerFn;
|
|
10650
10630
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
10651
10631
|
const hoistedDecls = [
|
|
10652
10632
|
emptyTlDecl,
|
|
10653
10633
|
hostReadsDecl,
|
|
10634
|
+
rawEntryDecl,
|
|
10654
10635
|
lineTrackDecl,
|
|
10655
10636
|
lineSpanDecl,
|
|
10656
10637
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
@@ -10720,18 +10701,19 @@ function childrenOf4(def) {
|
|
|
10720
10701
|
return [def.main, def.skipped];
|
|
10721
10702
|
case "scanTo":
|
|
10722
10703
|
return [def.sentinel, ...def.skip];
|
|
10704
|
+
case "routed":
|
|
10705
|
+
return def.fallback ? [def.fallback] : [];
|
|
10723
10706
|
case "lazy":
|
|
10724
10707
|
case "literal":
|
|
10725
10708
|
case "regex":
|
|
10726
10709
|
case "keywords":
|
|
10727
|
-
case "routed":
|
|
10728
10710
|
case "guard":
|
|
10729
10711
|
case "recover":
|
|
10730
10712
|
case "unknown":
|
|
10731
10713
|
return [];
|
|
10732
10714
|
}
|
|
10733
10715
|
}
|
|
10734
|
-
var
|
|
10716
|
+
var ruleNameOf3 = (c) => c._ruleName;
|
|
10735
10717
|
function lazyTarget(c) {
|
|
10736
10718
|
if (c._def.tag !== "lazy") return null;
|
|
10737
10719
|
try {
|
|
@@ -10806,7 +10788,7 @@ function evalRuleMapIR(ir) {
|
|
|
10806
10788
|
l._def.fnSrc = src;
|
|
10807
10789
|
return l;
|
|
10808
10790
|
};
|
|
10809
|
-
const _nd = (type, child, src, opts, staticError) => {
|
|
10791
|
+
const _nd = (type, child, src, opts, staticError, sigSrc) => {
|
|
10810
10792
|
if (staticError !== void 0 && staticError.length > 0) {
|
|
10811
10793
|
throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
|
|
10812
10794
|
}
|
|
@@ -10815,6 +10797,7 @@ function evalRuleMapIR(ir) {
|
|
|
10815
10797
|
}), opts);
|
|
10816
10798
|
n._def.buildSrc = src;
|
|
10817
10799
|
if (staticError !== void 0) n._def.buildStaticError = staticError;
|
|
10800
|
+
if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
|
|
10818
10801
|
return n;
|
|
10819
10802
|
};
|
|
10820
10803
|
const _gch = (items) => {
|
|
@@ -10967,7 +10950,7 @@ var Serializer = class {
|
|
|
10967
10950
|
if (seen.has(c)) return;
|
|
10968
10951
|
seen.add(c);
|
|
10969
10952
|
if (c._def.tag === "lazy") {
|
|
10970
|
-
if (
|
|
10953
|
+
if (ruleNameOf3(c) !== void 0) throw new Unserializable("scanSkip unit references a grammar rule");
|
|
10971
10954
|
const target = lazyTarget(c);
|
|
10972
10955
|
if (target) this.assertNoRuleRef(target, seen);
|
|
10973
10956
|
return;
|
|
@@ -10978,7 +10961,7 @@ var Serializer = class {
|
|
|
10978
10961
|
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
10979
10962
|
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
10980
10963
|
body(c) {
|
|
10981
|
-
return c._def.tag === "lazy" &&
|
|
10964
|
+
return c._def.tag === "lazy" && ruleNameOf3(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
10982
10965
|
}
|
|
10983
10966
|
run() {
|
|
10984
10967
|
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
@@ -11020,7 +11003,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11020
11003
|
analyze(c, active) {
|
|
11021
11004
|
const def = c._def;
|
|
11022
11005
|
if (def.tag === "lazy") {
|
|
11023
|
-
if (
|
|
11006
|
+
if (ruleNameOf3(c) !== void 0) return;
|
|
11024
11007
|
const target = lazyTarget(c);
|
|
11025
11008
|
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
11026
11009
|
if (active.has(target)) {
|
|
@@ -11072,7 +11055,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11072
11055
|
const kid = (x) => this.ref(x, selfOf);
|
|
11073
11056
|
switch (def.tag) {
|
|
11074
11057
|
case "lazy": {
|
|
11075
|
-
const name = this.localRuleNames.get(c) ??
|
|
11058
|
+
const name = this.localRuleNames.get(c) ?? ruleNameOf3(c);
|
|
11076
11059
|
if (name !== void 0) return `g[${JSON.stringify(name)}]`;
|
|
11077
11060
|
const target = lazyTarget(c);
|
|
11078
11061
|
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
@@ -11127,7 +11110,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11127
11110
|
case "peek":
|
|
11128
11111
|
return `peek(${kid(def.parser)})`;
|
|
11129
11112
|
case "routed":
|
|
11130
|
-
return "routed()"
|
|
11113
|
+
return def.fallback === void 0 ? "routed()" : `routed(${kid(def.fallback)})`;
|
|
11131
11114
|
case "trivia":
|
|
11132
11115
|
return `trivia(${kid(def.parser)})`;
|
|
11133
11116
|
case "token":
|
|
@@ -11160,6 +11143,9 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11160
11143
|
...projectEntry === void 0 ? [] : [projectEntry],
|
|
11161
11144
|
...def.captureTrivia ? ["captureTrivia: true"] : [],
|
|
11162
11145
|
...def.trailingTrivia ? ["trailingTrivia: true"] : [],
|
|
11146
|
+
// Declared or resolved arity. Carried so a re-lowered composed artifact does
|
|
11147
|
+
// not silently re-acquire the fail-open capture cost this module resolved away.
|
|
11148
|
+
...def.buildArity === void 0 ? [] : [`buildArity: ${def.buildArity}`],
|
|
11163
11149
|
...tagEntry === void 0 ? [] : [tagEntry]
|
|
11164
11150
|
];
|
|
11165
11151
|
const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
|
|
@@ -11168,8 +11154,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
11168
11154
|
return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
|
|
11169
11155
|
}
|
|
11170
11156
|
if (def.buildSrc !== void 0) {
|
|
11171
|
-
const
|
|
11172
|
-
|
|
11157
|
+
const tail = [];
|
|
11158
|
+
if (def.buildStaticError !== void 0 || def.buildSigSrc !== void 0) {
|
|
11159
|
+
if (!opts) tail.push("undefined");
|
|
11160
|
+
tail.push(def.buildStaticError === void 0 ? "undefined" : JSON.stringify(def.buildStaticError));
|
|
11161
|
+
}
|
|
11162
|
+
if (def.buildSigSrc !== void 0) tail.push(JSON.stringify(def.buildSigSrc));
|
|
11163
|
+
const trailing = tail.length > 0 ? `, ${tail.join(", ")}` : "";
|
|
11164
|
+
return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts}${trailing})`;
|
|
11173
11165
|
}
|
|
11174
11166
|
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
11175
11167
|
}
|
|
@@ -11233,7 +11225,7 @@ function pickPieces(pieces, names) {
|
|
|
11233
11225
|
const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
|
|
11234
11226
|
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);
|
|
11235
11227
|
}
|
|
11236
|
-
function fusedBody(pieces) {
|
|
11228
|
+
function fusedBody(pieces, hoist) {
|
|
11237
11229
|
for (const p of pieces) {
|
|
11238
11230
|
if (p.v === void 0) {
|
|
11239
11231
|
throw new Error(
|
|
@@ -11257,12 +11249,14 @@ function fusedBody(pieces) {
|
|
|
11257
11249
|
const contributingPieces = [...contributing];
|
|
11258
11250
|
const needsEmptyTl = contributingPieces.some((p) => p.needsEmptyTl);
|
|
11259
11251
|
const needsHostReads = contributingPieces.some((p) => p.needsHostReads);
|
|
11252
|
+
const needsRawEntry = contributingPieces.some((p) => p.needsRawEntry);
|
|
11260
11253
|
const lines = [
|
|
11261
11254
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
11262
11255
|
"const _pfFail = {}",
|
|
11263
11256
|
"let _pfEnd",
|
|
11264
11257
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
11265
11258
|
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
11259
|
+
...needsRawEntry ? [RAW_ENTRY_DECL] : [],
|
|
11266
11260
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
11267
11261
|
...new Set(contributingPieces.flatMap((p) => p.prelude)),
|
|
11268
11262
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
@@ -11271,8 +11265,7 @@ function fusedBody(pieces) {
|
|
|
11271
11265
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
11272
11266
|
const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
|
|
11273
11267
|
const { mode, elided } = hostModeOfPieces(pieces);
|
|
11274
|
-
const
|
|
11275
|
-
...lines,
|
|
11268
|
+
const tailSrc = [
|
|
11276
11269
|
"const _map = {",
|
|
11277
11270
|
wrapperEntries.join(",\n"),
|
|
11278
11271
|
"}",
|
|
@@ -11288,6 +11281,7 @@ function fusedBody(pieces) {
|
|
|
11288
11281
|
"}",
|
|
11289
11282
|
"return _map"
|
|
11290
11283
|
].join("\n");
|
|
11284
|
+
const rawBody = [...lines, tailSrc].join("\n");
|
|
11291
11285
|
const ANY2 = { kind: "any" };
|
|
11292
11286
|
const EMPTY3 = { kind: "empty" };
|
|
11293
11287
|
const recipes = /* @__PURE__ */ new Map();
|
|
@@ -11296,8 +11290,8 @@ function fusedBody(pieces) {
|
|
|
11296
11290
|
for (const [k, p] of winner) {
|
|
11297
11291
|
const r = p.firstSetRecipes?.get(k);
|
|
11298
11292
|
if (r) recipes.set(k, r);
|
|
11299
|
-
const
|
|
11300
|
-
if (
|
|
11293
|
+
const fs3 = p.firstSets?.get(k);
|
|
11294
|
+
if (fs3) shallow.set(k, fs3);
|
|
11301
11295
|
const nu = p.nullable?.get(k);
|
|
11302
11296
|
if (nu !== void 0) nullable2.set(k, nu);
|
|
11303
11297
|
}
|
|
@@ -11320,25 +11314,26 @@ function fusedBody(pieces) {
|
|
|
11320
11314
|
for (let iter = 0; iter <= recipes.size; iter++) {
|
|
11321
11315
|
let changed = false;
|
|
11322
11316
|
for (const [k, r] of recipes) {
|
|
11323
|
-
const
|
|
11324
|
-
if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(
|
|
11325
|
-
finalFS.set(k,
|
|
11317
|
+
const fs3 = resolveRecipe(r);
|
|
11318
|
+
if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(fs3)) {
|
|
11319
|
+
finalFS.set(k, fs3);
|
|
11326
11320
|
changed = true;
|
|
11327
11321
|
}
|
|
11328
11322
|
}
|
|
11329
11323
|
if (!changed) break;
|
|
11330
11324
|
}
|
|
11331
|
-
const
|
|
11325
|
+
const resolveFS = (src) => src.replace(
|
|
11332
11326
|
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
11333
11327
|
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
11334
11328
|
// matches every well-formed placeholder.
|
|
11335
11329
|
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
11336
11330
|
(_m, name, codevar) => {
|
|
11337
|
-
const
|
|
11338
|
-
if (!
|
|
11339
|
-
return `(${firstSetCond(codevar,
|
|
11331
|
+
const fs3 = finalFS.get(name);
|
|
11332
|
+
if (!fs3 || fs3.kind === "any" || fs3.kind === "empty") return "true";
|
|
11333
|
+
return `(${firstSetCond(codevar, fs3)})`;
|
|
11340
11334
|
}
|
|
11341
11335
|
);
|
|
11336
|
+
const body = hoist === void 0 ? resolveFS(rawBody) : [...hoist.claim(lines.map(resolveFS)), resolveFS(tailSrc)].join("\n");
|
|
11342
11337
|
const env = {};
|
|
11343
11338
|
for (const p of contributingPieces) {
|
|
11344
11339
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -11356,8 +11351,8 @@ function hostModeOfPieces(pieces) {
|
|
|
11356
11351
|
}
|
|
11357
11352
|
return { mode, elided: elided.length > 0 };
|
|
11358
11353
|
}
|
|
11359
|
-
function emitFusedSource(pieces) {
|
|
11360
|
-
const { body, env } = fusedBody(pieces);
|
|
11354
|
+
function emitFusedSource(pieces, hoist) {
|
|
11355
|
+
const { body, env } = fusedBody(pieces, hoist);
|
|
11361
11356
|
if (Object.keys(env).length > 0) {
|
|
11362
11357
|
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
11363
11358
|
}
|
|
@@ -11368,17 +11363,6 @@ ${body}
|
|
|
11368
11363
|
function isIRPiece(p) {
|
|
11369
11364
|
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
11370
11365
|
}
|
|
11371
|
-
function once(fn) {
|
|
11372
|
-
let done = false;
|
|
11373
|
-
let value;
|
|
11374
|
-
return () => {
|
|
11375
|
-
if (!done) {
|
|
11376
|
-
value = fn();
|
|
11377
|
-
done = true;
|
|
11378
|
-
}
|
|
11379
|
-
return value;
|
|
11380
|
-
};
|
|
11381
|
-
}
|
|
11382
11366
|
function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
|
|
11383
11367
|
if (!isIRPiece(p)) return p;
|
|
11384
11368
|
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
|
|
@@ -11391,6 +11375,34 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
|
|
|
11391
11375
|
return pieces;
|
|
11392
11376
|
}
|
|
11393
11377
|
|
|
11378
|
+
// src/functional/run.ts
|
|
11379
|
+
var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
|
|
11380
|
+
var REMOVED_RUN_RESULT_FIELDS = [
|
|
11381
|
+
[
|
|
11382
|
+
"triviaMap",
|
|
11383
|
+
`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.`
|
|
11384
|
+
],
|
|
11385
|
+
[
|
|
11386
|
+
"triviaLog",
|
|
11387
|
+
`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.`
|
|
11388
|
+
],
|
|
11389
|
+
[
|
|
11390
|
+
"triviaKindLabels",
|
|
11391
|
+
`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\`.`
|
|
11392
|
+
]
|
|
11393
|
+
];
|
|
11394
|
+
|
|
11395
|
+
// src/analysis/choice-cost-render.ts
|
|
11396
|
+
var ESC = "\x1B";
|
|
11397
|
+
var ANSI = {
|
|
11398
|
+
dim: `${ESC}[2m`,
|
|
11399
|
+
bold: `${ESC}[1m`,
|
|
11400
|
+
red: `${ESC}[31m`,
|
|
11401
|
+
yellow: `${ESC}[33m`,
|
|
11402
|
+
cyan: `${ESC}[36m`,
|
|
11403
|
+
reset: `${ESC}[0m`
|
|
11404
|
+
};
|
|
11405
|
+
|
|
11394
11406
|
// src/combinators/gate.ts
|
|
11395
11407
|
function gate(predicate) {
|
|
11396
11408
|
const meta = {
|
|
@@ -11410,9 +11422,6 @@ function gate(predicate) {
|
|
|
11410
11422
|
};
|
|
11411
11423
|
}
|
|
11412
11424
|
|
|
11413
|
-
// src/cst/offset-model.ts
|
|
11414
|
-
var { isFinite } = Number;
|
|
11415
|
-
|
|
11416
11425
|
// src/index.ts
|
|
11417
11426
|
registerRegexAnalyzer(firstSetFromRegex);
|
|
11418
11427
|
|
|
@@ -11605,6 +11614,12 @@ function stripTsFromSource(node2, code) {
|
|
|
11605
11614
|
}
|
|
11606
11615
|
return out + code.slice(cur, end);
|
|
11607
11616
|
}
|
|
11617
|
+
var _reducers = null;
|
|
11618
|
+
var _entrySource = null;
|
|
11619
|
+
function setReducerResolver(r, entrySource = null) {
|
|
11620
|
+
_reducers = r;
|
|
11621
|
+
_entrySource = entrySource;
|
|
11622
|
+
}
|
|
11608
11623
|
function isWordFactory(v2) {
|
|
11609
11624
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
11610
11625
|
}
|
|
@@ -11836,9 +11851,12 @@ function staticNodeOptionsFromValue(value) {
|
|
|
11836
11851
|
} else if (name === "tags") {
|
|
11837
11852
|
if (!Array.isArray(v2) || !v2.every((item) => typeof item === "string")) return STATIC_NODE_OPTIONS_FAILED;
|
|
11838
11853
|
opts.tags = v2;
|
|
11854
|
+
} else if (name === "buildArity") {
|
|
11855
|
+
if (typeof v2 !== "number" || !Number.isInteger(v2) || v2 < 0 || v2 > 6) return STATIC_NODE_OPTIONS_FAILED;
|
|
11856
|
+
opts.buildArity = v2;
|
|
11839
11857
|
}
|
|
11840
11858
|
}
|
|
11841
|
-
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
|
|
11859
|
+
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 || opts.buildArity !== void 0 ? opts : void 0;
|
|
11842
11860
|
}
|
|
11843
11861
|
function staticNodeOptions(expr, scope) {
|
|
11844
11862
|
const unwrapped = unwrapStaticExpr(expr);
|
|
@@ -11864,9 +11882,13 @@ function staticNodeOptions(expr, scope) {
|
|
|
11864
11882
|
const tags = staticStringArray(p.value, scope);
|
|
11865
11883
|
if (tags === void 0) return STATIC_NODE_OPTIONS_FAILED;
|
|
11866
11884
|
opts.tags = tags;
|
|
11885
|
+
} else if (name === "buildArity") {
|
|
11886
|
+
const arity = staticLiteralValue(p.value);
|
|
11887
|
+
if (typeof arity !== "number" || !Number.isInteger(arity) || arity < 0 || arity > 6) return STATIC_NODE_OPTIONS_FAILED;
|
|
11888
|
+
opts.buildArity = arity;
|
|
11867
11889
|
}
|
|
11868
11890
|
}
|
|
11869
|
-
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
|
|
11891
|
+
return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 || opts.buildArity !== void 0 ? opts : void 0;
|
|
11870
11892
|
}
|
|
11871
11893
|
function exprToCombi(node2, scope, code, mfs) {
|
|
11872
11894
|
if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
|
|
@@ -11962,6 +11984,18 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
11962
11984
|
const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
|
|
11963
11985
|
if (combi._def.tag === "node" && buildSrc !== void 0) {
|
|
11964
11986
|
combi._def.buildSrc = buildSrc;
|
|
11987
|
+
if (explicitType !== void 0 && firstArg.type === "Identifier") {
|
|
11988
|
+
combi._def.typeSrc = firstArg.name;
|
|
11989
|
+
}
|
|
11990
|
+
const resolved = _reducers?.resolve(buildSrc, be.start, code);
|
|
11991
|
+
if (resolved) {
|
|
11992
|
+
if (resolved.src !== null) combi._def.buildSigSrc = resolved.src;
|
|
11993
|
+
if (_entrySource !== null && code !== _entrySource && resolved.src !== null && be.type === "Identifier") {
|
|
11994
|
+
combi._def.buildSrc = resolved.src;
|
|
11995
|
+
}
|
|
11996
|
+
if (combi._def.buildArity === void 0 && resolved.arity !== null) combi._def.buildArity = resolved.arity;
|
|
11997
|
+
if (resolved.reason !== void 0) combi._def.buildArityUnresolved = resolved.reason;
|
|
11998
|
+
}
|
|
11965
11999
|
const staticError = directBuilderUnsupportedBindings(buildSrc);
|
|
11966
12000
|
if (staticError.length > 0) combi._def.buildStaticError = staticError;
|
|
11967
12001
|
}
|
|
@@ -12379,9 +12413,9 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
12379
12413
|
r._ruleName = name;
|
|
12380
12414
|
if (r._def.tag === "node" && r._def.type === void 0) r._def.type = name;
|
|
12381
12415
|
};
|
|
12382
|
-
const
|
|
12416
|
+
const ruleNameOf4 = (r) => r._ruleName;
|
|
12383
12417
|
const isNamedRuleRefForAnotherRule2 = (r, key) => {
|
|
12384
|
-
const name = r._def.tag === "lazy" ?
|
|
12418
|
+
const name = r._def.tag === "lazy" ? ruleNameOf4(r) : void 0;
|
|
12385
12419
|
return name !== void 0 && name !== key;
|
|
12386
12420
|
};
|
|
12387
12421
|
for (const key of keys) {
|
|
@@ -12472,6 +12506,692 @@ function referencesAny(node2, names, scope) {
|
|
|
12472
12506
|
return false;
|
|
12473
12507
|
}
|
|
12474
12508
|
|
|
12509
|
+
// src/plugin/reducer-resolver.ts
|
|
12510
|
+
import * as fs from "node:fs";
|
|
12511
|
+
import * as path from "node:path";
|
|
12512
|
+
import { createRequire } from "node:module";
|
|
12513
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
12514
|
+
var FUNCTION_TYPES = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
|
|
12515
|
+
var SCOPE_TYPES = /* @__PURE__ */ new Set([
|
|
12516
|
+
...FUNCTION_TYPES,
|
|
12517
|
+
"BlockStatement",
|
|
12518
|
+
"ForStatement",
|
|
12519
|
+
"ForInStatement",
|
|
12520
|
+
"ForOfStatement",
|
|
12521
|
+
"CatchClause",
|
|
12522
|
+
"ClassDeclaration",
|
|
12523
|
+
"ClassExpression",
|
|
12524
|
+
"StaticBlock"
|
|
12525
|
+
]);
|
|
12526
|
+
function newScope(start, end, parent, isFunction) {
|
|
12527
|
+
const s = { start, end, bindings: /* @__PURE__ */ new Map(), children: [], parent, isFunction };
|
|
12528
|
+
parent?.children.push(s);
|
|
12529
|
+
return s;
|
|
12530
|
+
}
|
|
12531
|
+
function patternNames(pat, out) {
|
|
12532
|
+
if (!pat || typeof pat !== "object") return;
|
|
12533
|
+
const n = pat;
|
|
12534
|
+
switch (n.type) {
|
|
12535
|
+
case "Identifier":
|
|
12536
|
+
out.push(n.name);
|
|
12537
|
+
return;
|
|
12538
|
+
case "AssignmentPattern":
|
|
12539
|
+
return patternNames(n.left, out);
|
|
12540
|
+
case "RestElement":
|
|
12541
|
+
return patternNames(n.argument, out);
|
|
12542
|
+
case "ArrayPattern":
|
|
12543
|
+
for (const el of n.elements ?? []) patternNames(el, out);
|
|
12544
|
+
return;
|
|
12545
|
+
case "ObjectPattern":
|
|
12546
|
+
for (const p of n.properties ?? []) patternNames(p.type === "RestElement" ? p.argument : p.value, out);
|
|
12547
|
+
return;
|
|
12548
|
+
// A TS parameter property / assignment target wrapper.
|
|
12549
|
+
case "TSParameterProperty":
|
|
12550
|
+
return patternNames(n.parameter, out);
|
|
12551
|
+
default:
|
|
12552
|
+
return;
|
|
12553
|
+
}
|
|
12554
|
+
}
|
|
12555
|
+
function declare(scope, name, b) {
|
|
12556
|
+
if (!scope.bindings.has(name)) scope.bindings.set(name, b);
|
|
12557
|
+
}
|
|
12558
|
+
function functionScope(scope) {
|
|
12559
|
+
let s = scope;
|
|
12560
|
+
while (!s.isFunction && s.parent) s = s.parent;
|
|
12561
|
+
return s;
|
|
12562
|
+
}
|
|
12563
|
+
function buildScopeTree(body, moduleEnd) {
|
|
12564
|
+
const root = newScope(0, moduleEnd, null, true);
|
|
12565
|
+
const declarePattern = (scope, pat, kind, init) => {
|
|
12566
|
+
const names = [];
|
|
12567
|
+
patternNames(pat, names);
|
|
12568
|
+
const target = kind === "var" ? functionScope(scope) : scope;
|
|
12569
|
+
for (const name of names) {
|
|
12570
|
+
declare(target, name, init !== void 0 && names.length === 1 ? { kind, init } : { kind });
|
|
12571
|
+
}
|
|
12572
|
+
};
|
|
12573
|
+
const walk = (n, scope) => {
|
|
12574
|
+
if (!n || typeof n !== "object") return;
|
|
12575
|
+
if (Array.isArray(n)) {
|
|
12576
|
+
for (const item of n) walk(item, scope);
|
|
12577
|
+
return;
|
|
12578
|
+
}
|
|
12579
|
+
const node2 = n;
|
|
12580
|
+
if (typeof node2.type !== "string") return;
|
|
12581
|
+
switch (node2.type) {
|
|
12582
|
+
case "VariableDeclaration": {
|
|
12583
|
+
const kind = node2.kind ?? "var";
|
|
12584
|
+
for (const d of node2.declarations ?? []) {
|
|
12585
|
+
declarePattern(scope, d.id, kind, d.init ?? void 0);
|
|
12586
|
+
}
|
|
12587
|
+
break;
|
|
12588
|
+
}
|
|
12589
|
+
case "FunctionDeclaration":
|
|
12590
|
+
if (node2.id) declare(functionScope(scope), node2.id.name, { kind: "function", init: node2 });
|
|
12591
|
+
break;
|
|
12592
|
+
case "ClassDeclaration":
|
|
12593
|
+
if (node2.id) declare(scope, node2.id.name, { kind: "class" });
|
|
12594
|
+
break;
|
|
12595
|
+
case "ImportDeclaration": {
|
|
12596
|
+
const specifier = node2.source?.value ?? "";
|
|
12597
|
+
for (const sp of node2.specifiers ?? []) {
|
|
12598
|
+
const local = sp.local?.name;
|
|
12599
|
+
if (!local) continue;
|
|
12600
|
+
const imported = sp.type === "ImportDefaultSpecifier" ? "default" : sp.type === "ImportNamespaceSpecifier" ? "*" : sp.imported?.name ?? local;
|
|
12601
|
+
declare(scope, local, { kind: "import", importRef: { specifier, imported } });
|
|
12602
|
+
}
|
|
12603
|
+
break;
|
|
12604
|
+
}
|
|
12605
|
+
default:
|
|
12606
|
+
break;
|
|
12607
|
+
}
|
|
12608
|
+
if (SCOPE_TYPES.has(node2.type)) {
|
|
12609
|
+
const isFn = FUNCTION_TYPES.has(node2.type);
|
|
12610
|
+
const inner = newScope(node2.start, node2.end, scope, isFn);
|
|
12611
|
+
if (isFn) {
|
|
12612
|
+
for (const p of node2.params ?? []) declarePattern(inner, p, "param");
|
|
12613
|
+
if (node2.type === "FunctionExpression" && node2.id) {
|
|
12614
|
+
declare(inner, node2.id.name, { kind: "function", init: node2 });
|
|
12615
|
+
}
|
|
12616
|
+
}
|
|
12617
|
+
if (node2.type === "CatchClause" && node2.param) declarePattern(inner, node2.param, "catch");
|
|
12618
|
+
for (const key of Object.keys(node2)) {
|
|
12619
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12620
|
+
walk(node2[key], inner);
|
|
12621
|
+
}
|
|
12622
|
+
return;
|
|
12623
|
+
}
|
|
12624
|
+
for (const key of Object.keys(node2)) {
|
|
12625
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12626
|
+
walk(node2[key], scope);
|
|
12627
|
+
}
|
|
12628
|
+
};
|
|
12629
|
+
for (const stmt of body) walk(stmt, root);
|
|
12630
|
+
return root;
|
|
12631
|
+
}
|
|
12632
|
+
function scopeAt(root, offset) {
|
|
12633
|
+
let cur = root;
|
|
12634
|
+
for (; ; ) {
|
|
12635
|
+
const next = cur.children.find((c) => offset >= c.start && offset <= c.end);
|
|
12636
|
+
if (!next) return cur;
|
|
12637
|
+
cur = next;
|
|
12638
|
+
}
|
|
12639
|
+
}
|
|
12640
|
+
function lookup(scope, name) {
|
|
12641
|
+
for (let s = scope; s; s = s.parent) {
|
|
12642
|
+
const b = s.bindings.get(name);
|
|
12643
|
+
if (b) return { binding: b, scope: s };
|
|
12644
|
+
}
|
|
12645
|
+
return null;
|
|
12646
|
+
}
|
|
12647
|
+
function markReassignments(body, root) {
|
|
12648
|
+
const walk = (n) => {
|
|
12649
|
+
if (!n || typeof n !== "object") return;
|
|
12650
|
+
if (Array.isArray(n)) {
|
|
12651
|
+
for (const item of n) walk(item);
|
|
12652
|
+
return;
|
|
12653
|
+
}
|
|
12654
|
+
const node2 = n;
|
|
12655
|
+
if (typeof node2.type !== "string") return;
|
|
12656
|
+
if (node2.type === "AssignmentExpression" || node2.type === "UpdateExpression") {
|
|
12657
|
+
const target = node2.type === "UpdateExpression" ? node2.argument : node2.left;
|
|
12658
|
+
const names = [];
|
|
12659
|
+
if (target) patternNames(target, names);
|
|
12660
|
+
for (const name of names) {
|
|
12661
|
+
const hit = lookup(scopeAt(root, node2.start), name);
|
|
12662
|
+
if (hit) hit.binding.reassigned = true;
|
|
12663
|
+
}
|
|
12664
|
+
}
|
|
12665
|
+
for (const key of Object.keys(node2)) {
|
|
12666
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12667
|
+
walk(node2[key]);
|
|
12668
|
+
}
|
|
12669
|
+
};
|
|
12670
|
+
for (const stmt of body) walk(stmt);
|
|
12671
|
+
}
|
|
12672
|
+
function functionArity(fn) {
|
|
12673
|
+
const params = fn.params ?? [];
|
|
12674
|
+
let count = 0;
|
|
12675
|
+
for (const p of params) {
|
|
12676
|
+
if (p.type === "RestElement") return { arity: null, reason: "rest-parameter" };
|
|
12677
|
+
if (p.type === "Identifier" && p.name === "this") continue;
|
|
12678
|
+
count++;
|
|
12679
|
+
}
|
|
12680
|
+
if (fn.type !== "ArrowFunctionExpression" && referencesArguments(fn.body)) {
|
|
12681
|
+
return { arity: null, reason: "arguments" };
|
|
12682
|
+
}
|
|
12683
|
+
return { arity: count };
|
|
12684
|
+
}
|
|
12685
|
+
function referencesArguments(n) {
|
|
12686
|
+
if (!n || typeof n !== "object") return false;
|
|
12687
|
+
if (Array.isArray(n)) return n.some(referencesArguments);
|
|
12688
|
+
const node2 = n;
|
|
12689
|
+
if (typeof node2.type !== "string") return false;
|
|
12690
|
+
if (node2.type === "Identifier" && node2.name === "arguments") return true;
|
|
12691
|
+
if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression") return false;
|
|
12692
|
+
for (const key of Object.keys(node2)) {
|
|
12693
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12694
|
+
if (referencesArguments(node2[key])) return true;
|
|
12695
|
+
}
|
|
12696
|
+
return false;
|
|
12697
|
+
}
|
|
12698
|
+
var _moduleCache = /* @__PURE__ */ new Map();
|
|
12699
|
+
function analyseModuleFile(file) {
|
|
12700
|
+
let mtimeMs;
|
|
12701
|
+
try {
|
|
12702
|
+
mtimeMs = fs.statSync(file).mtimeMs;
|
|
12703
|
+
} catch {
|
|
12704
|
+
return null;
|
|
12705
|
+
}
|
|
12706
|
+
const hit = _moduleCache.get(file);
|
|
12707
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.mod;
|
|
12708
|
+
let mod = null;
|
|
12709
|
+
try {
|
|
12710
|
+
const src = fs.readFileSync(file, "utf8");
|
|
12711
|
+
const r = parseSync2(file, src);
|
|
12712
|
+
if (r.errors.length === 0) {
|
|
12713
|
+
const body = r.program.body;
|
|
12714
|
+
const scopes = buildScopeTree(body, src.length);
|
|
12715
|
+
markReassignments(body, scopes);
|
|
12716
|
+
mod = { file, src, body, scopes };
|
|
12717
|
+
}
|
|
12718
|
+
} catch {
|
|
12719
|
+
}
|
|
12720
|
+
_moduleCache.set(file, { mtimeMs, mod });
|
|
12721
|
+
return mod;
|
|
12722
|
+
}
|
|
12723
|
+
var SOURCE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".jsx"];
|
|
12724
|
+
function resolveImport(fromFile, specifier) {
|
|
12725
|
+
if (specifier.startsWith(".") || path.isAbsolute(specifier)) {
|
|
12726
|
+
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
12727
|
+
const ext = path.extname(base);
|
|
12728
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
12729
|
+
const candidates = [
|
|
12730
|
+
...ext ? [base] : [],
|
|
12731
|
+
// `./x.js` in TS source usually means `./x.ts`.
|
|
12732
|
+
...SOURCE_EXTS.map((e) => `${stem}${e}`),
|
|
12733
|
+
...SOURCE_EXTS.map((e) => path.join(base, `index${e}`))
|
|
12734
|
+
];
|
|
12735
|
+
for (const c of candidates) {
|
|
12736
|
+
try {
|
|
12737
|
+
if (fs.statSync(c).isFile()) return fs.realpathSync(c);
|
|
12738
|
+
} catch {
|
|
12739
|
+
}
|
|
12740
|
+
}
|
|
12741
|
+
return null;
|
|
12742
|
+
}
|
|
12743
|
+
try {
|
|
12744
|
+
return fs.realpathSync(createRequire(fromFile).resolve(specifier));
|
|
12745
|
+
} catch {
|
|
12746
|
+
return null;
|
|
12747
|
+
}
|
|
12748
|
+
}
|
|
12749
|
+
function declarationBinds(decl, name) {
|
|
12750
|
+
if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration") {
|
|
12751
|
+
return decl.id?.name === name;
|
|
12752
|
+
}
|
|
12753
|
+
if (decl.type === "VariableDeclaration") {
|
|
12754
|
+
const names = [];
|
|
12755
|
+
for (const d of decl.declarations ?? []) patternNames(d.id, names);
|
|
12756
|
+
return names.includes(name);
|
|
12757
|
+
}
|
|
12758
|
+
return false;
|
|
12759
|
+
}
|
|
12760
|
+
function exportedBinding(mod, name, seen) {
|
|
12761
|
+
for (const stmt of mod.body) {
|
|
12762
|
+
if (stmt.type === "ExportDefaultDeclaration" && name === "default") {
|
|
12763
|
+
const d = stmt.declaration;
|
|
12764
|
+
if (FUNCTION_TYPES.has(d.type)) return { mod, binding: { kind: "const", init: d } };
|
|
12765
|
+
if (d.type === "Identifier") {
|
|
12766
|
+
const hit2 = lookup(mod.scopes, d.name);
|
|
12767
|
+
return hit2 ? { mod, binding: hit2.binding } : null;
|
|
12768
|
+
}
|
|
12769
|
+
return null;
|
|
12770
|
+
}
|
|
12771
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
12772
|
+
const decl = stmt.declaration;
|
|
12773
|
+
if (decl && declarationBinds(decl, name)) {
|
|
12774
|
+
const hit2 = lookup(mod.scopes, name);
|
|
12775
|
+
if (hit2 && hit2.scope === mod.scopes) return { mod, binding: hit2.binding };
|
|
12776
|
+
}
|
|
12777
|
+
for (const sp of stmt.specifiers ?? []) {
|
|
12778
|
+
const exported = sp.exported?.name ?? "";
|
|
12779
|
+
if (exported !== name) continue;
|
|
12780
|
+
const local = sp.local?.name ?? exported;
|
|
12781
|
+
const from = stmt.source?.value;
|
|
12782
|
+
if (from !== void 0) return followReExport(mod, from, local, seen);
|
|
12783
|
+
const hit2 = lookup(mod.scopes, local);
|
|
12784
|
+
return hit2 ? { mod, binding: hit2.binding } : null;
|
|
12785
|
+
}
|
|
12786
|
+
}
|
|
12787
|
+
if (stmt.type === "ExportAllDeclaration" && !stmt.exported) {
|
|
12788
|
+
const from = stmt.source?.value;
|
|
12789
|
+
if (from === void 0) continue;
|
|
12790
|
+
const found = followReExport(mod, from, name, seen);
|
|
12791
|
+
if (found) return found;
|
|
12792
|
+
}
|
|
12793
|
+
}
|
|
12794
|
+
const hit = lookup(mod.scopes, name);
|
|
12795
|
+
return hit && hit.scope === mod.scopes ? { mod, binding: hit.binding } : null;
|
|
12796
|
+
}
|
|
12797
|
+
function followReExport(mod, specifier, name, seen) {
|
|
12798
|
+
const file = resolveImport(mod.file, specifier);
|
|
12799
|
+
if (!file || seen.has(`${file}\0${name}`)) return null;
|
|
12800
|
+
seen.add(`${file}\0${name}`);
|
|
12801
|
+
const next = analyseModuleFile(file);
|
|
12802
|
+
return next ? exportedBinding(next, name, seen) : null;
|
|
12803
|
+
}
|
|
12804
|
+
var MAX_HOPS = 12;
|
|
12805
|
+
var IDENT_RE = /^[A-Za-z_$][\w$]*$/;
|
|
12806
|
+
var MEMBER_RE = /^([A-Za-z_$][\w$]*)\s*\.\s*([A-Za-z_$][\w$]*)$/;
|
|
12807
|
+
function createReducerResolver(entryFile, body, src) {
|
|
12808
|
+
const scopes = buildScopeTree(body, src.length);
|
|
12809
|
+
markReassignments(body, scopes);
|
|
12810
|
+
const entry = { file: entryFile, src, body, scopes };
|
|
12811
|
+
const registered = /* @__PURE__ */ new Map();
|
|
12812
|
+
const ambiguous = /* @__PURE__ */ new Set();
|
|
12813
|
+
const fromBinding = (mod, binding, memberName, hops, seen) => {
|
|
12814
|
+
if (hops > MAX_HOPS) return { arity: null, src: null, reason: "not-found" };
|
|
12815
|
+
if (binding.reassigned) return { arity: null, src: null, reason: "reassigned" };
|
|
12816
|
+
if (binding.kind === "param" || binding.kind === "catch" || binding.kind === "class") {
|
|
12817
|
+
return { arity: null, src: null, reason: "not-a-function" };
|
|
12818
|
+
}
|
|
12819
|
+
if (binding.kind === "import" && binding.importRef) {
|
|
12820
|
+
const { specifier, imported } = binding.importRef;
|
|
12821
|
+
if (imported === "*" && memberName === void 0) return { arity: null, src: null, reason: "not-a-function" };
|
|
12822
|
+
const file = resolveImport(mod.file, specifier);
|
|
12823
|
+
if (!file) return { arity: null, src: null, reason: "unresolved-import" };
|
|
12824
|
+
const next = analyseModuleFile(file);
|
|
12825
|
+
if (!next) return { arity: null, src: null, reason: "unresolved-import" };
|
|
12826
|
+
const wanted = imported === "*" ? memberName : imported;
|
|
12827
|
+
if (wanted === void 0) return { arity: null, src: null, reason: "not-a-function" };
|
|
12828
|
+
const found = exportedBinding(next, wanted, seen);
|
|
12829
|
+
if (!found) return { arity: null, src: null, reason: "not-found" };
|
|
12830
|
+
return fromBinding(found.mod, found.binding, void 0, hops + 1, seen);
|
|
12831
|
+
}
|
|
12832
|
+
const init = binding.init;
|
|
12833
|
+
if (!init) return { arity: null, src: null, reason: "not-a-function" };
|
|
12834
|
+
if (init.type === "Identifier") {
|
|
12835
|
+
const hit = lookup(scopeAt(mod.scopes, init.start), init.name);
|
|
12836
|
+
if (!hit) return { arity: null, src: null, reason: "not-found" };
|
|
12837
|
+
return fromBinding(mod, hit.binding, void 0, hops + 1, seen);
|
|
12838
|
+
}
|
|
12839
|
+
if (init.type === "ComputedMemberExpression" || init.computed === true && init.type.endsWith("MemberExpression")) {
|
|
12840
|
+
return { arity: null, src: null, reason: "computed" };
|
|
12841
|
+
}
|
|
12842
|
+
if (init.type === "StaticMemberExpression" || init.type === "MemberExpression") {
|
|
12843
|
+
const obj = init.object;
|
|
12844
|
+
const prop = init.property;
|
|
12845
|
+
if (obj?.type === "Identifier" && prop?.type === "Identifier") {
|
|
12846
|
+
const hit = lookup(scopeAt(mod.scopes, init.start), obj.name);
|
|
12847
|
+
if (!hit) return { arity: null, src: null, reason: "not-found" };
|
|
12848
|
+
return fromBinding(mod, hit.binding, prop.name, hops + 1, seen);
|
|
12849
|
+
}
|
|
12850
|
+
return { arity: null, src: null, reason: "computed" };
|
|
12851
|
+
}
|
|
12852
|
+
if (!FUNCTION_TYPES.has(init.type)) return { arity: null, src: null, reason: "not-a-function" };
|
|
12853
|
+
const { arity, reason } = functionArity(init);
|
|
12854
|
+
return { arity, src: mod.src.slice(init.start, init.end), ...reason ? { reason } : {} };
|
|
12855
|
+
};
|
|
12856
|
+
return {
|
|
12857
|
+
register(file) {
|
|
12858
|
+
const mod = analyseModuleFile(file);
|
|
12859
|
+
if (!mod) return false;
|
|
12860
|
+
if (mod.src === src) return true;
|
|
12861
|
+
const prior = registered.get(mod.src);
|
|
12862
|
+
if (prior !== void 0 && prior.file !== mod.file) {
|
|
12863
|
+
ambiguous.add(mod.src);
|
|
12864
|
+
registered.delete(mod.src);
|
|
12865
|
+
return false;
|
|
12866
|
+
}
|
|
12867
|
+
if (!ambiguous.has(mod.src)) registered.set(mod.src, mod);
|
|
12868
|
+
return !ambiguous.has(mod.src);
|
|
12869
|
+
},
|
|
12870
|
+
resolve(exprSrc, offset, offsetSrc) {
|
|
12871
|
+
const text = exprSrc.trim();
|
|
12872
|
+
const ident = IDENT_RE.test(text) ? text : null;
|
|
12873
|
+
const member = ident ? null : MEMBER_RE.exec(text);
|
|
12874
|
+
if (!ident && !member) return null;
|
|
12875
|
+
const mod = offsetSrc === src ? entry : registered.get(offsetSrc);
|
|
12876
|
+
if (!mod) {
|
|
12877
|
+
return { arity: null, src: null, reason: ambiguous.has(offsetSrc) ? "ambiguous-source" : "foreign-source" };
|
|
12878
|
+
}
|
|
12879
|
+
const name = ident ?? member[1];
|
|
12880
|
+
const memberName = ident ? void 0 : member[2];
|
|
12881
|
+
const hit = lookup(scopeAt(mod.scopes, offset), name);
|
|
12882
|
+
if (!hit) return { arity: null, src: null, reason: "not-found" };
|
|
12883
|
+
return fromBinding(mod, hit.binding, memberName, 0, /* @__PURE__ */ new Set());
|
|
12884
|
+
}
|
|
12885
|
+
};
|
|
12886
|
+
}
|
|
12887
|
+
|
|
12888
|
+
// src/plugin/free-identifiers.ts
|
|
12889
|
+
import { parseSync as parseSync3 } from "oxc-parser";
|
|
12890
|
+
var isNode = (v2) => typeof v2 === "object" && v2 !== null && typeof v2.type === "string";
|
|
12891
|
+
var TYPE_KEYS = /* @__PURE__ */ new Set([
|
|
12892
|
+
"typeAnnotation",
|
|
12893
|
+
"returnType",
|
|
12894
|
+
"typeParameters",
|
|
12895
|
+
"typeArguments",
|
|
12896
|
+
"superTypeArguments",
|
|
12897
|
+
"typeName",
|
|
12898
|
+
"implements",
|
|
12899
|
+
"extends",
|
|
12900
|
+
"accessibility"
|
|
12901
|
+
]);
|
|
12902
|
+
var TS_VALUE_WRAPPERS = /* @__PURE__ */ new Set([
|
|
12903
|
+
"TSAsExpression",
|
|
12904
|
+
"TSSatisfiesExpression",
|
|
12905
|
+
"TSNonNullExpression",
|
|
12906
|
+
"TSInstantiationExpression",
|
|
12907
|
+
"TSTypeAssertion"
|
|
12908
|
+
]);
|
|
12909
|
+
var HOST_GLOBALS = /* @__PURE__ */ new Set([
|
|
12910
|
+
...Object.getOwnPropertyNames(globalThis),
|
|
12911
|
+
"undefined",
|
|
12912
|
+
"NaN",
|
|
12913
|
+
"Infinity",
|
|
12914
|
+
"globalThis",
|
|
12915
|
+
"arguments",
|
|
12916
|
+
"window",
|
|
12917
|
+
"document",
|
|
12918
|
+
"self",
|
|
12919
|
+
"navigator",
|
|
12920
|
+
"location",
|
|
12921
|
+
"top",
|
|
12922
|
+
"parent",
|
|
12923
|
+
"require",
|
|
12924
|
+
"module",
|
|
12925
|
+
"exports",
|
|
12926
|
+
"__dirname",
|
|
12927
|
+
"__filename"
|
|
12928
|
+
]);
|
|
12929
|
+
function bindPattern(pattern, out) {
|
|
12930
|
+
if (!isNode(pattern)) return;
|
|
12931
|
+
switch (pattern.type) {
|
|
12932
|
+
case "Identifier":
|
|
12933
|
+
case "BindingIdentifier":
|
|
12934
|
+
out.add(String(pattern.name));
|
|
12935
|
+
return;
|
|
12936
|
+
case "ObjectPattern":
|
|
12937
|
+
for (const prop of pattern.properties ?? []) {
|
|
12938
|
+
if (!isNode(prop)) continue;
|
|
12939
|
+
if (prop.type === "RestElement" || prop.type === "BindingRestElement") bindPattern(prop.argument, out);
|
|
12940
|
+
else bindPattern(prop.value, out);
|
|
12941
|
+
}
|
|
12942
|
+
return;
|
|
12943
|
+
case "ArrayPattern":
|
|
12944
|
+
for (const el of pattern.elements ?? []) bindPattern(el, out);
|
|
12945
|
+
return;
|
|
12946
|
+
case "AssignmentPattern":
|
|
12947
|
+
bindPattern(pattern.left, out);
|
|
12948
|
+
return;
|
|
12949
|
+
case "RestElement":
|
|
12950
|
+
case "BindingRestElement":
|
|
12951
|
+
bindPattern(pattern.argument, out);
|
|
12952
|
+
return;
|
|
12953
|
+
case "TSParameterProperty":
|
|
12954
|
+
bindPattern(pattern.parameter, out);
|
|
12955
|
+
return;
|
|
12956
|
+
default:
|
|
12957
|
+
return;
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
function collectBindings(node2, out) {
|
|
12961
|
+
if (Array.isArray(node2)) {
|
|
12962
|
+
for (const child of node2) collectBindings(child, out);
|
|
12963
|
+
return;
|
|
12964
|
+
}
|
|
12965
|
+
if (!isNode(node2)) return;
|
|
12966
|
+
switch (node2.type) {
|
|
12967
|
+
case "ImportSpecifier":
|
|
12968
|
+
case "ImportDefaultSpecifier":
|
|
12969
|
+
case "ImportNamespaceSpecifier":
|
|
12970
|
+
if (isNode(node2.local)) out.add(String(node2.local.name));
|
|
12971
|
+
break;
|
|
12972
|
+
case "VariableDeclarator":
|
|
12973
|
+
bindPattern(node2.id, out);
|
|
12974
|
+
break;
|
|
12975
|
+
case "FunctionDeclaration":
|
|
12976
|
+
case "FunctionExpression":
|
|
12977
|
+
case "TSDeclareFunction":
|
|
12978
|
+
case "ArrowFunctionExpression":
|
|
12979
|
+
if (isNode(node2.id)) out.add(String(node2.id.name));
|
|
12980
|
+
for (const p of node2.params?.items ?? node2.params ?? []) bindPattern(p, out);
|
|
12981
|
+
if (isNode(node2.params) && node2.params.rest) bindPattern(node2.params.rest, out);
|
|
12982
|
+
break;
|
|
12983
|
+
case "ClassDeclaration":
|
|
12984
|
+
case "ClassExpression":
|
|
12985
|
+
if (isNode(node2.id)) out.add(String(node2.id.name));
|
|
12986
|
+
break;
|
|
12987
|
+
case "CatchClause":
|
|
12988
|
+
if (node2.param) {
|
|
12989
|
+
bindPattern(isNode(node2.param) && node2.param.pattern ? node2.param.pattern : node2.param, out);
|
|
12990
|
+
}
|
|
12991
|
+
break;
|
|
12992
|
+
default:
|
|
12993
|
+
break;
|
|
12994
|
+
}
|
|
12995
|
+
for (const key of Object.keys(node2)) {
|
|
12996
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
12997
|
+
collectBindings(node2[key], out);
|
|
12998
|
+
}
|
|
12999
|
+
}
|
|
13000
|
+
var FUNCTION_TYPES2 = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression", "FunctionDeclaration"]);
|
|
13001
|
+
function describeTopLevel(stmt) {
|
|
13002
|
+
const declared = /* @__PURE__ */ new Set();
|
|
13003
|
+
let target = stmt;
|
|
13004
|
+
let prefix = "";
|
|
13005
|
+
let exported = false;
|
|
13006
|
+
if (stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration") {
|
|
13007
|
+
exported = true;
|
|
13008
|
+
prefix = "export ";
|
|
13009
|
+
if (!isNode(stmt.declaration)) return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}{ \u2026 }` };
|
|
13010
|
+
target = stmt.declaration;
|
|
13011
|
+
}
|
|
13012
|
+
if (target.type === "VariableDeclaration") {
|
|
13013
|
+
const decls = (target.declarations ?? []).filter(isNode);
|
|
13014
|
+
for (const d of decls) bindPattern(d.id, declared);
|
|
13015
|
+
const declaresOnlyFunctions = decls.length > 0 && decls.every((d) => isNode(d.init) && FUNCTION_TYPES2.has(d.init.type));
|
|
13016
|
+
const name = [...declared][0];
|
|
13017
|
+
return {
|
|
13018
|
+
declared,
|
|
13019
|
+
exported,
|
|
13020
|
+
declaresOnlyFunctions,
|
|
13021
|
+
label: name ? `${prefix}${String(target.kind)} ${name}` : null
|
|
13022
|
+
};
|
|
13023
|
+
}
|
|
13024
|
+
if (target.type === "FunctionDeclaration" && isNode(target.id)) {
|
|
13025
|
+
declared.add(String(target.id.name));
|
|
13026
|
+
return { declared, exported, declaresOnlyFunctions: true, label: `${prefix}function ${String(target.id.name)}` };
|
|
13027
|
+
}
|
|
13028
|
+
if (target.type === "ClassDeclaration" && isNode(target.id)) {
|
|
13029
|
+
declared.add(String(target.id.name));
|
|
13030
|
+
return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}class ${String(target.id.name)}` };
|
|
13031
|
+
}
|
|
13032
|
+
return { declared, exported, declaresOnlyFunctions: false, label: null };
|
|
13033
|
+
}
|
|
13034
|
+
function findFreeIdentifiers(code, filename) {
|
|
13035
|
+
let parsed;
|
|
13036
|
+
try {
|
|
13037
|
+
parsed = parseSync3(filename, code);
|
|
13038
|
+
} catch {
|
|
13039
|
+
return [];
|
|
13040
|
+
}
|
|
13041
|
+
if (parsed.errors.length > 0) return [];
|
|
13042
|
+
const bound = /* @__PURE__ */ new Set();
|
|
13043
|
+
collectBindings(parsed.program.body, bound);
|
|
13044
|
+
const lineStarts = [0];
|
|
13045
|
+
for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) lineStarts.push(i + 1);
|
|
13046
|
+
const positionOf = (offset) => {
|
|
13047
|
+
let lo = 0;
|
|
13048
|
+
let hi = lineStarts.length - 1;
|
|
13049
|
+
while (lo < hi) {
|
|
13050
|
+
const mid = lo + hi + 1 >> 1;
|
|
13051
|
+
if (lineStarts[mid] <= offset) lo = mid;
|
|
13052
|
+
else hi = mid - 1;
|
|
13053
|
+
}
|
|
13054
|
+
return { line: lo + 1, column: offset - lineStarts[lo] + 1 };
|
|
13055
|
+
};
|
|
13056
|
+
const statements = parsed.program.body.filter(isNode);
|
|
13057
|
+
const tops = statements.map(describeTopLevel);
|
|
13058
|
+
const candidates = [];
|
|
13059
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13060
|
+
const readIn = /* @__PURE__ */ new Map();
|
|
13061
|
+
const visit = (node2, stmt, insideFunction) => {
|
|
13062
|
+
if (Array.isArray(node2)) {
|
|
13063
|
+
for (const child of node2) visit(child, stmt, insideFunction);
|
|
13064
|
+
return;
|
|
13065
|
+
}
|
|
13066
|
+
if (!isNode(node2)) return;
|
|
13067
|
+
const type = node2.type;
|
|
13068
|
+
if (type === "ImportDeclaration" || type === "ExportAllDeclaration") return;
|
|
13069
|
+
if (type === "MetaProperty" || type === "PrivateIdentifier") return;
|
|
13070
|
+
if (type === "ExportNamedDeclaration" && node2.source) return;
|
|
13071
|
+
if (type.startsWith("TS")) {
|
|
13072
|
+
if (TS_VALUE_WRAPPERS.has(type)) visit(node2.expression, stmt, insideFunction);
|
|
13073
|
+
return;
|
|
13074
|
+
}
|
|
13075
|
+
if (type === "Identifier" || type === "IdentifierReference") {
|
|
13076
|
+
const name = String(node2.name);
|
|
13077
|
+
let readers = readIn.get(name);
|
|
13078
|
+
if (!readers) readIn.set(name, readers = /* @__PURE__ */ new Set());
|
|
13079
|
+
readers.add(stmt);
|
|
13080
|
+
if (bound.has(name) || HOST_GLOBALS.has(name)) return;
|
|
13081
|
+
const offset = typeof node2.start === "number" ? node2.start : 0;
|
|
13082
|
+
const { line, column } = positionOf(offset);
|
|
13083
|
+
const key = `${name}@${line}:${column}`;
|
|
13084
|
+
if (seen.has(key)) return;
|
|
13085
|
+
seen.add(key);
|
|
13086
|
+
candidates.push({ name, line, column, enclosing: tops[stmt].label, stmt, insideFunction });
|
|
13087
|
+
return;
|
|
13088
|
+
}
|
|
13089
|
+
const nowInsideFunction = insideFunction || FUNCTION_TYPES2.has(type);
|
|
13090
|
+
for (const key of Object.keys(node2)) {
|
|
13091
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
13092
|
+
if (TYPE_KEYS.has(key)) continue;
|
|
13093
|
+
if (key === "key" && node2.computed !== true) continue;
|
|
13094
|
+
if (key === "property" && node2.computed !== true) continue;
|
|
13095
|
+
if (key === "label") continue;
|
|
13096
|
+
if (key === "exported") continue;
|
|
13097
|
+
if (key === "imported") continue;
|
|
13098
|
+
visit(node2[key], stmt, nowInsideFunction);
|
|
13099
|
+
}
|
|
13100
|
+
};
|
|
13101
|
+
for (let i = 0; i < statements.length; i++) visit(statements[i], i, false);
|
|
13102
|
+
return candidates.filter((c) => {
|
|
13103
|
+
const top = tops[c.stmt];
|
|
13104
|
+
if (!c.insideFunction || top.exported || !top.declaresOnlyFunctions || top.declared.size === 0) return true;
|
|
13105
|
+
for (const name of top.declared) {
|
|
13106
|
+
const readers = readIn.get(name);
|
|
13107
|
+
if (readers && [...readers].some((i) => i !== c.stmt)) return true;
|
|
13108
|
+
}
|
|
13109
|
+
return false;
|
|
13110
|
+
}).map(({ name, line, column, enclosing }) => ({ name, line, column, enclosing }));
|
|
13111
|
+
}
|
|
13112
|
+
|
|
13113
|
+
// src/compiler/module-hoist.ts
|
|
13114
|
+
var marker = (id) => `/*@pmh:${id}@*/`;
|
|
13115
|
+
var MARKER_RE = /\/\*@pmh:(\d+)@\*\/\n?/g;
|
|
13116
|
+
var HOIST_MARKER_PROBE = /\/\*@pmh:\d+@\*\//;
|
|
13117
|
+
var DECL_RE = /^(?:const|let|var|function\*?)\s+([A-Za-z_$][\w$]*)/;
|
|
13118
|
+
var ALWAYS_LOCAL = /* @__PURE__ */ new Set(["_map", "_k", "_env"]);
|
|
13119
|
+
function declaredNames(text) {
|
|
13120
|
+
const out = [];
|
|
13121
|
+
for (const line of text.split("\n")) {
|
|
13122
|
+
const m = DECL_RE.exec(line);
|
|
13123
|
+
if (m) out.push(m[1]);
|
|
13124
|
+
}
|
|
13125
|
+
return out;
|
|
13126
|
+
}
|
|
13127
|
+
function createModuleHoist() {
|
|
13128
|
+
const texts = [];
|
|
13129
|
+
const claim = (decls) => decls.map((text) => {
|
|
13130
|
+
const id = texts.length;
|
|
13131
|
+
texts.push(text);
|
|
13132
|
+
return marker(id);
|
|
13133
|
+
});
|
|
13134
|
+
const finalize = () => {
|
|
13135
|
+
const nameTexts = /* @__PURE__ */ new Map();
|
|
13136
|
+
const textCount = /* @__PURE__ */ new Map();
|
|
13137
|
+
const textNames = /* @__PURE__ */ new Map();
|
|
13138
|
+
const order = [];
|
|
13139
|
+
for (const text of texts) {
|
|
13140
|
+
const seen = textCount.get(text);
|
|
13141
|
+
textCount.set(text, (seen ?? 0) + 1);
|
|
13142
|
+
if (seen === void 0) {
|
|
13143
|
+
order.push(text);
|
|
13144
|
+
const names = declaredNames(text);
|
|
13145
|
+
textNames.set(text, names);
|
|
13146
|
+
for (const n of names) {
|
|
13147
|
+
const s = nameTexts.get(n);
|
|
13148
|
+
if (s) s.add(text);
|
|
13149
|
+
else nameTexts.set(n, /* @__PURE__ */ new Set([text]));
|
|
13150
|
+
}
|
|
13151
|
+
}
|
|
13152
|
+
}
|
|
13153
|
+
const hoisted = new Set(
|
|
13154
|
+
order.filter(
|
|
13155
|
+
(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)
|
|
13156
|
+
)
|
|
13157
|
+
);
|
|
13158
|
+
for (; ; ) {
|
|
13159
|
+
const localNames = [];
|
|
13160
|
+
for (const [n, ts] of nameTexts) {
|
|
13161
|
+
if (ts.size !== 1 || !hoisted.has([...ts][0])) localNames.push(n);
|
|
13162
|
+
}
|
|
13163
|
+
let changed = false;
|
|
13164
|
+
for (const text of [...hoisted]) {
|
|
13165
|
+
if (localNames.some((n) => new RegExp(`\\b${escapeName(n)}\\b`).test(text))) {
|
|
13166
|
+
hoisted.delete(text);
|
|
13167
|
+
changed = true;
|
|
13168
|
+
}
|
|
13169
|
+
}
|
|
13170
|
+
if (!changed) break;
|
|
13171
|
+
}
|
|
13172
|
+
const preludeTexts = order.filter((t) => hoisted.has(t));
|
|
13173
|
+
const prelude = preludeTexts.length === 0 ? "" : `${preludeTexts.join("\n")}
|
|
13174
|
+
`;
|
|
13175
|
+
const removed = texts.reduce((n, t) => hoisted.has(t) ? n + t.length + 1 : n, 0);
|
|
13176
|
+
return {
|
|
13177
|
+
prelude,
|
|
13178
|
+
savedBytes: removed - prelude.length,
|
|
13179
|
+
hoistedNames: preludeTexts.flatMap((t) => textNames.get(t)),
|
|
13180
|
+
resolve: (src) => src.replace(MARKER_RE, (m, idStr) => {
|
|
13181
|
+
const text = texts[Number(idStr)];
|
|
13182
|
+
if (text === void 0) throw new Error(`parseman: unknown hoist marker ${m}`);
|
|
13183
|
+
if (hoisted.has(text)) return "";
|
|
13184
|
+
return m.endsWith("\n") ? `${text}
|
|
13185
|
+
` : text;
|
|
13186
|
+
})
|
|
13187
|
+
};
|
|
13188
|
+
};
|
|
13189
|
+
return { claim, finalize };
|
|
13190
|
+
}
|
|
13191
|
+
function escapeName(n) {
|
|
13192
|
+
return n.replace(/\$/g, "\\$");
|
|
13193
|
+
}
|
|
13194
|
+
|
|
12475
13195
|
// src/plugin/index.ts
|
|
12476
13196
|
import { createHash } from "node:crypto";
|
|
12477
13197
|
var PARSEMAN_MODULE = "parseman";
|
|
@@ -12497,37 +13217,37 @@ function getCompiledResolver() {
|
|
|
12497
13217
|
}
|
|
12498
13218
|
var PRIVATE_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
|
|
12499
13219
|
function nearestPackageRoot(file) {
|
|
12500
|
-
let dir =
|
|
13220
|
+
let dir = path2.dirname(file);
|
|
12501
13221
|
for (; ; ) {
|
|
12502
13222
|
try {
|
|
12503
|
-
if (
|
|
13223
|
+
if (fs2.statSync(path2.join(dir, "package.json")).isFile()) return fs2.realpathSync(dir);
|
|
12504
13224
|
} catch {
|
|
12505
13225
|
}
|
|
12506
|
-
const parent =
|
|
13226
|
+
const parent = path2.dirname(dir);
|
|
12507
13227
|
if (parent === dir) return null;
|
|
12508
13228
|
dir = parent;
|
|
12509
13229
|
}
|
|
12510
13230
|
}
|
|
12511
13231
|
function isWithinPackageRoot(file, root) {
|
|
12512
|
-
const relative2 =
|
|
12513
|
-
return relative2 !== ".." && !relative2.startsWith(`..${
|
|
13232
|
+
const relative2 = path2.relative(root, file);
|
|
13233
|
+
return relative2 !== ".." && !relative2.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative2);
|
|
12514
13234
|
}
|
|
12515
13235
|
function resolvePrivateSourceModule(from, specifier) {
|
|
12516
|
-
if (!specifier.startsWith(".") && !
|
|
13236
|
+
if (!specifier.startsWith(".") && !path2.isAbsolute(specifier)) return null;
|
|
12517
13237
|
const packageRoot = nearestPackageRoot(from);
|
|
12518
13238
|
if (!packageRoot) return null;
|
|
12519
|
-
const base =
|
|
12520
|
-
const ext =
|
|
13239
|
+
const base = path2.resolve(path2.dirname(from), specifier);
|
|
13240
|
+
const ext = path2.extname(base);
|
|
12521
13241
|
const candidates = [
|
|
12522
13242
|
base,
|
|
12523
13243
|
...ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base.slice(0, -ext.length)}${next}`) : [],
|
|
12524
13244
|
...!ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base}${next}`) : [],
|
|
12525
|
-
...!ext ? ["index.ts", "index.tsx", "index.mts", "index.cts"].map((next) =>
|
|
13245
|
+
...!ext ? ["index.ts", "index.tsx", "index.mts", "index.cts"].map((next) => path2.join(base, next)) : []
|
|
12526
13246
|
];
|
|
12527
13247
|
for (const candidate of candidates) {
|
|
12528
13248
|
try {
|
|
12529
|
-
if (!
|
|
12530
|
-
const real =
|
|
13249
|
+
if (!fs2.statSync(candidate).isFile()) continue;
|
|
13250
|
+
const real = fs2.realpathSync(candidate);
|
|
12531
13251
|
if (isWithinPackageRoot(real, packageRoot)) return real;
|
|
12532
13252
|
} catch {
|
|
12533
13253
|
}
|
|
@@ -12535,7 +13255,7 @@ function resolvePrivateSourceModule(from, specifier) {
|
|
|
12535
13255
|
return null;
|
|
12536
13256
|
}
|
|
12537
13257
|
function resolveGrammarModule(from, specifier) {
|
|
12538
|
-
if (PRIVATE_SOURCE_EXTENSIONS.has(
|
|
13258
|
+
if (PRIVATE_SOURCE_EXTENSIONS.has(path2.extname(specifier))) {
|
|
12539
13259
|
const file2 = resolvePrivateSourceModule(from, specifier);
|
|
12540
13260
|
return file2 ? { file: file2, source: true } : null;
|
|
12541
13261
|
}
|
|
@@ -12556,7 +13276,7 @@ var _moduleParseCache = /* @__PURE__ */ new Map();
|
|
|
12556
13276
|
function parseModuleCached(filePath) {
|
|
12557
13277
|
let mtimeMs;
|
|
12558
13278
|
try {
|
|
12559
|
-
mtimeMs =
|
|
13279
|
+
mtimeMs = fs2.statSync(filePath).mtimeMs;
|
|
12560
13280
|
} catch {
|
|
12561
13281
|
return null;
|
|
12562
13282
|
}
|
|
@@ -12564,8 +13284,8 @@ function parseModuleCached(filePath) {
|
|
|
12564
13284
|
if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
|
|
12565
13285
|
let parsed = null;
|
|
12566
13286
|
try {
|
|
12567
|
-
const src =
|
|
12568
|
-
const r =
|
|
13287
|
+
const src = fs2.readFileSync(filePath, "utf8");
|
|
13288
|
+
const r = parseSync4(filePath, src);
|
|
12569
13289
|
if (r.errors.length === 0) parsed = { body: r.program.body, src };
|
|
12570
13290
|
} catch {
|
|
12571
13291
|
}
|
|
@@ -12693,7 +13413,7 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
|
|
|
12693
13413
|
if (_sourceLowering.has(file)) return null;
|
|
12694
13414
|
let mtimeMs;
|
|
12695
13415
|
try {
|
|
12696
|
-
mtimeMs =
|
|
13416
|
+
mtimeMs = fs2.statSync(file).mtimeMs;
|
|
12697
13417
|
} catch {
|
|
12698
13418
|
return null;
|
|
12699
13419
|
}
|
|
@@ -12703,10 +13423,10 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
|
|
|
12703
13423
|
_sourceLowering.add(file);
|
|
12704
13424
|
let parsedModule = null;
|
|
12705
13425
|
try {
|
|
12706
|
-
const source =
|
|
13426
|
+
const source = fs2.readFileSync(file, "utf8");
|
|
12707
13427
|
const transformed = transformMacro(source, file, moduleAliases, warnUnloweredRegex, recovery);
|
|
12708
13428
|
if (!transformed || transformed.warnings.length > 0) return null;
|
|
12709
|
-
const parsed =
|
|
13429
|
+
const parsed = parseSync4(file, transformed.code);
|
|
12710
13430
|
parsedModule = parsed.errors.length === 0 ? { body: parsed.program.body, src: transformed.code } : null;
|
|
12711
13431
|
return parsedModule;
|
|
12712
13432
|
} catch {
|
|
@@ -12717,9 +13437,20 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
|
|
|
12717
13437
|
}
|
|
12718
13438
|
}
|
|
12719
13439
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false, grammarCoverage = false) {
|
|
13440
|
+
const depth = degradationCaptureDepth();
|
|
13441
|
+
try {
|
|
13442
|
+
return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage);
|
|
13443
|
+
} finally {
|
|
13444
|
+
setReducerResolver(null);
|
|
13445
|
+
endLoweringCapture();
|
|
13446
|
+
endInlineCapCapture();
|
|
13447
|
+
for (const d of unwindDegradationCapture(depth)) console.warn(formatDegradation(d));
|
|
13448
|
+
}
|
|
13449
|
+
}
|
|
13450
|
+
function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage) {
|
|
12720
13451
|
let result;
|
|
12721
13452
|
try {
|
|
12722
|
-
result =
|
|
13453
|
+
result = parseSync4(id, code);
|
|
12723
13454
|
} catch {
|
|
12724
13455
|
return null;
|
|
12725
13456
|
}
|
|
@@ -12776,6 +13507,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12776
13507
|
}
|
|
12777
13508
|
}
|
|
12778
13509
|
}
|
|
13510
|
+
const reducerResolver = createReducerResolver(id, body, code);
|
|
13511
|
+
setReducerResolver(reducerResolver, code);
|
|
12779
13512
|
const topLevelFunction = (moduleBody, name) => {
|
|
12780
13513
|
for (const st of moduleBody) {
|
|
12781
13514
|
const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
|
|
@@ -12870,13 +13603,18 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12870
13603
|
scope: factoryScope,
|
|
12871
13604
|
imported: true
|
|
12872
13605
|
});
|
|
13606
|
+
reducerResolver.register(file);
|
|
12873
13607
|
}
|
|
12874
13608
|
const scope = /* @__PURE__ */ new Map();
|
|
12875
13609
|
const replacements = [];
|
|
12876
13610
|
const warnings = [];
|
|
13611
|
+
const moduleHoist = grammarCoverage ? void 0 : createModuleHoist();
|
|
12877
13612
|
beginLoweringCapture();
|
|
13613
|
+
beginInlineCapCapture();
|
|
13614
|
+
beginDegradationCapture();
|
|
12878
13615
|
let anyUnresolved = false;
|
|
12879
13616
|
let keepMacroImport = false;
|
|
13617
|
+
const exportedFactories = [];
|
|
12880
13618
|
let runtimeComposeFallback = false;
|
|
12881
13619
|
let ruleMapHolderCounter = 0;
|
|
12882
13620
|
const lineOf = (pos) => {
|
|
@@ -12990,7 +13728,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12990
13728
|
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
12991
13729
|
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
12992
13730
|
).join(", ")}])`;
|
|
12993
|
-
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: [] }`;
|
|
13731
|
+
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: [] }`;
|
|
12994
13732
|
};
|
|
12995
13733
|
const isSpread = (it) => "__spreadLocal" in it;
|
|
12996
13734
|
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
@@ -13001,9 +13739,19 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13001
13739
|
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})`;
|
|
13002
13740
|
const withGrammarReflection = (grammarExpr, reflection) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`;
|
|
13003
13741
|
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 })`;
|
|
13004
|
-
const emittedCoverageDefinitions = (source) => {
|
|
13742
|
+
const emittedCoverageDefinitions = (source, where) => {
|
|
13005
13743
|
const ids = /* @__PURE__ */ new Set();
|
|
13006
13744
|
for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
|
|
13745
|
+
if (grammarCoverage && ids.size === 0) {
|
|
13746
|
+
recordDegradation({
|
|
13747
|
+
code: "coverage-definitions-unavailable",
|
|
13748
|
+
severity: "warn",
|
|
13749
|
+
where,
|
|
13750
|
+
subject: "generated coverage hooks",
|
|
13751
|
+
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",
|
|
13752
|
+
otherwise: "the emitted hook IDs would form the coverage denominator"
|
|
13753
|
+
});
|
|
13754
|
+
}
|
|
13007
13755
|
return [...ids].sort().map((id2) => ({
|
|
13008
13756
|
id: id2,
|
|
13009
13757
|
kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : id2.startsWith("dispatch:") ? "dispatch-arm" : "choice-arm"
|
|
@@ -13072,26 +13820,6 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13072
13820
|
}
|
|
13073
13821
|
return out;
|
|
13074
13822
|
};
|
|
13075
|
-
const carriedRuleMapsDetailed = (items) => {
|
|
13076
|
-
const maps = [];
|
|
13077
|
-
const opaque = [];
|
|
13078
|
-
const add = (it) => {
|
|
13079
|
-
if (isIR(it)) {
|
|
13080
|
-
maps.push(evalRuleMapIR(it.ir));
|
|
13081
|
-
return;
|
|
13082
|
-
}
|
|
13083
|
-
const o = it;
|
|
13084
|
-
opaque.push({
|
|
13085
|
-
ns: o.ns ?? "<unknown>",
|
|
13086
|
-
ruleNames: o.ruleFns instanceof Map ? [...o.ruleFns.keys()] : []
|
|
13087
|
-
});
|
|
13088
|
-
};
|
|
13089
|
-
for (const it of items) {
|
|
13090
|
-
if (isSpread(it)) for (const p of importedPieces(it.__spreadLocal) ?? []) add(p);
|
|
13091
|
-
else add(it);
|
|
13092
|
-
}
|
|
13093
|
-
return { maps, opaque };
|
|
13094
|
-
};
|
|
13095
13823
|
const materializeLeafCoverage = (items, localRules, localNs, composing, captureTerminals = false) => {
|
|
13096
13824
|
const sources = [];
|
|
13097
13825
|
const add = (item) => {
|
|
@@ -13242,16 +13970,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13242
13970
|
carried.push(...r.carried);
|
|
13243
13971
|
importedFactories.push(...r.importedFactories ?? []);
|
|
13244
13972
|
}
|
|
13245
|
-
const detailed = once(() => carriedRuleMapsDetailed(carried));
|
|
13246
|
-
runFusedGatingDiagnostic(
|
|
13247
|
-
() => detailed().maps,
|
|
13248
|
-
void 0,
|
|
13249
|
-
() => detailed().opaque
|
|
13250
|
-
);
|
|
13251
13973
|
const pieces = materializeCarried(carried, composing, false, cHostMode);
|
|
13252
13974
|
try {
|
|
13253
13975
|
return {
|
|
13254
|
-
replacement: emitFusedSource(pieces),
|
|
13976
|
+
replacement: emitFusedSource(pieces, moduleHoist),
|
|
13255
13977
|
carried,
|
|
13256
13978
|
...composing ? { trivia: composing } : {},
|
|
13257
13979
|
...importedFactories.length ? { importedFactories } : {}
|
|
@@ -13304,12 +14026,6 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13304
14026
|
}
|
|
13305
14027
|
try {
|
|
13306
14028
|
const localNs = nsFor(`composeLeaf${init.start}`);
|
|
13307
|
-
const detailedLeaf = once(() => carriedRuleMapsDetailed(carried));
|
|
13308
|
-
runFusedGatingDiagnostic(
|
|
13309
|
-
() => [...detailedLeaf().maps, [...localRules]],
|
|
13310
|
-
void 0,
|
|
13311
|
-
() => detailedLeaf().opaque
|
|
13312
|
-
);
|
|
13313
14029
|
const plainLocalPiece = compileLinkable([...localRules], localNs, { ...composing ? { trivia: composing } : {}, ...localScanSkip ? { scanSkip: localScanSkip } : {}, recovery });
|
|
13314
14030
|
if (!plainLocalPiece) {
|
|
13315
14031
|
warn(init.start, "composeLeaf(): local rules could not be statically compiled");
|
|
@@ -13325,9 +14041,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13325
14041
|
warn(init.start, "composeLeaf(): every pre-final grammar must explicitly prove recognition-only");
|
|
13326
14042
|
return null;
|
|
13327
14043
|
}
|
|
13328
|
-
const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
|
|
14044
|
+
const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece], moduleHoist));
|
|
13329
14045
|
return {
|
|
13330
|
-
replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)),
|
|
14046
|
+
replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement, `${id} composeLeaf()`)),
|
|
13331
14047
|
...importedFactories.length ? { importedFactories } : {}
|
|
13332
14048
|
};
|
|
13333
14049
|
} catch (e) {
|
|
@@ -13407,7 +14123,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13407
14123
|
const refEntry = scope.get(varName);
|
|
13408
14124
|
const refCombi = refEntry?.combi ?? null;
|
|
13409
14125
|
if (refCombi) {
|
|
13410
|
-
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage
|
|
14126
|
+
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage });
|
|
13411
14127
|
if (compiled2.inlineExpression === null) {
|
|
13412
14128
|
warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
|
|
13413
14129
|
continue;
|
|
@@ -13438,7 +14154,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13438
14154
|
const source = compiledRules.replacement ?? code.slice(init.start, init.end);
|
|
13439
14155
|
let replacement = withCoverageDefinitions(
|
|
13440
14156
|
source,
|
|
13441
|
-
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(source)
|
|
14157
|
+
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(source, `${id} rules()`)
|
|
13442
14158
|
);
|
|
13443
14159
|
if (compiledRules.replacement !== null) {
|
|
13444
14160
|
replacement = withHostMode(replacement, compiledRules.hostMode, compiledRules.hostBranchElided);
|
|
@@ -13463,7 +14179,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13463
14179
|
localComposedCarried.set(varName, fused.carried);
|
|
13464
14180
|
if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
|
|
13465
14181
|
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
13466
|
-
replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
|
|
14182
|
+
replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement, `${id} compose()`)) });
|
|
13467
14183
|
markUsedImportedFactories(fused.importedFactories);
|
|
13468
14184
|
continue;
|
|
13469
14185
|
}
|
|
@@ -13510,11 +14226,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13510
14226
|
scope.set(varName, staticValue);
|
|
13511
14227
|
continue;
|
|
13512
14228
|
}
|
|
13513
|
-
if (factoryDecls.has(varName))
|
|
14229
|
+
if (factoryDecls.has(varName)) {
|
|
14230
|
+
if (exportPrefix) exportedFactories.push({ name: varName, pos: init.start });
|
|
14231
|
+
continue;
|
|
14232
|
+
}
|
|
13514
14233
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
13515
14234
|
continue;
|
|
13516
14235
|
}
|
|
13517
|
-
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage
|
|
14236
|
+
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage });
|
|
13518
14237
|
if (compiled.inlineExpression === null) {
|
|
13519
14238
|
warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
|
|
13520
14239
|
continue;
|
|
@@ -13581,6 +14300,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13581
14300
|
for (const imp of macroImports) {
|
|
13582
14301
|
imp.fullyResolved = !anyUnresolved && !keepMacroImport;
|
|
13583
14302
|
}
|
|
14303
|
+
if (exportedFactories.length > 0 && macroImports.some((imp) => imp.fullyResolved)) {
|
|
14304
|
+
const which = exportedFactories.map((f) => ` - ${id}:${lineOf(f.pos)} \u2014 export const ${f.name}`).join("\n");
|
|
14305
|
+
throw new Error(
|
|
14306
|
+
`${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:
|
|
14307
|
+
${which}
|
|
14308
|
+
Drop the \`export\`. The factory is macro-only; \`rules(${exportedFactories[0].name})\` is lowered in place and needs no runtime value.`
|
|
14309
|
+
);
|
|
14310
|
+
}
|
|
13584
14311
|
const ms = new MagicString(code);
|
|
13585
14312
|
const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
|
|
13586
14313
|
const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
|
|
@@ -13629,8 +14356,21 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13629
14356
|
}
|
|
13630
14357
|
}
|
|
13631
14358
|
const applied = runtimeComposeFallback ? [] : replacements;
|
|
14359
|
+
const hoisted = applied.length > 0 ? moduleHoist?.finalize() : void 0;
|
|
13632
14360
|
for (const { start, end, replacement } of applied.slice().sort((a, b) => b.start - a.start)) {
|
|
13633
|
-
ms.overwrite(start, end, replacement);
|
|
14361
|
+
ms.overwrite(start, end, hoisted ? hoisted.resolve(replacement) : replacement);
|
|
14362
|
+
}
|
|
14363
|
+
if (hoisted !== void 0 && hoisted.prelude !== "") {
|
|
14364
|
+
const claiming = applied.filter((r) => HOIST_MARKER_PROBE.test(r.replacement));
|
|
14365
|
+
if (claiming.length === 0) {
|
|
14366
|
+
throw new Error(`${id} \u2014 internal: a module-hoist prelude was produced but no applied replacement carries a hoist marker`);
|
|
14367
|
+
}
|
|
14368
|
+
const firstStart = Math.min(...claiming.map((r) => r.start));
|
|
14369
|
+
const anchor = body.find((s) => s.start <= firstStart && firstStart < s.end);
|
|
14370
|
+
if (anchor === void 0) {
|
|
14371
|
+
throw new Error(`${id} \u2014 internal: no top-level statement encloses macro replacement at ${firstStart}`);
|
|
14372
|
+
}
|
|
14373
|
+
ms.appendLeft(anchor.start, hoisted.prelude);
|
|
13634
14374
|
}
|
|
13635
14375
|
if (applied.length > 0) {
|
|
13636
14376
|
ms.prepend(
|
|
@@ -13640,12 +14380,38 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
13640
14380
|
`
|
|
13641
14381
|
);
|
|
13642
14382
|
}
|
|
14383
|
+
if (applied.length > 0) {
|
|
14384
|
+
let sourceFree = null;
|
|
14385
|
+
const freeInSource = () => sourceFree ??= new Set(findFreeIdentifiers(code, id).map((f) => f.name));
|
|
14386
|
+
const free = findFreeIdentifiers(ms.toString(), id).filter((f) => !freeInSource().has(f.name));
|
|
14387
|
+
if (free.length > 0) {
|
|
14388
|
+
const macroProvided = free.filter((f) => allNames.has(f.name));
|
|
14389
|
+
const where = (f) => ` - ${id}:${f.line}:${f.column} \u2014 \`${f.name}\`` + (f.enclosing ? `, inside \`${f.enclosing}\`` : "");
|
|
14390
|
+
const cause = macroProvided.length > 0 ? `
|
|
14391
|
+
${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.` : "";
|
|
14392
|
+
throw new Error(
|
|
14393
|
+
`${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):
|
|
14394
|
+
${free.map(where).join("\n")}${cause}`
|
|
14395
|
+
);
|
|
14396
|
+
}
|
|
14397
|
+
}
|
|
14398
|
+
for (const line of formatInlineCapSites(endInlineCapCapture(), resolveInlineMax())) warnings.push(`${id}: ${line}`);
|
|
13643
14399
|
const unlowered = endLoweringCapture();
|
|
13644
14400
|
if (warnUnloweredRegex) {
|
|
13645
14401
|
for (const src of unlowered) {
|
|
13646
14402
|
warnings.push(`${id}: regex ${src} did not lower to a fast charCodeAt scan (RegExp.exec fallback)`);
|
|
13647
14403
|
}
|
|
13648
14404
|
}
|
|
14405
|
+
setReducerResolver(null);
|
|
14406
|
+
const degradations = endDegradationCapture();
|
|
14407
|
+
if (degradations.length > 0) {
|
|
14408
|
+
const lines = formatDegradations(degradations);
|
|
14409
|
+
if (resolveDegradationLevel() === "error") {
|
|
14410
|
+
throw new Error(`parseman: ${degradations.length} degraded compilation path(s) in ${id}
|
|
14411
|
+
${lines.join("\n")}`);
|
|
14412
|
+
}
|
|
14413
|
+
for (const l of lines) warnings.push(`${id}: ${l}`);
|
|
14414
|
+
}
|
|
13649
14415
|
return {
|
|
13650
14416
|
code: ms.toString(),
|
|
13651
14417
|
map: ms.generateMap({ hires: true }),
|