parseman 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/gating.d.ts +66 -1
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/combinators/case-fold.d.ts +39 -0
- package/dist/combinators/case-fold.d.ts.map +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts +38 -11
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +12 -3
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/not.d.ts.map +1 -1
- package/dist/combinators/peek.d.ts +27 -0
- package/dist/combinators/peek.d.ts.map +1 -0
- package/dist/combinators/repeat.d.ts +68 -3
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +23 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +40 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +5 -0
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/index.cjs +459 -86
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +459 -86
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs.map +1 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +538 -123
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +538 -123
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/ebnf.d.ts.map +1 -1
- package/dist/spec/index.cjs +79 -17
- package/dist/spec/index.cjs.map +2 -2
- package/dist/spec/index.js +79 -17
- package/dist/spec/index.js.map +2 -2
- package/dist/spec/model.d.ts +23 -3
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/spec/railroad.d.ts.map +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +5 -1
package/dist/plugin/index.cjs
CHANGED
|
@@ -42,6 +42,11 @@ var import_oxc_resolver = require("oxc-resolver");
|
|
|
42
42
|
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
43
43
|
|
|
44
44
|
// src/combinators/first-set.ts
|
|
45
|
+
function resolveNamedRef(p, resolve2) {
|
|
46
|
+
if (resolve2 === void 0) return void 0;
|
|
47
|
+
const name = p._ruleName;
|
|
48
|
+
return name === void 0 ? void 0 : resolve2(name);
|
|
49
|
+
}
|
|
45
50
|
function union(a, b) {
|
|
46
51
|
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
47
52
|
if (a.kind === "empty") return b;
|
|
@@ -79,10 +84,10 @@ function any() {
|
|
|
79
84
|
function empty() {
|
|
80
85
|
return { kind: "empty" };
|
|
81
86
|
}
|
|
82
|
-
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
87
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
83
88
|
if (seen.has(p)) return true;
|
|
84
89
|
seen.add(p);
|
|
85
|
-
const me = (c) => matchesEmpty(c, seen);
|
|
90
|
+
const me = (c) => matchesEmpty(c, seen, resolve2);
|
|
86
91
|
const d = p._def;
|
|
87
92
|
switch (d.tag) {
|
|
88
93
|
case "literal":
|
|
@@ -99,8 +104,16 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
99
104
|
case "many":
|
|
100
105
|
case "optional":
|
|
101
106
|
case "not":
|
|
107
|
+
case "peek":
|
|
102
108
|
return true;
|
|
103
109
|
// zero repetitions / absent / lookahead
|
|
110
|
+
// Default `sepBy` is `(item (sep item)*)?` — it MATCHES THE EMPTY STRING.
|
|
111
|
+
// Any `min >= 1` requires that many ITEMS, so it is nullable only when the
|
|
112
|
+
// item is. (Keying this off `min === 1` reported every `{ min: 2 }` list as
|
|
113
|
+
// nullable — safe, but wrong, and it put a bogus `nullable-prefix` note on
|
|
114
|
+
// the gating diagnostic for a list that can never match empty.)
|
|
115
|
+
case "sepBy":
|
|
116
|
+
return d.min >= 1 ? me(d.parser) : true;
|
|
104
117
|
case "oneOrMore":
|
|
105
118
|
return me(d.parser);
|
|
106
119
|
case "sequence":
|
|
@@ -124,7 +137,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
124
137
|
try {
|
|
125
138
|
return me(d.thunk());
|
|
126
139
|
} catch {
|
|
127
|
-
|
|
140
|
+
const t = resolveNamedRef(p, resolve2);
|
|
141
|
+
return t ? me(t) : true;
|
|
128
142
|
}
|
|
129
143
|
default:
|
|
130
144
|
return true;
|
|
@@ -133,18 +147,45 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
133
147
|
function isZeroWidthAssertion(p) {
|
|
134
148
|
return p._def.tag === "not";
|
|
135
149
|
}
|
|
150
|
+
function isPositiveLookahead(p) {
|
|
151
|
+
return p._def.tag === "peek";
|
|
152
|
+
}
|
|
153
|
+
function narrowBy(acc, constraint) {
|
|
154
|
+
if (constraint.kind === "any") return acc;
|
|
155
|
+
if (acc === null) return constraint;
|
|
156
|
+
if (acc.kind === "any") return constraint;
|
|
157
|
+
if (acc.kind === "empty" || constraint.kind === "empty") return { kind: "empty" };
|
|
158
|
+
const ranges = [];
|
|
159
|
+
for (const a of acc.ranges) for (const b of constraint.ranges) {
|
|
160
|
+
const lo = Math.max(a.lo, b.lo);
|
|
161
|
+
const hi = Math.min(a.hi, b.hi);
|
|
162
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
163
|
+
}
|
|
164
|
+
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
165
|
+
}
|
|
166
|
+
function applyAssertion(fs2, assertion) {
|
|
167
|
+
if (assertion === null) return fs2;
|
|
168
|
+
if (fs2.kind === "empty") return assertion;
|
|
169
|
+
return narrowBy(fs2, assertion) ?? fs2;
|
|
170
|
+
}
|
|
136
171
|
function sequenceFirstSet(parsers) {
|
|
137
172
|
let fs2 = empty();
|
|
173
|
+
let assertion = null;
|
|
138
174
|
for (const p of parsers) {
|
|
175
|
+
if (isPositiveLookahead(p)) {
|
|
176
|
+
assertion = narrowBy(assertion, p._meta.firstSet);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
139
179
|
if (!isZeroWidthAssertion(p)) fs2 = union(fs2, p._meta.firstSet);
|
|
140
|
-
if (!matchesEmpty(p)) return fs2;
|
|
180
|
+
if (!matchesEmpty(p)) return applyAssertion(fs2, assertion);
|
|
141
181
|
}
|
|
142
|
-
return fs2;
|
|
182
|
+
return applyAssertion(fs2, assertion);
|
|
143
183
|
}
|
|
144
|
-
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
184
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
145
185
|
if (seen.has(p)) return any();
|
|
146
186
|
seen.add(p);
|
|
147
|
-
const fs2 = (c) => firstSetOf(c, seen);
|
|
187
|
+
const fs2 = (c) => firstSetOf(c, seen, resolve2);
|
|
188
|
+
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve2);
|
|
148
189
|
const d = p._def;
|
|
149
190
|
switch (d.tag) {
|
|
150
191
|
case "literal":
|
|
@@ -156,7 +197,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
156
197
|
try {
|
|
157
198
|
return fs2(d.thunk());
|
|
158
199
|
} catch {
|
|
159
|
-
|
|
200
|
+
const t = resolveNamedRef(p, resolve2);
|
|
201
|
+
return t ? fs2(t) : any();
|
|
160
202
|
}
|
|
161
203
|
case "choice": {
|
|
162
204
|
let out = empty();
|
|
@@ -165,11 +207,20 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
165
207
|
}
|
|
166
208
|
case "sequence": {
|
|
167
209
|
let out = empty();
|
|
210
|
+
let assertion = null;
|
|
168
211
|
for (const term of d.parsers) {
|
|
212
|
+
if (isPositiveLookahead(term)) {
|
|
213
|
+
assertion = narrowBy(assertion, fs2(term));
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
169
216
|
if (!isZeroWidthAssertion(term)) out = union(out, fs2(term));
|
|
170
|
-
if (!
|
|
217
|
+
if (!empties(term)) return applyAssertion(out, assertion);
|
|
171
218
|
}
|
|
172
|
-
return out;
|
|
219
|
+
return applyAssertion(out, assertion);
|
|
220
|
+
}
|
|
221
|
+
case "peek": {
|
|
222
|
+
const inner = d.parser;
|
|
223
|
+
return empties(inner) ? any() : fs2(inner);
|
|
173
224
|
}
|
|
174
225
|
case "oneOrMore":
|
|
175
226
|
case "many":
|
|
@@ -185,6 +236,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
185
236
|
return fs2(d.parser);
|
|
186
237
|
case "sepBy":
|
|
187
238
|
return fs2(d.parser);
|
|
239
|
+
// both min 0 and min 1 start with the item
|
|
188
240
|
case "skip":
|
|
189
241
|
return fs2(d.main);
|
|
190
242
|
default:
|
|
@@ -906,9 +958,46 @@ function firstSetFromRegex(source) {
|
|
|
906
958
|
return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
|
|
907
959
|
}
|
|
908
960
|
|
|
961
|
+
// src/combinators/case-fold.ts
|
|
962
|
+
function canonical(ch2) {
|
|
963
|
+
const u = ch2.toUpperCase();
|
|
964
|
+
if (u.length !== 1) return ch2;
|
|
965
|
+
if (ch2.codePointAt(0) >= 128 && u.codePointAt(0) < 128) return ch2;
|
|
966
|
+
return u;
|
|
967
|
+
}
|
|
968
|
+
var foldIndex = null;
|
|
969
|
+
function buildFoldIndex() {
|
|
970
|
+
const byCanonical = /* @__PURE__ */ new Map();
|
|
971
|
+
for (let cp = 0; cp < 65536; cp++) {
|
|
972
|
+
if (cp >= 55296 && cp <= 57343) continue;
|
|
973
|
+
const key = canonical(String.fromCharCode(cp));
|
|
974
|
+
const bucket = byCanonical.get(key);
|
|
975
|
+
if (bucket) bucket.push(cp);
|
|
976
|
+
else byCanonical.set(key, [cp]);
|
|
977
|
+
}
|
|
978
|
+
const index = /* @__PURE__ */ new Map();
|
|
979
|
+
for (const members of byCanonical.values()) {
|
|
980
|
+
if (members.length < 2) continue;
|
|
981
|
+
for (const cp of members) index.set(cp, members);
|
|
982
|
+
}
|
|
983
|
+
return index;
|
|
984
|
+
}
|
|
985
|
+
function caseFoldVariants(cp) {
|
|
986
|
+
if (cp < 128) {
|
|
987
|
+
if (cp >= 65 && cp <= 90) return [cp, cp + 32];
|
|
988
|
+
if (cp >= 97 && cp <= 122) return [cp, cp - 32];
|
|
989
|
+
return [cp];
|
|
990
|
+
}
|
|
991
|
+
if (cp > 65535) return [cp];
|
|
992
|
+
foldIndex ??= buildFoldIndex();
|
|
993
|
+
return foldIndex.get(cp) ?? [cp];
|
|
994
|
+
}
|
|
995
|
+
|
|
909
996
|
// src/combinators/keywords.ts
|
|
910
|
-
function word(str,
|
|
911
|
-
|
|
997
|
+
function word(str, boundaryOrOpts, opts) {
|
|
998
|
+
const boundary = typeof boundaryOrOpts === "string" ? boundaryOrOpts : "_0-9A-Za-z";
|
|
999
|
+
const rest = typeof boundaryOrOpts === "object" && boundaryOrOpts !== null ? boundaryOrOpts : opts;
|
|
1000
|
+
return keywords([str], { ...rest, boundary });
|
|
912
1001
|
}
|
|
913
1002
|
function escapeRe(s) {
|
|
914
1003
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -917,7 +1006,7 @@ function keywords(words, opts = {}) {
|
|
|
917
1006
|
const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
|
|
918
1007
|
const alt = sorted.map(escapeRe).join("|");
|
|
919
1008
|
const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
|
|
920
|
-
const flags = opts.caseInsensitive ? "
|
|
1009
|
+
const flags = opts.caseInsensitive ? "iy" : "uy";
|
|
921
1010
|
const re = new RegExp(`(?:${alt})${boundary}`, flags);
|
|
922
1011
|
let firstSet2 = empty();
|
|
923
1012
|
for (const w of sorted) {
|
|
@@ -925,10 +1014,7 @@ function keywords(words, opts = {}) {
|
|
|
925
1014
|
const cp = w.codePointAt(0);
|
|
926
1015
|
firstSet2 = union(firstSet2, fromChar(cp));
|
|
927
1016
|
if (opts.caseInsensitive) {
|
|
928
|
-
const
|
|
929
|
-
const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
|
|
930
|
-
if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
|
|
931
|
-
if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
|
|
1017
|
+
for (const v2 of caseFoldVariants(cp)) firstSet2 = union(firstSet2, fromChar(v2));
|
|
932
1018
|
}
|
|
933
1019
|
}
|
|
934
1020
|
const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
|
|
@@ -1076,6 +1162,7 @@ function deriveExpected(c) {
|
|
|
1076
1162
|
case "oneOrMore":
|
|
1077
1163
|
case "transform":
|
|
1078
1164
|
case "not":
|
|
1165
|
+
case "peek":
|
|
1079
1166
|
return deriveExpected(def.parser);
|
|
1080
1167
|
case "lazy": {
|
|
1081
1168
|
const name = c._ruleName;
|
|
@@ -1546,6 +1633,13 @@ function rollbackTrivia(ctx, mark) {
|
|
|
1546
1633
|
rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
|
|
1547
1634
|
if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
|
|
1548
1635
|
}
|
|
1636
|
+
function saveLookaheadMark(ctx) {
|
|
1637
|
+
return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
|
|
1638
|
+
}
|
|
1639
|
+
function rollbackLookahead(ctx, mark) {
|
|
1640
|
+
rollbackTrivia(ctx, mark);
|
|
1641
|
+
if (ctx._probe) ctx._probe.best = mark.probeBest;
|
|
1642
|
+
}
|
|
1549
1643
|
function scanWithLabels(input, cur, ctx) {
|
|
1550
1644
|
const triviaP = ctx.trivia;
|
|
1551
1645
|
const spec = analyzeLabeledTrivia(triviaP);
|
|
@@ -1943,13 +2037,26 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1943
2037
|
}
|
|
1944
2038
|
return { value: result.value, end: result.span.end };
|
|
1945
2039
|
}
|
|
1946
|
-
function
|
|
2040
|
+
function resolveBounds(what, opts) {
|
|
2041
|
+
const min = opts.min ?? 0;
|
|
2042
|
+
const max = opts.max ?? Infinity;
|
|
2043
|
+
const bad = (msg) => {
|
|
2044
|
+
throw new RangeError(`parseman: ${what} ${msg}`);
|
|
2045
|
+
};
|
|
2046
|
+
if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
|
|
2047
|
+
if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
|
|
2048
|
+
if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
|
|
2049
|
+
return { min, max };
|
|
2050
|
+
}
|
|
2051
|
+
function many(combinator, opts = {}) {
|
|
2052
|
+
const { min, max } = resolveBounds("many()", opts);
|
|
2053
|
+
if (min >= 1) return atLeast(combinator, min, max);
|
|
1947
2054
|
const meta = {
|
|
1948
2055
|
firstSet: combinator._meta.firstSet,
|
|
1949
2056
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1950
2057
|
isTrivia: false
|
|
1951
2058
|
};
|
|
1952
|
-
const def = { tag: "many", parser: combinator, min: 0 };
|
|
2059
|
+
const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
|
|
1953
2060
|
let expected;
|
|
1954
2061
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1955
2062
|
return {
|
|
@@ -1959,7 +2066,9 @@ function many(combinator) {
|
|
|
1959
2066
|
parse(input, pos, ctx) {
|
|
1960
2067
|
const values = def.valueUnused ? void 0 : [];
|
|
1961
2068
|
let cur = pos;
|
|
2069
|
+
let count = 0;
|
|
1962
2070
|
while (cur < input.length) {
|
|
2071
|
+
if (count >= max) break;
|
|
1963
2072
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1964
2073
|
if (item === "stop") break;
|
|
1965
2074
|
if ("fail" in item) {
|
|
@@ -1970,23 +2079,28 @@ function many(combinator) {
|
|
|
1970
2079
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1971
2080
|
if (values !== void 0) values.push(error);
|
|
1972
2081
|
captureError(ctx, error);
|
|
2082
|
+
count++;
|
|
1973
2083
|
cur = end;
|
|
1974
2084
|
continue;
|
|
1975
2085
|
}
|
|
1976
2086
|
if (values !== void 0) values.push(item.value);
|
|
2087
|
+
count++;
|
|
1977
2088
|
cur = item.end;
|
|
1978
2089
|
}
|
|
1979
2090
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1980
2091
|
}
|
|
1981
2092
|
};
|
|
1982
2093
|
}
|
|
1983
|
-
function oneOrMore(combinator) {
|
|
2094
|
+
function oneOrMore(combinator, opts = {}) {
|
|
2095
|
+
return many(combinator, { ...opts, min: opts.min ?? 1 });
|
|
2096
|
+
}
|
|
2097
|
+
function atLeast(combinator, min, max) {
|
|
1984
2098
|
const meta = {
|
|
1985
2099
|
firstSet: combinator._meta.firstSet,
|
|
1986
2100
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1987
2101
|
isTrivia: false
|
|
1988
2102
|
};
|
|
1989
|
-
const def = { tag: "oneOrMore", parser: combinator, min:
|
|
2103
|
+
const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
|
|
1990
2104
|
let expected;
|
|
1991
2105
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1992
2106
|
return {
|
|
@@ -1998,7 +2112,19 @@ function oneOrMore(combinator) {
|
|
|
1998
2112
|
if (!first.ok) return first;
|
|
1999
2113
|
const values = def.valueUnused ? void 0 : [first.value];
|
|
2000
2114
|
let cur = first.span.end;
|
|
2115
|
+
let count = 1;
|
|
2116
|
+
while (count < min) {
|
|
2117
|
+
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2118
|
+
if (item === "stop" || "fail" in item) {
|
|
2119
|
+
expected ??= deriveExpected(combinator);
|
|
2120
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
2121
|
+
}
|
|
2122
|
+
if (values !== void 0) values.push(item.value);
|
|
2123
|
+
cur = item.end;
|
|
2124
|
+
count++;
|
|
2125
|
+
}
|
|
2001
2126
|
while (cur < input.length) {
|
|
2127
|
+
if (count >= max) break;
|
|
2002
2128
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2003
2129
|
if (item === "stop") break;
|
|
2004
2130
|
if ("fail" in item) {
|
|
@@ -2009,10 +2135,12 @@ function oneOrMore(combinator) {
|
|
|
2009
2135
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
2010
2136
|
if (values !== void 0) values.push(error);
|
|
2011
2137
|
captureError(ctx, error);
|
|
2138
|
+
count++;
|
|
2012
2139
|
cur = end;
|
|
2013
2140
|
continue;
|
|
2014
2141
|
}
|
|
2015
2142
|
if (values !== void 0) values.push(item.value);
|
|
2143
|
+
count++;
|
|
2016
2144
|
cur = item.end;
|
|
2017
2145
|
}
|
|
2018
2146
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
@@ -2042,17 +2170,33 @@ function optional(combinator) {
|
|
|
2042
2170
|
}
|
|
2043
2171
|
};
|
|
2044
2172
|
}
|
|
2045
|
-
function
|
|
2173
|
+
function oneOrMoreSep(combinator, separator, opts = {}) {
|
|
2174
|
+
return sepBy(combinator, separator, { ...opts, min: opts.min ?? 1 });
|
|
2175
|
+
}
|
|
2176
|
+
function sepBy(combinator, separator, opts = {}) {
|
|
2177
|
+
const { min, max } = resolveBounds("sepBy()", opts);
|
|
2178
|
+
const trailing = opts.trailing ?? "forbid";
|
|
2046
2179
|
const meta = {
|
|
2047
2180
|
firstSet: combinator._meta.firstSet,
|
|
2048
2181
|
canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
|
|
2049
2182
|
isTrivia: false
|
|
2050
2183
|
};
|
|
2051
2184
|
let expected;
|
|
2185
|
+
const failAt2 = (at) => {
|
|
2186
|
+
expected ??= deriveExpected(combinator);
|
|
2187
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
|
|
2188
|
+
};
|
|
2052
2189
|
return {
|
|
2053
2190
|
_tag: "sepBy",
|
|
2054
2191
|
_meta: meta,
|
|
2055
|
-
_def: {
|
|
2192
|
+
_def: {
|
|
2193
|
+
tag: "sepBy",
|
|
2194
|
+
parser: combinator,
|
|
2195
|
+
separator,
|
|
2196
|
+
min,
|
|
2197
|
+
...max === Infinity ? {} : { max },
|
|
2198
|
+
...trailing === "forbid" ? {} : { trailing }
|
|
2199
|
+
},
|
|
2056
2200
|
parse(input, pos, ctx) {
|
|
2057
2201
|
const first = combinator.parse(input, pos, ctx);
|
|
2058
2202
|
const values = [];
|
|
@@ -2063,6 +2207,7 @@ function sepBy(combinator, separator) {
|
|
|
2063
2207
|
} else {
|
|
2064
2208
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2065
2209
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2210
|
+
if (min >= 1) return failAt2(pos);
|
|
2066
2211
|
return { ok: true, value: [], span: { start: pos, end: pos } };
|
|
2067
2212
|
}
|
|
2068
2213
|
expected ??= deriveExpected(combinator);
|
|
@@ -2072,6 +2217,7 @@ function sepBy(combinator, separator) {
|
|
|
2072
2217
|
cur = rec.end;
|
|
2073
2218
|
}
|
|
2074
2219
|
while (cur < input.length) {
|
|
2220
|
+
if (values.length >= max) break;
|
|
2075
2221
|
const loopMark = saveTriviaMark(ctx);
|
|
2076
2222
|
let sepPos = cur;
|
|
2077
2223
|
if (ctx.trivia) {
|
|
@@ -2088,6 +2234,7 @@ function sepBy(combinator, separator) {
|
|
|
2088
2234
|
rollbackTrivia(ctx, loopMark);
|
|
2089
2235
|
break;
|
|
2090
2236
|
}
|
|
2237
|
+
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
2091
2238
|
let nextPos = sep2.span.end;
|
|
2092
2239
|
if (ctx.trivia) {
|
|
2093
2240
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
@@ -2109,12 +2256,18 @@ function sepBy(combinator, separator) {
|
|
|
2109
2256
|
cur = rec.end;
|
|
2110
2257
|
continue;
|
|
2111
2258
|
}
|
|
2259
|
+
if (sepMark !== void 0) {
|
|
2260
|
+
rollbackTrivia(ctx, sepMark);
|
|
2261
|
+
cur = sep2.span.end;
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2112
2264
|
rollbackTrivia(ctx, loopMark);
|
|
2113
2265
|
break;
|
|
2114
2266
|
}
|
|
2115
2267
|
values.push(next.value);
|
|
2116
2268
|
cur = next.span.end;
|
|
2117
2269
|
}
|
|
2270
|
+
if (values.length < min) return failAt2(cur);
|
|
2118
2271
|
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
2119
2272
|
}
|
|
2120
2273
|
};
|
|
@@ -2167,6 +2320,7 @@ function markUnusedValues(root) {
|
|
|
2167
2320
|
visit(def.separator, false);
|
|
2168
2321
|
return;
|
|
2169
2322
|
case "not":
|
|
2323
|
+
case "peek":
|
|
2170
2324
|
visit(def.parser, false);
|
|
2171
2325
|
return;
|
|
2172
2326
|
case "grammar":
|
|
@@ -2285,9 +2439,9 @@ function not(combinator) {
|
|
|
2285
2439
|
_meta: meta,
|
|
2286
2440
|
_def: { tag: "not", parser: combinator },
|
|
2287
2441
|
parse(input, pos, ctx) {
|
|
2288
|
-
const mark =
|
|
2442
|
+
const mark = saveLookaheadMark(ctx);
|
|
2289
2443
|
const result = combinator.parse(input, pos, ctx);
|
|
2290
|
-
|
|
2444
|
+
rollbackLookahead(ctx, mark);
|
|
2291
2445
|
if (result.ok) {
|
|
2292
2446
|
return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2293
2447
|
}
|
|
@@ -2296,6 +2450,28 @@ function not(combinator) {
|
|
|
2296
2450
|
};
|
|
2297
2451
|
}
|
|
2298
2452
|
|
|
2453
|
+
// src/combinators/peek.ts
|
|
2454
|
+
function peek(combinator) {
|
|
2455
|
+
const meta = {
|
|
2456
|
+
// A nullable body imposes no first-char constraint — `any()`, not its set.
|
|
2457
|
+
firstSet: matchesEmpty(combinator) ? any() : combinator._meta.firstSet,
|
|
2458
|
+
canMatchNewline: false,
|
|
2459
|
+
isTrivia: false
|
|
2460
|
+
};
|
|
2461
|
+
return {
|
|
2462
|
+
_tag: "peek",
|
|
2463
|
+
_meta: meta,
|
|
2464
|
+
_def: { tag: "peek", parser: combinator },
|
|
2465
|
+
parse(input, pos, ctx) {
|
|
2466
|
+
const mark = saveLookaheadMark(ctx);
|
|
2467
|
+
const result = combinator.parse(input, pos, ctx);
|
|
2468
|
+
rollbackLookahead(ctx, mark);
|
|
2469
|
+
if (result.ok) return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
2470
|
+
return { ok: false, expected: [`peek(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2299
2475
|
// src/compiler/build-arity.ts
|
|
2300
2476
|
var PARAM_LIST_RE = (
|
|
2301
2477
|
// (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
|
|
@@ -2394,6 +2570,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2394
2570
|
case "expect":
|
|
2395
2571
|
case "withCtx":
|
|
2396
2572
|
case "not":
|
|
2573
|
+
case "peek":
|
|
2397
2574
|
return parserHasOwnFields(d.parser, seen);
|
|
2398
2575
|
default:
|
|
2399
2576
|
return false;
|
|
@@ -2433,6 +2610,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2433
2610
|
case "expect":
|
|
2434
2611
|
case "withCtx":
|
|
2435
2612
|
case "not":
|
|
2613
|
+
case "peek":
|
|
2436
2614
|
case "field":
|
|
2437
2615
|
return parserHasTriviaSite(d.parser, seen);
|
|
2438
2616
|
case "choice":
|
|
@@ -2482,6 +2660,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2482
2660
|
case "expect":
|
|
2483
2661
|
case "withCtx":
|
|
2484
2662
|
case "not":
|
|
2663
|
+
case "peek":
|
|
2485
2664
|
return parserEnablesTriviaCapture(d.parser, seen);
|
|
2486
2665
|
// leaf() clears the inner CST/trivia collector and publishes only its own
|
|
2487
2666
|
// terminal result. An inner parser() capture cannot reach this node.
|
|
@@ -2830,7 +3009,7 @@ function leaf(root, fn) {
|
|
|
2830
3009
|
}
|
|
2831
3010
|
|
|
2832
3011
|
// src/version.ts
|
|
2833
|
-
var PARSEMAN_VERSION = "0.
|
|
3012
|
+
var PARSEMAN_VERSION = "0.34.0";
|
|
2834
3013
|
|
|
2835
3014
|
// src/analysis/gating.ts
|
|
2836
3015
|
var isAny = (fs2) => fs2.kind === "any";
|
|
@@ -2870,14 +3049,15 @@ function firstSetToString(fs2) {
|
|
|
2870
3049
|
var ruleNameOf = (p) => p._ruleName;
|
|
2871
3050
|
var SUGGESTIONS = {
|
|
2872
3051
|
"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.",
|
|
2873
|
-
"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.",
|
|
2874
|
-
"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.",
|
|
3052
|
+
"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.",
|
|
3053
|
+
"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.",
|
|
2875
3054
|
"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.",
|
|
2876
3055
|
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
2877
3056
|
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
2878
3057
|
};
|
|
2879
|
-
function classifyBroadArm(arm) {
|
|
3058
|
+
function classifyBroadArm(arm, resolve2) {
|
|
2880
3059
|
const seen = /* @__PURE__ */ new Set();
|
|
3060
|
+
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
2881
3061
|
const walk2 = (p) => {
|
|
2882
3062
|
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
2883
3063
|
seen.add(p);
|
|
@@ -2889,6 +3069,10 @@ function classifyBroadArm(arm) {
|
|
|
2889
3069
|
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
2890
3070
|
case "not":
|
|
2891
3071
|
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
3072
|
+
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
3073
|
+
// (or the body is nullable) — walk in for the real cause.
|
|
3074
|
+
case "peek":
|
|
3075
|
+
return walk2(d.parser);
|
|
2892
3076
|
case "scanTo":
|
|
2893
3077
|
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
2894
3078
|
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
@@ -2905,18 +3089,23 @@ function classifyBroadArm(arm) {
|
|
|
2905
3089
|
return walk2(d.parser);
|
|
2906
3090
|
case "lazy": {
|
|
2907
3091
|
const name = p._ruleName;
|
|
3092
|
+
let target;
|
|
2908
3093
|
try {
|
|
2909
|
-
|
|
2910
|
-
if (name !== void 0) return inner ? { cause: "cross-artifact-ref", detail: `via ref g.${name} \u2192 ${inner.detail}` } : null;
|
|
2911
|
-
return inner;
|
|
3094
|
+
target = d.thunk();
|
|
2912
3095
|
} catch {
|
|
2913
|
-
|
|
3096
|
+
const bound = name !== void 0 ? resolve2?.(name) : void 0;
|
|
3097
|
+
if (bound === void 0)
|
|
3098
|
+
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
3099
|
+
target = bound;
|
|
2914
3100
|
}
|
|
3101
|
+
const inner = walk2(target);
|
|
3102
|
+
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
3103
|
+
return inner;
|
|
2915
3104
|
}
|
|
2916
3105
|
case "optional":
|
|
2917
3106
|
case "many": {
|
|
2918
3107
|
const inner = walk2(d.parser);
|
|
2919
|
-
return
|
|
3108
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
2920
3109
|
}
|
|
2921
3110
|
case "oneOrMore":
|
|
2922
3111
|
case "transform":
|
|
@@ -2938,13 +3127,13 @@ function classifyBroadArm(arm) {
|
|
|
2938
3127
|
sawNullablePrefix = true;
|
|
2939
3128
|
continue;
|
|
2940
3129
|
}
|
|
2941
|
-
if (isAny(firstSetOf(t))) {
|
|
3130
|
+
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
|
|
2942
3131
|
const inner = walk2(t);
|
|
2943
3132
|
if (sawNullablePrefix)
|
|
2944
|
-
return
|
|
3133
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
2945
3134
|
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
2946
3135
|
}
|
|
2947
|
-
if (matchesEmpty(t)) {
|
|
3136
|
+
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve2)) {
|
|
2948
3137
|
sawNullablePrefix = true;
|
|
2949
3138
|
continue;
|
|
2950
3139
|
}
|
|
@@ -2955,7 +3144,7 @@ function classifyBroadArm(arm) {
|
|
|
2955
3144
|
case "choice": {
|
|
2956
3145
|
for (const a of d.parsers) {
|
|
2957
3146
|
const r = walk2(a);
|
|
2958
|
-
if (r) return
|
|
3147
|
+
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
2959
3148
|
}
|
|
2960
3149
|
return null;
|
|
2961
3150
|
}
|
|
@@ -2992,7 +3181,7 @@ function detectAntiPatterns(rule, arms) {
|
|
|
2992
3181
|
kind: "double-not",
|
|
2993
3182
|
rule,
|
|
2994
3183
|
armIndex: i,
|
|
2995
|
-
message: "not(not(...)) hand-rolls automatic first-char gating and MISCOMPILES among shared-first-char sibling arms (its first-set is ANY, poisoning dispatch).
|
|
3184
|
+
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."
|
|
2996
3185
|
});
|
|
2997
3186
|
} else {
|
|
2998
3187
|
out.push({
|
|
@@ -3015,6 +3204,9 @@ function detectAntiPatterns(rule, arms) {
|
|
|
3015
3204
|
return out;
|
|
3016
3205
|
}
|
|
3017
3206
|
function analyzeGating(entry, opts) {
|
|
3207
|
+
return analyzeGatingRules([[ruleNameOf(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3208
|
+
}
|
|
3209
|
+
function analyzeGatingRules(ruleMap, opts) {
|
|
3018
3210
|
const raw = [];
|
|
3019
3211
|
const antiPatterns = [];
|
|
3020
3212
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -3024,7 +3216,7 @@ function analyzeGating(entry, opts) {
|
|
|
3024
3216
|
const d = p._def;
|
|
3025
3217
|
const rule = ruleNameOf(p) ?? enclosingRule;
|
|
3026
3218
|
if (d.tag === "choice") {
|
|
3027
|
-
raw.push({ g: analyzeChoice(p, d, rule), rule });
|
|
3219
|
+
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
3028
3220
|
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
3029
3221
|
}
|
|
3030
3222
|
const rec = d;
|
|
@@ -3041,7 +3233,7 @@ function analyzeGating(entry, opts) {
|
|
|
3041
3233
|
}
|
|
3042
3234
|
for (const k of kids) visit(k, rule);
|
|
3043
3235
|
};
|
|
3044
|
-
visit(
|
|
3236
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf(root) ?? name);
|
|
3045
3237
|
const perRule = /* @__PURE__ */ new Map();
|
|
3046
3238
|
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3047
3239
|
const seenInRule = /* @__PURE__ */ new Map();
|
|
@@ -3057,18 +3249,19 @@ function analyzeGating(entry, opts) {
|
|
|
3057
3249
|
});
|
|
3058
3250
|
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
3059
3251
|
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
3060
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted);
|
|
3252
|
+
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
3061
3253
|
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
3254
|
+
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
3062
3255
|
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
3063
|
-
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, acceptedUnused, choices, antiPatterns };
|
|
3256
|
+
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, deferred, acceptedUnused, choices, antiPatterns };
|
|
3064
3257
|
}
|
|
3065
|
-
function analyzeChoice(p, d, rule) {
|
|
3258
|
+
function analyzeChoice(p, d, rule, resolve2) {
|
|
3066
3259
|
const arms = d.parsers;
|
|
3067
3260
|
const shallow = arms.map((a) => a._meta.firstSet);
|
|
3068
|
-
const deep = arms.map((a) => firstSetOf(a));
|
|
3261
|
+
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve2));
|
|
3069
3262
|
const anyArms = deep.map((fs2, index) => ({ fs: fs2, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
3070
|
-
const { cause, detail } = classifyBroadArm(arms[index]);
|
|
3071
|
-
return { index, cause, detail, shallowAnyOnly: false, suggestion: SUGGESTIONS[cause] };
|
|
3263
|
+
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve2);
|
|
3264
|
+
return { index, cause, detail, shallowAnyOnly: false, unresolvedExternal: unresolvedExternal === true, suggestion: SUGGESTIONS[cause] };
|
|
3072
3265
|
});
|
|
3073
3266
|
const overlaps = [];
|
|
3074
3267
|
for (let i = 0; i < deep.length; i++)
|
|
@@ -3084,10 +3277,12 @@ function analyzeChoice(p, d, rule) {
|
|
|
3084
3277
|
if (d.disjoint) gates = "yes";
|
|
3085
3278
|
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
3086
3279
|
else gates = "no";
|
|
3280
|
+
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
3087
3281
|
return {
|
|
3088
3282
|
rule,
|
|
3089
3283
|
strategy: d.strategy?.tag ?? "firstMatch",
|
|
3090
3284
|
gates,
|
|
3285
|
+
deferred,
|
|
3091
3286
|
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
3092
3287
|
anyArms,
|
|
3093
3288
|
overlaps
|
|
@@ -3254,6 +3449,7 @@ function children(def, winners) {
|
|
|
3254
3449
|
case "field":
|
|
3255
3450
|
case "grammar":
|
|
3256
3451
|
case "not":
|
|
3452
|
+
case "peek":
|
|
3257
3453
|
case "node":
|
|
3258
3454
|
case "guard":
|
|
3259
3455
|
case "withCtx":
|
|
@@ -5027,6 +5223,9 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5027
5223
|
case "keywords":
|
|
5028
5224
|
case "guard":
|
|
5029
5225
|
case "not":
|
|
5226
|
+
// peek(): emitted under a non-capturing probe ctx and zero-width on both
|
|
5227
|
+
// outcomes, so it can never leave a partial capture behind.
|
|
5228
|
+
case "peek":
|
|
5030
5229
|
case "trivia":
|
|
5031
5230
|
case "token":
|
|
5032
5231
|
case "leaf":
|
|
@@ -5098,6 +5297,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5098
5297
|
case "leaf":
|
|
5099
5298
|
return true;
|
|
5100
5299
|
case "not":
|
|
5300
|
+
case "peek":
|
|
5101
5301
|
case "guard":
|
|
5102
5302
|
case "trivia":
|
|
5103
5303
|
case "unknown":
|
|
@@ -5224,6 +5424,7 @@ function emitKeywordsFast(def, ctx, pos) {
|
|
|
5224
5424
|
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
5225
5425
|
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
5226
5426
|
if (def.caseInsensitive && def.boundary) return null;
|
|
5427
|
+
if (def.caseInsensitive && def.words.some((w) => w.split("").some((c) => c.charCodeAt(0) > 127))) return null;
|
|
5227
5428
|
let boundary = null;
|
|
5228
5429
|
if (def.boundary) {
|
|
5229
5430
|
let body = def.boundary;
|
|
@@ -5271,7 +5472,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
5271
5472
|
if (fast) return fast;
|
|
5272
5473
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
5273
5474
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
5274
|
-
const flags = def.caseInsensitive ? "
|
|
5475
|
+
const flags = def.caseInsensitive ? "iy" : "uy";
|
|
5275
5476
|
const source = `(?:${alt})${boundary}`;
|
|
5276
5477
|
const key = `${source}/${flags}`;
|
|
5277
5478
|
let rName = ctx.regexMap.get(key);
|
|
@@ -5417,6 +5618,7 @@ function failsAtStart(p) {
|
|
|
5417
5618
|
case "keywords":
|
|
5418
5619
|
case "guard":
|
|
5419
5620
|
case "not":
|
|
5621
|
+
case "peek":
|
|
5420
5622
|
return true;
|
|
5421
5623
|
case "transform":
|
|
5422
5624
|
case "label":
|
|
@@ -5830,14 +6032,17 @@ function emitMany(def, ctx, pos) {
|
|
|
5830
6032
|
];
|
|
5831
6033
|
const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
|
|
5832
6034
|
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
|
|
5833
|
-
|
|
6035
|
+
for (let i = 0; i < def.min; i++) {
|
|
5834
6036
|
const firstR = emit(def.parser, ctx, curV);
|
|
5835
6037
|
stmts.push(...firstR.stmts);
|
|
5836
6038
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
5837
6039
|
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
5838
6040
|
}
|
|
6041
|
+
const maxV = def.max === void 0 ? null : wantValue ? `${arrV}.length` : v(ctx, "_cnt");
|
|
6042
|
+
if (maxV !== null && !wantValue) stmts.push(`${ind(ctx)}let ${maxV} = ${def.min}`);
|
|
5839
6043
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
5840
6044
|
ctx.indent++;
|
|
6045
|
+
if (maxV !== null) stmts.push(`${ind(ctx)}if (${maxV} >= ${def.max}) break`);
|
|
5841
6046
|
let itemPos = curV;
|
|
5842
6047
|
let rollback = "";
|
|
5843
6048
|
if (ctx.activeTrivia) {
|
|
@@ -5878,7 +6083,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5878
6083
|
`${ind(ctx)}if (!${iterOk}) {`,
|
|
5879
6084
|
`${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
|
|
5880
6085
|
`${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
|
|
5881
|
-
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
|
|
6086
|
+
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : maxV !== null ? [`${ind(ctx)} ${maxV}++`] : [],
|
|
5882
6087
|
`${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
|
|
5883
6088
|
`${ind(ctx)} ${curV} = ${rrV}.end`,
|
|
5884
6089
|
`${ind(ctx)} continue`,
|
|
@@ -5891,6 +6096,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5891
6096
|
stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
5892
6097
|
}
|
|
5893
6098
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
6099
|
+
else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
|
|
5894
6100
|
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
5895
6101
|
ctx.indent--;
|
|
5896
6102
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -5955,8 +6161,8 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5955
6161
|
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
5956
6162
|
...firstStmts
|
|
5957
6163
|
];
|
|
5958
|
-
const failItem = (nextOkVar, itemPosVar,
|
|
5959
|
-
if (!rec) return [
|
|
6164
|
+
const failItem = (nextOkVar, itemPosVar, breakStmt) => {
|
|
6165
|
+
if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) ${breakStmt === "break" ? "break" : `{ ${breakStmt} }`}`];
|
|
5960
6166
|
const rr = v(ctx, "_rr");
|
|
5961
6167
|
return [
|
|
5962
6168
|
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
@@ -5964,10 +6170,24 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5964
6170
|
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
5965
6171
|
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
5966
6172
|
`${ind(ctx)} }`,
|
|
5967
|
-
`${ind(ctx)} ${
|
|
6173
|
+
`${ind(ctx)} ${breakStmt}`,
|
|
5968
6174
|
`${ind(ctx)}}`
|
|
5969
6175
|
];
|
|
5970
6176
|
};
|
|
6177
|
+
const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
|
|
6178
|
+
const postSepMarks = () => {
|
|
6179
|
+
if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
|
|
6180
|
+
const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg");
|
|
6181
|
+
return {
|
|
6182
|
+
decl: [
|
|
6183
|
+
`${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
|
|
6184
|
+
`${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
|
|
6185
|
+
`${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
|
|
6186
|
+
`${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`
|
|
6187
|
+
],
|
|
6188
|
+
rb: `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${lv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${rw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${lg}; `
|
|
6189
|
+
};
|
|
6190
|
+
};
|
|
5971
6191
|
if (rec) {
|
|
5972
6192
|
const rr0 = v(ctx, "_rr0");
|
|
5973
6193
|
const didV = v(ctx, "_did");
|
|
@@ -5993,6 +6213,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5993
6213
|
);
|
|
5994
6214
|
ctx.indent++;
|
|
5995
6215
|
}
|
|
6216
|
+
if (def.max !== void 0) stmts.push(`${ind(ctx)}if (${arrV}.length >= ${def.max}) break`);
|
|
5996
6217
|
let sepAtPos = curV;
|
|
5997
6218
|
if (ctx.activeTrivia) {
|
|
5998
6219
|
if (ctx.capturing) {
|
|
@@ -6013,12 +6234,14 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6013
6234
|
const rollbackToSep = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
|
|
6014
6235
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
6015
6236
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
6237
|
+
const post = postSepMarks();
|
|
6238
|
+
stmts.push(...post.decl);
|
|
6016
6239
|
const npV = v(ctx, "_np");
|
|
6017
6240
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
6018
6241
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6019
6242
|
stmts.push(
|
|
6020
6243
|
...nextStmts,
|
|
6021
|
-
...failItem(nextOk, npV, rollbackToSep,
|
|
6244
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
6022
6245
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6023
6246
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6024
6247
|
);
|
|
@@ -6034,7 +6257,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6034
6257
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6035
6258
|
stmts.push(
|
|
6036
6259
|
...nextStmts,
|
|
6037
|
-
...failItem(nextOk, npV, "",
|
|
6260
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
|
|
6038
6261
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6039
6262
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6040
6263
|
);
|
|
@@ -6051,10 +6274,12 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6051
6274
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
6052
6275
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
6053
6276
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
6277
|
+
const post = postSepMarks();
|
|
6278
|
+
stmts.push(...post.decl);
|
|
6054
6279
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
6055
6280
|
stmts.push(
|
|
6056
6281
|
...nextStmts,
|
|
6057
|
-
...failItem(nextOk, sepEnd, nextRb,
|
|
6282
|
+
...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
6058
6283
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6059
6284
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6060
6285
|
);
|
|
@@ -6063,6 +6288,9 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6063
6288
|
stmts.push(`${ind(ctx)}}`);
|
|
6064
6289
|
ctx.indent--;
|
|
6065
6290
|
stmts.push(`${ind(ctx)}}`);
|
|
6291
|
+
if (def.min >= 1) {
|
|
6292
|
+
stmts.push(...emitIfFail(ctx, `${arrV}.length < ${def.min}`, failArrBody(ctx, deriveExpectedArr([def.parser]), curV)));
|
|
6293
|
+
}
|
|
6066
6294
|
return { stmts, valueVar: arrV, endVar: curV };
|
|
6067
6295
|
}
|
|
6068
6296
|
function emitScanTo(def, ctx, pos) {
|
|
@@ -6114,17 +6342,44 @@ function emitScanTo(def, ctx, pos) {
|
|
|
6114
6342
|
return { stmts, valueVar: valV, endVar: curV };
|
|
6115
6343
|
}
|
|
6116
6344
|
function emitNot(def, ctx, pos) {
|
|
6345
|
+
const sinksLive = !!ctx.capturing || !!ctx.recovery;
|
|
6346
|
+
const leaves = sinksLive ? v(ctx, "_ntl") : null;
|
|
6347
|
+
const raw = sinksLive ? v(ctx, "_ntr") : null;
|
|
6348
|
+
const tl = sinksLive ? v(ctx, "_ntt") : null;
|
|
6349
|
+
const log = sinksLive ? v(ctx, "_ntg") : null;
|
|
6350
|
+
const fields = sinksLive ? v(ctx, "_ntf") : null;
|
|
6351
|
+
const errors = sinksLive ? v(ctx, "_nte") : null;
|
|
6117
6352
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
6118
6353
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
6119
6354
|
return {
|
|
6120
6355
|
stmts: [
|
|
6356
|
+
...sinksLive ? [
|
|
6357
|
+
`${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${tl} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`
|
|
6358
|
+
] : [],
|
|
6121
6359
|
...stmts,
|
|
6360
|
+
...sinksLive ? [
|
|
6361
|
+
`${ind(ctx)}if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${log}; if (_ctx._fields) _ctx._fields.length = ${fields}; if (_ctx._errors) _ctx._errors.length = ${errors}`
|
|
6362
|
+
] : [],
|
|
6122
6363
|
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
6123
6364
|
],
|
|
6124
6365
|
valueVar: "null",
|
|
6125
6366
|
endVar: pos
|
|
6126
6367
|
};
|
|
6127
6368
|
}
|
|
6369
|
+
function emitPeek(def, ctx, pos) {
|
|
6370
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
6371
|
+
const { stmts, okVar } = emitFallible(def.parser, probeCtx, pos, true);
|
|
6372
|
+
ctx.vars = probeCtx.vars;
|
|
6373
|
+
const label2 = JSON.stringify(`peek(${def.parser._tag})`);
|
|
6374
|
+
return {
|
|
6375
|
+
stmts: [
|
|
6376
|
+
...stmts,
|
|
6377
|
+
...emitIfFail(ctx, `!${okVar}`, failBody(ctx, label2, pos))
|
|
6378
|
+
],
|
|
6379
|
+
valueVar: "null",
|
|
6380
|
+
endVar: pos
|
|
6381
|
+
};
|
|
6382
|
+
}
|
|
6128
6383
|
function escapeTokenLiteral(value) {
|
|
6129
6384
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6130
6385
|
}
|
|
@@ -6412,9 +6667,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
6412
6667
|
return { stmts, valueVar: vv, endVar: ev };
|
|
6413
6668
|
}
|
|
6414
6669
|
function emitLazy(p, def, ctx, pos) {
|
|
6415
|
-
const
|
|
6670
|
+
const canonical2 = ctx.ruleNames?.get(p);
|
|
6416
6671
|
const usage = ctx.lazyUsage;
|
|
6417
|
-
if (!
|
|
6672
|
+
if (!canonical2 && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
6418
6673
|
let resolved;
|
|
6419
6674
|
try {
|
|
6420
6675
|
resolved = def.thunk();
|
|
@@ -6424,7 +6679,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
6424
6679
|
return emit(resolved, ctx, pos);
|
|
6425
6680
|
}
|
|
6426
6681
|
if (!ctx.namedParsers.has(p)) {
|
|
6427
|
-
const fnName2 =
|
|
6682
|
+
const fnName2 = canonical2 ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
6428
6683
|
ctx.namedParsers.set(p, fnName2);
|
|
6429
6684
|
let resolved;
|
|
6430
6685
|
try {
|
|
@@ -6707,6 +6962,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
6707
6962
|
}
|
|
6708
6963
|
case "not":
|
|
6709
6964
|
return emitNot(def, ctx, pos);
|
|
6965
|
+
case "peek":
|
|
6966
|
+
return emitPeek(def, ctx, pos);
|
|
6710
6967
|
case "node":
|
|
6711
6968
|
return emitNode(def, ctx, pos);
|
|
6712
6969
|
case "scanTo":
|
|
@@ -6795,6 +7052,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6795
7052
|
case "many":
|
|
6796
7053
|
case "oneOrMore":
|
|
6797
7054
|
case "not":
|
|
7055
|
+
case "peek":
|
|
6798
7056
|
case "transform":
|
|
6799
7057
|
return hasNodeDef(d.parser, seen);
|
|
6800
7058
|
case "skip":
|
|
@@ -6846,17 +7104,18 @@ function childrenOf(def) {
|
|
|
6846
7104
|
case "field":
|
|
6847
7105
|
case "grammar":
|
|
6848
7106
|
case "not":
|
|
7107
|
+
case "peek":
|
|
6849
7108
|
case "node":
|
|
6850
7109
|
case "withCtx":
|
|
6851
7110
|
case "expect":
|
|
6852
7111
|
return [def.parser];
|
|
6853
|
-
// emitMany's
|
|
6854
|
-
// (
|
|
6855
|
-
// emit() call
|
|
7112
|
+
// emitMany's min>=1 branch and emitSepBy both codegen `def.parser` more than
|
|
7113
|
+
// once (`min` mandatory matches, then again inside the repeat loop) — each is a
|
|
7114
|
+
// real emit() call site, so the usage analysis must see one edge per site or it
|
|
6856
7115
|
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
6857
|
-
// referenced from
|
|
7116
|
+
// referenced from several positions within this one compiled function.
|
|
6858
7117
|
case "oneOrMore":
|
|
6859
|
-
return
|
|
7118
|
+
return Array.from({ length: def.min + 1 }, () => def.parser);
|
|
6860
7119
|
case "sepBy":
|
|
6861
7120
|
return [def.parser, def.parser, def.separator];
|
|
6862
7121
|
case "skip":
|
|
@@ -7002,12 +7261,41 @@ function resolveGatingLevel(opt) {
|
|
|
7002
7261
|
return "warn";
|
|
7003
7262
|
}
|
|
7004
7263
|
function runGatingDiagnostic(combinator, opt) {
|
|
7264
|
+
return reportGating(opt, (o) => analyzeGating(combinator, o));
|
|
7265
|
+
}
|
|
7266
|
+
function runGatingDiagnosticRules(ruleMap, opt) {
|
|
7267
|
+
return reportGating(opt, (o) => analyzeGatingRules(ruleMap, o));
|
|
7268
|
+
}
|
|
7269
|
+
function runFusedGatingDiagnostic(getRuleMaps, opt) {
|
|
7270
|
+
if (resolveGatingLevel(opt) === "off") return void 0;
|
|
7271
|
+
const ruleMaps = getRuleMaps();
|
|
7272
|
+
if (!ruleMaps.some((map) => hasExternalRuleRef(map))) return void 0;
|
|
7273
|
+
const winners = /* @__PURE__ */ new Map();
|
|
7274
|
+
for (const map of ruleMaps) for (const [name, rule] of map) {
|
|
7275
|
+
if (rule._def.tag === "lazy") {
|
|
7276
|
+
try {
|
|
7277
|
+
rule._def.thunk();
|
|
7278
|
+
} catch {
|
|
7279
|
+
continue;
|
|
7280
|
+
}
|
|
7281
|
+
}
|
|
7282
|
+
winners.set(name, rule);
|
|
7283
|
+
}
|
|
7284
|
+
const entries = [...winners];
|
|
7285
|
+
return reportGating(opt, (o) => {
|
|
7286
|
+
const deferredHere = new Set(analyzeGatingRules(entries, o).deferred.map((c) => c.id));
|
|
7287
|
+
const fused = analyzeGatingRules(entries, { ...o, resolveRef: (name) => winners.get(name) });
|
|
7288
|
+
return { ...fused, ungated: fused.ungated.filter((c) => deferredHere.has(c.id)), antiPatterns: [] };
|
|
7289
|
+
});
|
|
7290
|
+
}
|
|
7291
|
+
function reportGating(opt, analyze) {
|
|
7005
7292
|
const level = resolveGatingLevel(opt);
|
|
7006
7293
|
if (level === "off") return void 0;
|
|
7007
|
-
const
|
|
7294
|
+
const obj = opt !== null && typeof opt === "object" ? opt : void 0;
|
|
7295
|
+
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;
|
|
7008
7296
|
let report;
|
|
7009
7297
|
try {
|
|
7010
|
-
report =
|
|
7298
|
+
report = analyze(analyzeOpts);
|
|
7011
7299
|
} catch {
|
|
7012
7300
|
return void 0;
|
|
7013
7301
|
}
|
|
@@ -7143,6 +7431,7 @@ function publicRuleWrapperSource(rule, fnSource) {
|
|
|
7143
7431
|
return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
|
|
7144
7432
|
}
|
|
7145
7433
|
function compileRuleMap(ruleMap, opts) {
|
|
7434
|
+
runGatingDiagnosticRules(ruleMap, opts?.gating);
|
|
7146
7435
|
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
7147
7436
|
const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
|
|
7148
7437
|
const grammarTrivia = opts?.trivia;
|
|
@@ -7261,6 +7550,7 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
7261
7550
|
}
|
|
7262
7551
|
function compileLinkable(ruleMapArg, ns, opts) {
|
|
7263
7552
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
7553
|
+
if (opts?.gating !== void 0) runGatingDiagnosticRules(ruleMapArg, opts.gating);
|
|
7264
7554
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
7265
7555
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
7266
7556
|
const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
@@ -7317,6 +7607,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7317
7607
|
fnNameToKey.set(fn, key);
|
|
7318
7608
|
}
|
|
7319
7609
|
ctx.ruleNames = ruleNames;
|
|
7610
|
+
const externalRefs = /* @__PURE__ */ new Set();
|
|
7320
7611
|
{
|
|
7321
7612
|
const scanned = /* @__PURE__ */ new Set();
|
|
7322
7613
|
const scanExternal = (p) => {
|
|
@@ -7332,11 +7623,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7332
7623
|
}
|
|
7333
7624
|
if (resolved === void 0) {
|
|
7334
7625
|
const name = p._ruleName;
|
|
7335
|
-
if (name
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7626
|
+
if (name) {
|
|
7627
|
+
externalRefs.add(p);
|
|
7628
|
+
if (!ctx.namedParsers.has(p)) {
|
|
7629
|
+
assertRuleName(name);
|
|
7630
|
+
const fn = `_r_${name}`;
|
|
7631
|
+
ruleNames.set(p, fn);
|
|
7632
|
+
ctx.namedParsers.set(p, fn);
|
|
7633
|
+
}
|
|
7340
7634
|
}
|
|
7341
7635
|
return;
|
|
7342
7636
|
}
|
|
@@ -7449,12 +7743,32 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7449
7743
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
7450
7744
|
needsHostReads: !!ctx.needsHostReads,
|
|
7451
7745
|
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
7452
|
-
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
|
|
7746
|
+
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
|
|
7453
7747
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
7454
7748
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
7455
7749
|
};
|
|
7456
7750
|
}
|
|
7457
|
-
function
|
|
7751
|
+
function hasExternalRuleRef(ruleMap) {
|
|
7752
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7753
|
+
const visit = (p) => {
|
|
7754
|
+
if (seen.has(p)) return false;
|
|
7755
|
+
seen.add(p);
|
|
7756
|
+
const def = p._def;
|
|
7757
|
+
if (def.tag === "lazy") {
|
|
7758
|
+
let resolved;
|
|
7759
|
+
try {
|
|
7760
|
+
resolved = def.thunk();
|
|
7761
|
+
} catch {
|
|
7762
|
+
resolved = void 0;
|
|
7763
|
+
}
|
|
7764
|
+
if (resolved === void 0) return p._ruleName !== void 0;
|
|
7765
|
+
return visit(resolved);
|
|
7766
|
+
}
|
|
7767
|
+
return childrenOf(def).some(visit);
|
|
7768
|
+
};
|
|
7769
|
+
return ruleMap.some(([, rule]) => visit(rule));
|
|
7770
|
+
}
|
|
7771
|
+
function hasSemanticReduction(roots, externalRefs) {
|
|
7458
7772
|
const seen = /* @__PURE__ */ new Set();
|
|
7459
7773
|
const visit = (parser2) => {
|
|
7460
7774
|
if (seen.has(parser2)) return false;
|
|
@@ -7465,6 +7779,7 @@ function hasSemanticReduction(roots) {
|
|
|
7465
7779
|
if (def.tag === "guard" || def.tag === "withCtx") return true;
|
|
7466
7780
|
if (def.tag === "node" && def.build !== void 0) return true;
|
|
7467
7781
|
if (def.tag === "lazy") {
|
|
7782
|
+
if (externalRefs?.has(parser2)) return false;
|
|
7468
7783
|
try {
|
|
7469
7784
|
return visit(def.thunk());
|
|
7470
7785
|
} catch {
|
|
@@ -7556,6 +7871,7 @@ function childrenOf2(def) {
|
|
|
7556
7871
|
case "label":
|
|
7557
7872
|
case "field":
|
|
7558
7873
|
case "not":
|
|
7874
|
+
case "peek":
|
|
7559
7875
|
case "node":
|
|
7560
7876
|
case "withCtx":
|
|
7561
7877
|
case "expect":
|
|
@@ -7589,9 +7905,16 @@ function lazyTarget(c) {
|
|
|
7589
7905
|
}
|
|
7590
7906
|
var Unserializable = class extends Error {
|
|
7591
7907
|
};
|
|
7592
|
-
function
|
|
7908
|
+
function repeatOpts(min, max, trailing) {
|
|
7909
|
+
const parts = [];
|
|
7910
|
+
if (min !== 0) parts.push(`min: ${min}`);
|
|
7911
|
+
if (max !== void 0) parts.push(`max: ${max}`);
|
|
7912
|
+
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
7913
|
+
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
7914
|
+
}
|
|
7915
|
+
function serializeRuleMap(ruleMap, scanSkip) {
|
|
7593
7916
|
try {
|
|
7594
|
-
return new Serializer(ruleMap).run();
|
|
7917
|
+
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
7595
7918
|
} catch (e) {
|
|
7596
7919
|
if (e instanceof Unserializable) {
|
|
7597
7920
|
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
@@ -7692,6 +8015,7 @@ function evalRuleMapIR(ir) {
|
|
|
7692
8015
|
"optional",
|
|
7693
8016
|
"sepBy",
|
|
7694
8017
|
"not",
|
|
8018
|
+
"peek",
|
|
7695
8019
|
"node",
|
|
7696
8020
|
"parser",
|
|
7697
8021
|
"scanTo",
|
|
@@ -7724,6 +8048,7 @@ function evalRuleMapIR(ir) {
|
|
|
7724
8048
|
optional,
|
|
7725
8049
|
sepBy,
|
|
7726
8050
|
not,
|
|
8051
|
+
peek,
|
|
7727
8052
|
node,
|
|
7728
8053
|
parser,
|
|
7729
8054
|
scanTo,
|
|
@@ -7752,10 +8077,28 @@ var Serializer = class {
|
|
|
7752
8077
|
decls = [];
|
|
7753
8078
|
ruleValues = /* @__PURE__ */ new Set();
|
|
7754
8079
|
ruleMap;
|
|
7755
|
-
|
|
8080
|
+
scanSkip;
|
|
8081
|
+
constructor(ruleMap, scanSkip = []) {
|
|
7756
8082
|
this.ruleMap = ruleMap;
|
|
8083
|
+
this.scanSkip = scanSkip;
|
|
7757
8084
|
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
7758
8085
|
}
|
|
8086
|
+
/** A grammar-level `scanSkip` unit is an opaque TERMINAL (a string, a balanced
|
|
8087
|
+
* bracket) — it must not reach a grammar-rule ref (`g[name]`), because the
|
|
8088
|
+
* serialized `scanSkip` array sits in the `rules({ scanSkip })` OPTIONS position,
|
|
8089
|
+
* outside the `(g) => …` factory where `g` is bound. A unit that does reference a
|
|
8090
|
+
* rule can't be carried in options; bail to the full-pieces fallback. */
|
|
8091
|
+
assertNoRuleRef(c, seen) {
|
|
8092
|
+
if (seen.has(c)) return;
|
|
8093
|
+
seen.add(c);
|
|
8094
|
+
if (c._def.tag === "lazy") {
|
|
8095
|
+
if (ruleNameOf2(c) !== void 0) throw new Unserializable("scanSkip unit references a grammar rule");
|
|
8096
|
+
const target = lazyTarget(c);
|
|
8097
|
+
if (target) this.assertNoRuleRef(target, seen);
|
|
8098
|
+
return;
|
|
8099
|
+
}
|
|
8100
|
+
for (const child of childrenOf2(c._def)) this.assertNoRuleRef(child, seen);
|
|
8101
|
+
}
|
|
7759
8102
|
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
7760
8103
|
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
7761
8104
|
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
@@ -7764,21 +8107,38 @@ var Serializer = class {
|
|
|
7764
8107
|
}
|
|
7765
8108
|
run() {
|
|
7766
8109
|
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
8110
|
+
for (const c of this.scanSkip) {
|
|
8111
|
+
this.assertNoRuleRef(c, /* @__PURE__ */ new Set());
|
|
8112
|
+
this.analyze(c, /* @__PURE__ */ new Set());
|
|
8113
|
+
}
|
|
7767
8114
|
let n = 0;
|
|
7768
8115
|
for (const [c, count] of this.counts) {
|
|
7769
8116
|
if (this.ruleValues.has(c)) continue;
|
|
7770
8117
|
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
7771
8118
|
}
|
|
8119
|
+
const outerDecls = [];
|
|
8120
|
+
const innerDecls = [];
|
|
8121
|
+
let optionsArg = "";
|
|
8122
|
+
if (this.scanSkip.length > 0) {
|
|
8123
|
+
this.decls = outerDecls;
|
|
8124
|
+
const units = this.scanSkip.map((c) => this.ref(c, null));
|
|
8125
|
+
optionsArg = `{ scanSkip: [${units.join(", ")}] }, `;
|
|
8126
|
+
}
|
|
8127
|
+
this.decls = innerDecls;
|
|
7772
8128
|
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
7773
|
-
|
|
8129
|
+
const factory = innerDecls.length === 0 ? `(g) => ({
|
|
7774
8130
|
${body}
|
|
7775
|
-
}))
|
|
7776
|
-
|
|
7777
|
-
${this.decls.map((d) => " " + d).join("\n")}
|
|
8131
|
+
})` : `(g) => {
|
|
8132
|
+
${innerDecls.map((d) => " " + d).join("\n")}
|
|
7778
8133
|
return ({
|
|
7779
8134
|
${body}
|
|
7780
8135
|
})
|
|
7781
|
-
}
|
|
8136
|
+
}`;
|
|
8137
|
+
const call = `rules(${optionsArg}${factory})`;
|
|
8138
|
+
return outerDecls.length === 0 ? call : `(() => {
|
|
8139
|
+
${outerDecls.map((d) => " " + d).join("\n")}
|
|
8140
|
+
return ${call}
|
|
8141
|
+
})()`;
|
|
7782
8142
|
}
|
|
7783
8143
|
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
7784
8144
|
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
@@ -7863,17 +8223,21 @@ ${body}
|
|
|
7863
8223
|
return `_gch([${items.join(", ")}])`;
|
|
7864
8224
|
}
|
|
7865
8225
|
case "many":
|
|
7866
|
-
return `many(${kid(def.parser)})`;
|
|
8226
|
+
return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
8227
|
+
// A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
|
|
8228
|
+
// it through `many` so the bounds survive (oneOrMore defaults min to 1).
|
|
7867
8229
|
case "oneOrMore":
|
|
7868
|
-
return `oneOrMore(${kid(def.parser)})`;
|
|
8230
|
+
return def.min === 1 && def.max === void 0 ? `oneOrMore(${kid(def.parser)})` : `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
7869
8231
|
case "optional":
|
|
7870
8232
|
return `optional(${kid(def.parser)})`;
|
|
7871
8233
|
case "attempt":
|
|
7872
8234
|
return `attempt(${kid(def.parser)})`;
|
|
7873
8235
|
case "sepBy":
|
|
7874
|
-
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
8236
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)}${repeatOpts(def.min, def.max, def.trailing)})`;
|
|
7875
8237
|
case "not":
|
|
7876
8238
|
return `not(${kid(def.parser)})`;
|
|
8239
|
+
case "peek":
|
|
8240
|
+
return `peek(${kid(def.parser)})`;
|
|
7877
8241
|
case "trivia":
|
|
7878
8242
|
return `trivia(${kid(def.parser)})`;
|
|
7879
8243
|
case "token":
|
|
@@ -8309,15 +8673,12 @@ var SUPPORTED = {
|
|
|
8309
8673
|
literal: (...a) => literal(a[0], a[1]),
|
|
8310
8674
|
regex: (...a) => regex(a[0], a[1]),
|
|
8311
8675
|
keywords: (...a) => keywords(a[0], a[1]),
|
|
8312
|
-
word: (...a) => word(a[0], a[1]),
|
|
8676
|
+
word: (...a) => word(a[0], a[1], a[2]),
|
|
8313
8677
|
sequence: (...a) => sequence(...a),
|
|
8314
8678
|
choice: (...a) => choice(...a),
|
|
8315
8679
|
attempt: (...a) => attempt(a[0]),
|
|
8316
|
-
many: (...a) => many(a[0]),
|
|
8317
|
-
oneOrMore: (...a) => oneOrMore(a[0]),
|
|
8318
8680
|
optional: (...a) => optional(a[0]),
|
|
8319
8681
|
skip: (...a) => skip(a[0], a[1]),
|
|
8320
|
-
sepBy: (...a) => sepBy(a[0], a[1]),
|
|
8321
8682
|
trivia: (...a) => trivia(a[0]),
|
|
8322
8683
|
label: (...a) => label(a[0], a[1]),
|
|
8323
8684
|
field: (...a) => field(a[0], a[1]),
|
|
@@ -8493,8 +8854,8 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
8493
8854
|
return null;
|
|
8494
8855
|
}
|
|
8495
8856
|
}
|
|
8496
|
-
if (callee.name === "sepBy") {
|
|
8497
|
-
const [itemArg, sepArg] = node2.arguments;
|
|
8857
|
+
if (callee.name === "sepBy" || callee.name === "oneOrMoreSep") {
|
|
8858
|
+
const [itemArg, sepArg, optsArg] = node2.arguments;
|
|
8498
8859
|
if (!itemArg || !sepArg || itemArg.type === "SpreadElement" || sepArg.type === "SpreadElement") return null;
|
|
8499
8860
|
const itemMfs = [];
|
|
8500
8861
|
const itemCombi = anyValue(itemArg, scope, code, itemMfs);
|
|
@@ -8502,25 +8863,42 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
8502
8863
|
const sepMfs = [];
|
|
8503
8864
|
const sepCombi = anyValue(sepArg, scope, code, sepMfs);
|
|
8504
8865
|
if (!isCombinator(sepCombi)) return null;
|
|
8866
|
+
let opts;
|
|
8867
|
+
if (optsArg) {
|
|
8868
|
+
if (optsArg.type === "SpreadElement") return null;
|
|
8869
|
+
const v2 = anyValue(optsArg, scope, code, []);
|
|
8870
|
+
if (!v2 || typeof v2 !== "object") return null;
|
|
8871
|
+
opts = v2;
|
|
8872
|
+
}
|
|
8505
8873
|
if (mfs) mfs.push(...itemMfs, ...sepMfs, ...itemMfs);
|
|
8506
8874
|
try {
|
|
8507
|
-
return sepBy(itemCombi, sepCombi);
|
|
8875
|
+
return callee.name === "sepBy" ? sepBy(itemCombi, sepCombi, opts) : oneOrMoreSep(itemCombi, sepCombi, opts);
|
|
8508
8876
|
} catch {
|
|
8509
8877
|
return null;
|
|
8510
8878
|
}
|
|
8511
8879
|
}
|
|
8512
|
-
if (callee.name === "oneOrMore") {
|
|
8513
|
-
const [itemArg] = node2.arguments;
|
|
8880
|
+
if (callee.name === "oneOrMore" || callee.name === "many") {
|
|
8881
|
+
const [itemArg, optsArg] = node2.arguments;
|
|
8514
8882
|
if (!itemArg || itemArg.type === "SpreadElement") return null;
|
|
8515
8883
|
const itemMfs = [];
|
|
8516
8884
|
const itemCombi = anyValue(itemArg, scope, code, itemMfs);
|
|
8517
8885
|
if (!isCombinator(itemCombi)) return null;
|
|
8518
|
-
|
|
8886
|
+
let opts;
|
|
8887
|
+
if (optsArg) {
|
|
8888
|
+
if (optsArg.type === "SpreadElement") return null;
|
|
8889
|
+
const v2 = anyValue(optsArg, scope, code, []);
|
|
8890
|
+
if (!v2 || typeof v2 !== "object") return null;
|
|
8891
|
+
opts = v2;
|
|
8892
|
+
}
|
|
8893
|
+
let combi;
|
|
8519
8894
|
try {
|
|
8520
|
-
|
|
8895
|
+
combi = callee.name === "many" ? many(itemCombi, opts) : oneOrMore(itemCombi, opts);
|
|
8521
8896
|
} catch {
|
|
8522
8897
|
return null;
|
|
8523
8898
|
}
|
|
8899
|
+
const min = combi._def.tag === "oneOrMore" ? combi._def.min : 0;
|
|
8900
|
+
if (mfs) for (let i = 0; i <= min; i++) mfs.push(...itemMfs);
|
|
8901
|
+
return combi;
|
|
8524
8902
|
}
|
|
8525
8903
|
if (callee.name === "not") {
|
|
8526
8904
|
const [innerArg] = node2.arguments;
|
|
@@ -8533,6 +8911,17 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
8533
8911
|
return null;
|
|
8534
8912
|
}
|
|
8535
8913
|
}
|
|
8914
|
+
if (callee.name === "peek") {
|
|
8915
|
+
const [innerArg] = node2.arguments;
|
|
8916
|
+
if (!innerArg || innerArg.type === "SpreadElement") return null;
|
|
8917
|
+
const inner = anyValue(innerArg, scope, code, mfs);
|
|
8918
|
+
if (!isCombinator(inner)) return null;
|
|
8919
|
+
try {
|
|
8920
|
+
return peek(inner);
|
|
8921
|
+
} catch {
|
|
8922
|
+
return null;
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8536
8925
|
if (callee.name === "balanced") {
|
|
8537
8926
|
const [openArg, closeArg, optsArg] = node2.arguments;
|
|
8538
8927
|
if (!openArg || !closeArg || openArg.type === "SpreadElement" || closeArg.type === "SpreadElement") return null;
|
|
@@ -9162,6 +9551,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9162
9551
|
const warnings = [];
|
|
9163
9552
|
beginLoweringCapture();
|
|
9164
9553
|
let anyUnresolved = false;
|
|
9554
|
+
let keepMacroImport = false;
|
|
9165
9555
|
let runtimeComposeFallback = false;
|
|
9166
9556
|
let ruleMapHolderCounter = 0;
|
|
9167
9557
|
const lineOf = (pos) => {
|
|
@@ -9196,17 +9586,21 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9196
9586
|
const gTrivia = triviaValue ? evaluateExpr(triviaValue, scope, code, []) : void 0;
|
|
9197
9587
|
const scanSkipValue = optionValue("scanSkip");
|
|
9198
9588
|
const gScanSkip = scanSkipValue ? evaluateCombinatorArray(scanSkipValue, scope, code) ?? void 0 : void 0;
|
|
9589
|
+
if (gScanSkip) {
|
|
9590
|
+
for (const rule of ruleMap.values()) {
|
|
9591
|
+
if (rule && !rule._meta.isTrivia) {
|
|
9592
|
+
;
|
|
9593
|
+
rule._meta.grammarScanSkip = gScanSkip;
|
|
9594
|
+
}
|
|
9595
|
+
}
|
|
9596
|
+
}
|
|
9199
9597
|
return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {}, ...gScanSkip ? { scanSkip: gScanSkip } : {} };
|
|
9200
9598
|
};
|
|
9201
9599
|
const compileRulesFactory = (init, label2) => {
|
|
9202
9600
|
const evaluated = evaluateRulesFactory(init, label2);
|
|
9203
9601
|
if (!evaluated) return null;
|
|
9204
9602
|
const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, recovery, coverage: grammarCoverage });
|
|
9205
|
-
|
|
9206
|
-
warn(init.start, `${label2}: rule map couldn't be inlined`);
|
|
9207
|
-
return null;
|
|
9208
|
-
}
|
|
9209
|
-
return { replacement: compiled.replacement, ruleMap: evaluated.ruleMap, ...compiled.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {}, ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {} };
|
|
9603
|
+
return { replacement: compiled?.replacement ?? null, ruleMap: evaluated.ruleMap, ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {}, ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {} };
|
|
9210
9604
|
};
|
|
9211
9605
|
const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
|
|
9212
9606
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
@@ -9301,6 +9695,17 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9301
9695
|
}
|
|
9302
9696
|
return out;
|
|
9303
9697
|
};
|
|
9698
|
+
const carriedRuleMaps = (items) => {
|
|
9699
|
+
const out = [];
|
|
9700
|
+
const add = (it) => {
|
|
9701
|
+
if (isIR(it)) out.push(evalRuleMapIR(it.ir));
|
|
9702
|
+
};
|
|
9703
|
+
for (const it of items) {
|
|
9704
|
+
if (isSpread(it)) for (const p of importedPieces(it.__spreadLocal) ?? []) add(p);
|
|
9705
|
+
else add(it);
|
|
9706
|
+
}
|
|
9707
|
+
return out;
|
|
9708
|
+
};
|
|
9304
9709
|
const materializeLeafCoverage = (items, localRules, localNs, composing, captureTerminals = false) => {
|
|
9305
9710
|
const sources = [];
|
|
9306
9711
|
const add = (item) => {
|
|
@@ -9332,12 +9737,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9332
9737
|
}
|
|
9333
9738
|
return pieces;
|
|
9334
9739
|
};
|
|
9335
|
-
const localCarried = (rm, label2, composing) => {
|
|
9740
|
+
const localCarried = (rm, label2, composing, scanSkip) => {
|
|
9336
9741
|
const entries = [...rm];
|
|
9337
9742
|
const ns = nsFor(label2);
|
|
9338
|
-
const ir = serializeRuleMap(entries);
|
|
9743
|
+
const ir = serializeRuleMap(entries, scanSkip);
|
|
9339
9744
|
if (ir) return { carried: [{ ns, ir }] };
|
|
9340
|
-
const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, recovery });
|
|
9745
|
+
const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, recovery });
|
|
9341
9746
|
return p ? { carried: [p] } : null;
|
|
9342
9747
|
};
|
|
9343
9748
|
const argPieces = (arg, label2, composing) => {
|
|
@@ -9362,18 +9767,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9362
9767
|
}
|
|
9363
9768
|
}
|
|
9364
9769
|
if (isRulesCall(arg)) {
|
|
9365
|
-
const
|
|
9366
|
-
|
|
9367
|
-
const a1 = rulesArgs[1];
|
|
9368
|
-
const optionsFirst = a0?.type === "ObjectExpression";
|
|
9369
|
-
const factory = optionsFirst ? a1 : a0;
|
|
9370
|
-
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
9371
|
-
return rm ? localCarried(rm, label2, composing) : null;
|
|
9770
|
+
const evaluated = evaluateRulesFactory(arg, label2);
|
|
9771
|
+
return evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
|
|
9372
9772
|
}
|
|
9373
9773
|
if (arg.type === "Identifier") {
|
|
9374
9774
|
const name = arg.name;
|
|
9375
9775
|
const rm = localRuleMaps.get(name);
|
|
9376
|
-
if (rm) return localCarried(rm, label2, composing);
|
|
9776
|
+
if (rm) return localCarried(rm, label2, composing, localGrammarScanSkip.get(name));
|
|
9377
9777
|
const composed = localComposedCarried.get(name);
|
|
9378
9778
|
if (composed) return { carried: composed };
|
|
9379
9779
|
const pieces = importedPieces(name);
|
|
@@ -9439,6 +9839,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9439
9839
|
}
|
|
9440
9840
|
carried.push(...r.carried);
|
|
9441
9841
|
}
|
|
9842
|
+
runFusedGatingDiagnostic(() => carriedRuleMaps(carried));
|
|
9442
9843
|
const pieces = materializeCarried(carried, composing);
|
|
9443
9844
|
try {
|
|
9444
9845
|
return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
|
|
@@ -9487,6 +9888,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9487
9888
|
}
|
|
9488
9889
|
try {
|
|
9489
9890
|
const localNs = nsFor(`composeLeaf${init.start}`);
|
|
9891
|
+
runFusedGatingDiagnostic(() => [...carriedRuleMaps(carried), [...localRules]]);
|
|
9490
9892
|
const plainLocalPiece = compileLinkable([...localRules], localNs, { ...composing ? { trivia: composing } : {}, ...localScanSkip ? { scanSkip: localScanSkip } : {}, recovery });
|
|
9491
9893
|
if (!plainLocalPiece) {
|
|
9492
9894
|
warn(init.start, "composeLeaf(): local rules could not be statically compiled");
|
|
@@ -9575,7 +9977,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9575
9977
|
const refEntry = scope.get(varName);
|
|
9576
9978
|
const refCombi = refEntry?.combi ?? null;
|
|
9577
9979
|
if (refCombi) {
|
|
9578
|
-
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage });
|
|
9980
|
+
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage, gating: { entryName: varName } });
|
|
9579
9981
|
if (compiled2.inlineExpression === null) {
|
|
9580
9982
|
warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
|
|
9581
9983
|
continue;
|
|
@@ -9590,17 +9992,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9590
9992
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
9591
9993
|
if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
|
|
9592
9994
|
if (compiledRules.scanSkip) localGrammarScanSkip.set(varName, compiledRules.scanSkip);
|
|
9995
|
+
const ns = nsFor(varName);
|
|
9996
|
+
const pieces = exportPrefix || compiledRules.replacement === null ? compileLinkable([...compiledRules.ruleMap], ns, {
|
|
9997
|
+
...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
|
|
9998
|
+
recovery
|
|
9999
|
+
}) : null;
|
|
10000
|
+
if (compiledRules.replacement === null) {
|
|
10001
|
+
if (!pieces || !hasExternalRuleRef([...compiledRules.ruleMap])) {
|
|
10002
|
+
warn(init.start, `${varName}: rule map couldn't be inlined`);
|
|
10003
|
+
continue;
|
|
10004
|
+
}
|
|
10005
|
+
keepMacroImport = true;
|
|
10006
|
+
}
|
|
10007
|
+
const source = compiledRules.replacement ?? code.slice(init.start, init.end);
|
|
9593
10008
|
let replacement = withCoverageDefinitions(
|
|
9594
|
-
|
|
9595
|
-
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(
|
|
10009
|
+
source,
|
|
10010
|
+
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(source)
|
|
9596
10011
|
);
|
|
9597
|
-
if (exportPrefix) {
|
|
9598
|
-
const
|
|
9599
|
-
|
|
9600
|
-
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
9601
|
-
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
9602
|
-
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
9603
|
-
}
|
|
10012
|
+
if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
10013
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
|
|
10014
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
9604
10015
|
}
|
|
9605
10016
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
9606
10017
|
continue;
|
|
@@ -9643,7 +10054,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9643
10054
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
9644
10055
|
continue;
|
|
9645
10056
|
}
|
|
9646
|
-
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage });
|
|
10057
|
+
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage, gating: { entryName: varName } });
|
|
9647
10058
|
if (compiled.inlineExpression === null) {
|
|
9648
10059
|
warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
|
|
9649
10060
|
continue;
|
|
@@ -9662,6 +10073,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9662
10073
|
}
|
|
9663
10074
|
const compiledRules = compileRulesFactory(init, "{ \u2026 }");
|
|
9664
10075
|
if (!compiledRules) continue;
|
|
10076
|
+
if (compiledRules.replacement === null) {
|
|
10077
|
+
warn(init.start, `{ \u2026 }: rule map couldn't be inlined`);
|
|
10078
|
+
continue;
|
|
10079
|
+
}
|
|
9665
10080
|
const pattern = d.id;
|
|
9666
10081
|
const bindings = [];
|
|
9667
10082
|
let allOk = true;
|
|
@@ -9703,7 +10118,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9703
10118
|
}
|
|
9704
10119
|
if (replacements.length === 0 && warnings.length === 0) return null;
|
|
9705
10120
|
for (const imp of macroImports) {
|
|
9706
|
-
imp.fullyResolved = !anyUnresolved;
|
|
10121
|
+
imp.fullyResolved = !anyUnresolved && !keepMacroImport;
|
|
9707
10122
|
}
|
|
9708
10123
|
const ms = new import_magic_string.default(code);
|
|
9709
10124
|
if (!anyUnresolved) {
|