parseman 0.33.0 → 0.35.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/README.md +94 -72
- 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 +1 -1
- 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.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/cst/capture-buffer.d.ts.map +1 -1
- package/dist/index.cjs +462 -105
- 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 +462 -105
- 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 +529 -135
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +529 -135
- package/dist/plugin/index.js.map +4 -4
- package/dist/run/index.cjs +216 -0
- package/dist/run/index.cjs.map +7 -0
- package/dist/run/index.d.ts +28 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +193 -0
- package/dist/run/index.js.map +7 -0
- 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 +11 -1
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
absolutize: () => absolutize,
|
|
28
28
|
absolutizeCST: () => absolutizeCST,
|
|
29
29
|
analyzeGating: () => analyzeGating,
|
|
30
|
+
analyzeGatingRules: () => analyzeGatingRules,
|
|
30
31
|
annotateSpan: () => annotateSpan,
|
|
31
32
|
applyEdit: () => applyEdit,
|
|
32
33
|
attempt: () => attempt,
|
|
@@ -74,10 +75,12 @@ __export(index_exports, {
|
|
|
74
75
|
not: () => not,
|
|
75
76
|
offsetToLineCol: () => offsetToLineCol,
|
|
76
77
|
oneOrMore: () => oneOrMore,
|
|
78
|
+
oneOrMoreSep: () => oneOrMoreSep,
|
|
77
79
|
optional: () => optional,
|
|
78
80
|
parse: () => parse,
|
|
79
81
|
parseDoc: () => parseDoc,
|
|
80
82
|
parser: () => parser,
|
|
83
|
+
peek: () => peek,
|
|
81
84
|
ref: () => ref,
|
|
82
85
|
regex: () => regex,
|
|
83
86
|
relativize: () => relativize,
|
|
@@ -102,6 +105,11 @@ __export(index_exports, {
|
|
|
102
105
|
module.exports = __toCommonJS(index_exports);
|
|
103
106
|
|
|
104
107
|
// src/combinators/first-set.ts
|
|
108
|
+
function resolveNamedRef(p, resolve) {
|
|
109
|
+
if (resolve === void 0) return void 0;
|
|
110
|
+
const name = p._ruleName;
|
|
111
|
+
return name === void 0 ? void 0 : resolve(name);
|
|
112
|
+
}
|
|
105
113
|
function union(a, b) {
|
|
106
114
|
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
107
115
|
if (a.kind === "empty") return b;
|
|
@@ -139,10 +147,10 @@ function any() {
|
|
|
139
147
|
function empty() {
|
|
140
148
|
return { kind: "empty" };
|
|
141
149
|
}
|
|
142
|
-
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
150
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
143
151
|
if (seen.has(p)) return true;
|
|
144
152
|
seen.add(p);
|
|
145
|
-
const me = (c) => matchesEmpty(c, seen);
|
|
153
|
+
const me = (c) => matchesEmpty(c, seen, resolve);
|
|
146
154
|
const d = p._def;
|
|
147
155
|
switch (d.tag) {
|
|
148
156
|
case "literal":
|
|
@@ -159,8 +167,16 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
159
167
|
case "many":
|
|
160
168
|
case "optional":
|
|
161
169
|
case "not":
|
|
170
|
+
case "peek":
|
|
162
171
|
return true;
|
|
163
172
|
// zero repetitions / absent / lookahead
|
|
173
|
+
// Default `sepBy` is `(item (sep item)*)?` — it MATCHES THE EMPTY STRING.
|
|
174
|
+
// Any `min >= 1` requires that many ITEMS, so it is nullable only when the
|
|
175
|
+
// item is. (Keying this off `min === 1` reported every `{ min: 2 }` list as
|
|
176
|
+
// nullable — safe, but wrong, and it put a bogus `nullable-prefix` note on
|
|
177
|
+
// the gating diagnostic for a list that can never match empty.)
|
|
178
|
+
case "sepBy":
|
|
179
|
+
return d.min >= 1 ? me(d.parser) : true;
|
|
164
180
|
case "oneOrMore":
|
|
165
181
|
return me(d.parser);
|
|
166
182
|
case "sequence":
|
|
@@ -184,7 +200,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
184
200
|
try {
|
|
185
201
|
return me(d.thunk());
|
|
186
202
|
} catch {
|
|
187
|
-
|
|
203
|
+
const t = resolveNamedRef(p, resolve);
|
|
204
|
+
return t ? me(t) : true;
|
|
188
205
|
}
|
|
189
206
|
default:
|
|
190
207
|
return true;
|
|
@@ -193,18 +210,45 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
193
210
|
function isZeroWidthAssertion(p) {
|
|
194
211
|
return p._def.tag === "not";
|
|
195
212
|
}
|
|
213
|
+
function isPositiveLookahead(p) {
|
|
214
|
+
return p._def.tag === "peek";
|
|
215
|
+
}
|
|
216
|
+
function narrowBy(acc, constraint) {
|
|
217
|
+
if (constraint.kind === "any") return acc;
|
|
218
|
+
if (acc === null) return constraint;
|
|
219
|
+
if (acc.kind === "any") return constraint;
|
|
220
|
+
if (acc.kind === "empty" || constraint.kind === "empty") return { kind: "empty" };
|
|
221
|
+
const ranges = [];
|
|
222
|
+
for (const a of acc.ranges) for (const b of constraint.ranges) {
|
|
223
|
+
const lo = Math.max(a.lo, b.lo);
|
|
224
|
+
const hi = Math.min(a.hi, b.hi);
|
|
225
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
226
|
+
}
|
|
227
|
+
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
228
|
+
}
|
|
229
|
+
function applyAssertion(fs, assertion) {
|
|
230
|
+
if (assertion === null) return fs;
|
|
231
|
+
if (fs.kind === "empty") return assertion;
|
|
232
|
+
return narrowBy(fs, assertion) ?? fs;
|
|
233
|
+
}
|
|
196
234
|
function sequenceFirstSet(parsers) {
|
|
197
235
|
let fs = empty();
|
|
236
|
+
let assertion = null;
|
|
198
237
|
for (const p of parsers) {
|
|
238
|
+
if (isPositiveLookahead(p)) {
|
|
239
|
+
assertion = narrowBy(assertion, p._meta.firstSet);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
199
242
|
if (!isZeroWidthAssertion(p)) fs = union(fs, p._meta.firstSet);
|
|
200
|
-
if (!matchesEmpty(p)) return fs;
|
|
243
|
+
if (!matchesEmpty(p)) return applyAssertion(fs, assertion);
|
|
201
244
|
}
|
|
202
|
-
return fs;
|
|
245
|
+
return applyAssertion(fs, assertion);
|
|
203
246
|
}
|
|
204
|
-
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
247
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
205
248
|
if (seen.has(p)) return any();
|
|
206
249
|
seen.add(p);
|
|
207
|
-
const fs = (c) => firstSetOf(c, seen);
|
|
250
|
+
const fs = (c) => firstSetOf(c, seen, resolve);
|
|
251
|
+
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve);
|
|
208
252
|
const d = p._def;
|
|
209
253
|
switch (d.tag) {
|
|
210
254
|
case "literal":
|
|
@@ -216,7 +260,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
216
260
|
try {
|
|
217
261
|
return fs(d.thunk());
|
|
218
262
|
} catch {
|
|
219
|
-
|
|
263
|
+
const t = resolveNamedRef(p, resolve);
|
|
264
|
+
return t ? fs(t) : any();
|
|
220
265
|
}
|
|
221
266
|
case "choice": {
|
|
222
267
|
let out = empty();
|
|
@@ -225,11 +270,20 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
225
270
|
}
|
|
226
271
|
case "sequence": {
|
|
227
272
|
let out = empty();
|
|
273
|
+
let assertion = null;
|
|
228
274
|
for (const term of d.parsers) {
|
|
275
|
+
if (isPositiveLookahead(term)) {
|
|
276
|
+
assertion = narrowBy(assertion, fs(term));
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
229
279
|
if (!isZeroWidthAssertion(term)) out = union(out, fs(term));
|
|
230
|
-
if (!
|
|
280
|
+
if (!empties(term)) return applyAssertion(out, assertion);
|
|
231
281
|
}
|
|
232
|
-
return out;
|
|
282
|
+
return applyAssertion(out, assertion);
|
|
283
|
+
}
|
|
284
|
+
case "peek": {
|
|
285
|
+
const inner = d.parser;
|
|
286
|
+
return empties(inner) ? any() : fs(inner);
|
|
233
287
|
}
|
|
234
288
|
case "oneOrMore":
|
|
235
289
|
case "many":
|
|
@@ -245,6 +299,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
245
299
|
return fs(d.parser);
|
|
246
300
|
case "sepBy":
|
|
247
301
|
return fs(d.parser);
|
|
302
|
+
// both min 0 and min 1 start with the item
|
|
248
303
|
case "skip":
|
|
249
304
|
return fs(d.main);
|
|
250
305
|
default:
|
|
@@ -386,28 +441,28 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
|
|
|
386
441
|
else if (len === 1) {
|
|
387
442
|
b[keySingle] = arr[0];
|
|
388
443
|
b[keyMulti] = void 0;
|
|
389
|
-
} else arr.length = len;
|
|
444
|
+
} else if (arr.length !== len) arr.length = len;
|
|
390
445
|
return;
|
|
391
446
|
}
|
|
392
447
|
if (len === 0) b[keySingle] = void 0;
|
|
393
448
|
}
|
|
394
449
|
function rollbackCstCapture(ctx, mark) {
|
|
395
|
-
if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
|
|
450
|
+
if (ctx._errors && mark.errors !== void 0 && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors;
|
|
396
451
|
const b = ctx._cstBuf;
|
|
397
452
|
if (b) {
|
|
398
453
|
rollbackBufList(b, "raw", "rawSingle", mark.raw);
|
|
399
454
|
rollbackBufList(b, "ch", "single", mark.leaves);
|
|
400
455
|
if (b.tl) {
|
|
401
456
|
if (mark.tlog === 0) b.tl = void 0;
|
|
402
|
-
else b.tl.length = mark.tlog;
|
|
457
|
+
else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog;
|
|
403
458
|
}
|
|
404
|
-
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
459
|
+
if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields;
|
|
405
460
|
return;
|
|
406
461
|
}
|
|
407
|
-
if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
|
|
408
|
-
if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
|
|
409
|
-
if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
|
|
410
|
-
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
462
|
+
if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw;
|
|
463
|
+
if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog;
|
|
464
|
+
if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves;
|
|
465
|
+
if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields;
|
|
411
466
|
}
|
|
412
467
|
function pushCstTriviaEntry(ctx, start, end, kindIndex) {
|
|
413
468
|
const insertIdx = cstRawLen(ctx);
|
|
@@ -929,9 +984,46 @@ function firstSetFromRegex(source) {
|
|
|
929
984
|
return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
|
|
930
985
|
}
|
|
931
986
|
|
|
987
|
+
// src/combinators/case-fold.ts
|
|
988
|
+
function canonical(ch2) {
|
|
989
|
+
const u = ch2.toUpperCase();
|
|
990
|
+
if (u.length !== 1) return ch2;
|
|
991
|
+
if (ch2.codePointAt(0) >= 128 && u.codePointAt(0) < 128) return ch2;
|
|
992
|
+
return u;
|
|
993
|
+
}
|
|
994
|
+
var foldIndex = null;
|
|
995
|
+
function buildFoldIndex() {
|
|
996
|
+
const byCanonical = /* @__PURE__ */ new Map();
|
|
997
|
+
for (let cp = 0; cp < 65536; cp++) {
|
|
998
|
+
if (cp >= 55296 && cp <= 57343) continue;
|
|
999
|
+
const key = canonical(String.fromCharCode(cp));
|
|
1000
|
+
const bucket = byCanonical.get(key);
|
|
1001
|
+
if (bucket) bucket.push(cp);
|
|
1002
|
+
else byCanonical.set(key, [cp]);
|
|
1003
|
+
}
|
|
1004
|
+
const index = /* @__PURE__ */ new Map();
|
|
1005
|
+
for (const members of byCanonical.values()) {
|
|
1006
|
+
if (members.length < 2) continue;
|
|
1007
|
+
for (const cp of members) index.set(cp, members);
|
|
1008
|
+
}
|
|
1009
|
+
return index;
|
|
1010
|
+
}
|
|
1011
|
+
function caseFoldVariants(cp) {
|
|
1012
|
+
if (cp < 128) {
|
|
1013
|
+
if (cp >= 65 && cp <= 90) return [cp, cp + 32];
|
|
1014
|
+
if (cp >= 97 && cp <= 122) return [cp, cp - 32];
|
|
1015
|
+
return [cp];
|
|
1016
|
+
}
|
|
1017
|
+
if (cp > 65535) return [cp];
|
|
1018
|
+
foldIndex ??= buildFoldIndex();
|
|
1019
|
+
return foldIndex.get(cp) ?? [cp];
|
|
1020
|
+
}
|
|
1021
|
+
|
|
932
1022
|
// src/combinators/keywords.ts
|
|
933
|
-
function word(str,
|
|
934
|
-
|
|
1023
|
+
function word(str, boundaryOrOpts, opts) {
|
|
1024
|
+
const boundary = typeof boundaryOrOpts === "string" ? boundaryOrOpts : "_0-9A-Za-z";
|
|
1025
|
+
const rest = typeof boundaryOrOpts === "object" && boundaryOrOpts !== null ? boundaryOrOpts : opts;
|
|
1026
|
+
return keywords([str], { ...rest, boundary });
|
|
935
1027
|
}
|
|
936
1028
|
function makeWord(boundary = "_0-9A-Za-z") {
|
|
937
1029
|
return (str) => keywords([str], { boundary });
|
|
@@ -943,7 +1035,7 @@ function keywords(words, opts = {}) {
|
|
|
943
1035
|
const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
|
|
944
1036
|
const alt = sorted.map(escapeRe).join("|");
|
|
945
1037
|
const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
|
|
946
|
-
const flags = opts.caseInsensitive ? "
|
|
1038
|
+
const flags = opts.caseInsensitive ? "iy" : "uy";
|
|
947
1039
|
const re = new RegExp(`(?:${alt})${boundary}`, flags);
|
|
948
1040
|
let firstSet2 = empty();
|
|
949
1041
|
for (const w of sorted) {
|
|
@@ -951,10 +1043,7 @@ function keywords(words, opts = {}) {
|
|
|
951
1043
|
const cp = w.codePointAt(0);
|
|
952
1044
|
firstSet2 = union(firstSet2, fromChar(cp));
|
|
953
1045
|
if (opts.caseInsensitive) {
|
|
954
|
-
const
|
|
955
|
-
const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
|
|
956
|
-
if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
|
|
957
|
-
if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
|
|
1046
|
+
for (const v2 of caseFoldVariants(cp)) firstSet2 = union(firstSet2, fromChar(v2));
|
|
958
1047
|
}
|
|
959
1048
|
}
|
|
960
1049
|
const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
|
|
@@ -1066,6 +1155,10 @@ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSet
|
|
|
1066
1155
|
|
|
1067
1156
|
// src/combinators/expect.ts
|
|
1068
1157
|
function deriveExpected(c) {
|
|
1158
|
+
return derive(c, /* @__PURE__ */ new Set());
|
|
1159
|
+
}
|
|
1160
|
+
function derive(c, seen) {
|
|
1161
|
+
const deriveExpected2 = (p) => derive(p, seen);
|
|
1069
1162
|
const def = c._def;
|
|
1070
1163
|
switch (def.tag) {
|
|
1071
1164
|
case "literal":
|
|
@@ -1077,20 +1170,32 @@ function deriveExpected(c) {
|
|
|
1077
1170
|
case "label":
|
|
1078
1171
|
return [def.label];
|
|
1079
1172
|
case "choice":
|
|
1080
|
-
return def.parsers.flatMap(
|
|
1081
|
-
|
|
1082
|
-
|
|
1173
|
+
return def.parsers.flatMap(deriveExpected2);
|
|
1174
|
+
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
1175
|
+
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
1176
|
+
// able to fail first — deriving from term 0 alone named a token the parse
|
|
1177
|
+
// never actually required. A leading `not(…)` is zero-width and contributes
|
|
1178
|
+
// nothing (its own expectation is about what must NOT be here), but it is
|
|
1179
|
+
// nullable, so keep scanning past it. Stop at the first term that must match.
|
|
1180
|
+
case "sequence": {
|
|
1181
|
+
const out = [];
|
|
1182
|
+
for (const term of def.parsers) {
|
|
1183
|
+
if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
|
|
1184
|
+
if (!matchesEmpty(term)) break;
|
|
1185
|
+
}
|
|
1186
|
+
return out;
|
|
1187
|
+
}
|
|
1083
1188
|
case "attempt":
|
|
1084
|
-
return
|
|
1189
|
+
return deriveExpected2(def.parser);
|
|
1085
1190
|
// Delegating wrappers whose first-token failure is their inner parser's — must
|
|
1086
1191
|
// pass through so a start-failure (and the first-set-miss fast-path in attempt/
|
|
1087
1192
|
// node, which reads this) names the real expected token, not the wrapper tag.
|
|
1088
1193
|
case "field":
|
|
1089
1194
|
case "withCtx":
|
|
1090
1195
|
case "recover":
|
|
1091
|
-
return
|
|
1196
|
+
return deriveExpected2(def.parser);
|
|
1092
1197
|
case "skip":
|
|
1093
|
-
return
|
|
1198
|
+
return deriveExpected2(def.main);
|
|
1094
1199
|
case "expect":
|
|
1095
1200
|
return def.expected;
|
|
1096
1201
|
case "node":
|
|
@@ -1103,13 +1208,18 @@ function deriveExpected(c) {
|
|
|
1103
1208
|
case "oneOrMore":
|
|
1104
1209
|
case "transform":
|
|
1105
1210
|
case "not":
|
|
1106
|
-
|
|
1211
|
+
case "peek":
|
|
1212
|
+
return deriveExpected2(def.parser);
|
|
1107
1213
|
case "lazy": {
|
|
1108
1214
|
const name = c._ruleName;
|
|
1215
|
+
if (seen.has(c)) return name ? [name] : [];
|
|
1216
|
+
seen.add(c);
|
|
1109
1217
|
try {
|
|
1110
|
-
return
|
|
1218
|
+
return deriveExpected2(def.thunk());
|
|
1111
1219
|
} catch {
|
|
1112
1220
|
return name ? [name] : [];
|
|
1221
|
+
} finally {
|
|
1222
|
+
seen.delete(c);
|
|
1113
1223
|
}
|
|
1114
1224
|
}
|
|
1115
1225
|
default:
|
|
@@ -1240,14 +1350,14 @@ function choice(...args) {
|
|
|
1240
1350
|
const result = parsers[i].parse(input, pos, ctx);
|
|
1241
1351
|
if (!result.ok) {
|
|
1242
1352
|
rollbackCstCapture(ctx, mark);
|
|
1243
|
-
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
1353
|
+
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1244
1354
|
expected.push(...result.expected);
|
|
1245
1355
|
continue;
|
|
1246
1356
|
}
|
|
1247
1357
|
const checks = autoNot[i];
|
|
1248
1358
|
if (checks && autoNotFires(input, result.span.end, checks)) {
|
|
1249
1359
|
rollbackCstCapture(ctx, mark);
|
|
1250
|
-
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
1360
|
+
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1251
1361
|
continue;
|
|
1252
1362
|
}
|
|
1253
1363
|
return result;
|
|
@@ -1574,7 +1684,14 @@ function saveTriviaMark(ctx) {
|
|
|
1574
1684
|
}
|
|
1575
1685
|
function rollbackTrivia(ctx, mark) {
|
|
1576
1686
|
rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
|
|
1577
|
-
if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
|
|
1687
|
+
if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
|
|
1688
|
+
}
|
|
1689
|
+
function saveLookaheadMark(ctx) {
|
|
1690
|
+
return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
|
|
1691
|
+
}
|
|
1692
|
+
function rollbackLookahead(ctx, mark) {
|
|
1693
|
+
rollbackTrivia(ctx, mark);
|
|
1694
|
+
if (ctx._probe) ctx._probe.best = mark.probeBest;
|
|
1578
1695
|
}
|
|
1579
1696
|
function scanWithLabels(input, cur, ctx) {
|
|
1580
1697
|
const triviaP = ctx.trivia;
|
|
@@ -1935,7 +2052,7 @@ function attempt(parser2) {
|
|
|
1935
2052
|
const result = parser2.parse(input, pos, ctx);
|
|
1936
2053
|
if (result.ok) return result;
|
|
1937
2054
|
rollbackCstCapture(ctx, mark);
|
|
1938
|
-
if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
|
|
2055
|
+
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
1939
2056
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
1940
2057
|
}
|
|
1941
2058
|
};
|
|
@@ -1973,13 +2090,26 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1973
2090
|
}
|
|
1974
2091
|
return { value: result.value, end: result.span.end };
|
|
1975
2092
|
}
|
|
1976
|
-
function
|
|
2093
|
+
function resolveBounds(what, opts) {
|
|
2094
|
+
const min = opts.min ?? 0;
|
|
2095
|
+
const max = opts.max ?? Infinity;
|
|
2096
|
+
const bad = (msg) => {
|
|
2097
|
+
throw new RangeError(`parseman: ${what} ${msg}`);
|
|
2098
|
+
};
|
|
2099
|
+
if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
|
|
2100
|
+
if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
|
|
2101
|
+
if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
|
|
2102
|
+
return { min, max };
|
|
2103
|
+
}
|
|
2104
|
+
function many(combinator, opts = {}) {
|
|
2105
|
+
const { min, max } = resolveBounds("many()", opts);
|
|
2106
|
+
if (min >= 1) return atLeast(combinator, min, max);
|
|
1977
2107
|
const meta = {
|
|
1978
2108
|
firstSet: combinator._meta.firstSet,
|
|
1979
2109
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1980
2110
|
isTrivia: false
|
|
1981
2111
|
};
|
|
1982
|
-
const def = { tag: "many", parser: combinator, min: 0 };
|
|
2112
|
+
const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
|
|
1983
2113
|
let expected;
|
|
1984
2114
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1985
2115
|
return {
|
|
@@ -1989,7 +2119,9 @@ function many(combinator) {
|
|
|
1989
2119
|
parse(input, pos, ctx) {
|
|
1990
2120
|
const values = def.valueUnused ? void 0 : [];
|
|
1991
2121
|
let cur = pos;
|
|
2122
|
+
let count = 0;
|
|
1992
2123
|
while (cur < input.length) {
|
|
2124
|
+
if (count >= max) break;
|
|
1993
2125
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1994
2126
|
if (item === "stop") break;
|
|
1995
2127
|
if ("fail" in item) {
|
|
@@ -2000,23 +2132,28 @@ function many(combinator) {
|
|
|
2000
2132
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
2001
2133
|
if (values !== void 0) values.push(error);
|
|
2002
2134
|
captureError(ctx, error);
|
|
2135
|
+
count++;
|
|
2003
2136
|
cur = end;
|
|
2004
2137
|
continue;
|
|
2005
2138
|
}
|
|
2006
2139
|
if (values !== void 0) values.push(item.value);
|
|
2140
|
+
count++;
|
|
2007
2141
|
cur = item.end;
|
|
2008
2142
|
}
|
|
2009
2143
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
2010
2144
|
}
|
|
2011
2145
|
};
|
|
2012
2146
|
}
|
|
2013
|
-
function oneOrMore(combinator) {
|
|
2147
|
+
function oneOrMore(combinator, opts = {}) {
|
|
2148
|
+
return many(combinator, { ...opts, min: opts.min ?? 1 });
|
|
2149
|
+
}
|
|
2150
|
+
function atLeast(combinator, min, max) {
|
|
2014
2151
|
const meta = {
|
|
2015
2152
|
firstSet: combinator._meta.firstSet,
|
|
2016
2153
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
2017
2154
|
isTrivia: false
|
|
2018
2155
|
};
|
|
2019
|
-
const def = { tag: "oneOrMore", parser: combinator, min:
|
|
2156
|
+
const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
|
|
2020
2157
|
let expected;
|
|
2021
2158
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
2022
2159
|
return {
|
|
@@ -2028,7 +2165,19 @@ function oneOrMore(combinator) {
|
|
|
2028
2165
|
if (!first.ok) return first;
|
|
2029
2166
|
const values = def.valueUnused ? void 0 : [first.value];
|
|
2030
2167
|
let cur = first.span.end;
|
|
2168
|
+
let count = 1;
|
|
2169
|
+
while (count < min) {
|
|
2170
|
+
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2171
|
+
if (item === "stop" || "fail" in item) {
|
|
2172
|
+
expected ??= deriveExpected(combinator);
|
|
2173
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
2174
|
+
}
|
|
2175
|
+
if (values !== void 0) values.push(item.value);
|
|
2176
|
+
cur = item.end;
|
|
2177
|
+
count++;
|
|
2178
|
+
}
|
|
2031
2179
|
while (cur < input.length) {
|
|
2180
|
+
if (count >= max) break;
|
|
2032
2181
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2033
2182
|
if (item === "stop") break;
|
|
2034
2183
|
if ("fail" in item) {
|
|
@@ -2039,10 +2188,12 @@ function oneOrMore(combinator) {
|
|
|
2039
2188
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
2040
2189
|
if (values !== void 0) values.push(error);
|
|
2041
2190
|
captureError(ctx, error);
|
|
2191
|
+
count++;
|
|
2042
2192
|
cur = end;
|
|
2043
2193
|
continue;
|
|
2044
2194
|
}
|
|
2045
2195
|
if (values !== void 0) values.push(item.value);
|
|
2196
|
+
count++;
|
|
2046
2197
|
cur = item.end;
|
|
2047
2198
|
}
|
|
2048
2199
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
@@ -2072,17 +2223,33 @@ function optional(combinator) {
|
|
|
2072
2223
|
}
|
|
2073
2224
|
};
|
|
2074
2225
|
}
|
|
2075
|
-
function
|
|
2226
|
+
function oneOrMoreSep(combinator, separator, opts = {}) {
|
|
2227
|
+
return sepBy(combinator, separator, { ...opts, min: opts.min ?? 1 });
|
|
2228
|
+
}
|
|
2229
|
+
function sepBy(combinator, separator, opts = {}) {
|
|
2230
|
+
const { min, max } = resolveBounds("sepBy()", opts);
|
|
2231
|
+
const trailing = opts.trailing ?? "forbid";
|
|
2076
2232
|
const meta = {
|
|
2077
2233
|
firstSet: combinator._meta.firstSet,
|
|
2078
2234
|
canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
|
|
2079
2235
|
isTrivia: false
|
|
2080
2236
|
};
|
|
2081
2237
|
let expected;
|
|
2238
|
+
const failAt2 = (at) => {
|
|
2239
|
+
expected ??= deriveExpected(combinator);
|
|
2240
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
|
|
2241
|
+
};
|
|
2082
2242
|
return {
|
|
2083
2243
|
_tag: "sepBy",
|
|
2084
2244
|
_meta: meta,
|
|
2085
|
-
_def: {
|
|
2245
|
+
_def: {
|
|
2246
|
+
tag: "sepBy",
|
|
2247
|
+
parser: combinator,
|
|
2248
|
+
separator,
|
|
2249
|
+
min,
|
|
2250
|
+
...max === Infinity ? {} : { max },
|
|
2251
|
+
...trailing === "forbid" ? {} : { trailing }
|
|
2252
|
+
},
|
|
2086
2253
|
parse(input, pos, ctx) {
|
|
2087
2254
|
const first = combinator.parse(input, pos, ctx);
|
|
2088
2255
|
const values = [];
|
|
@@ -2093,6 +2260,7 @@ function sepBy(combinator, separator) {
|
|
|
2093
2260
|
} else {
|
|
2094
2261
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2095
2262
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2263
|
+
if (min >= 1) return failAt2(pos);
|
|
2096
2264
|
return { ok: true, value: [], span: { start: pos, end: pos } };
|
|
2097
2265
|
}
|
|
2098
2266
|
expected ??= deriveExpected(combinator);
|
|
@@ -2102,6 +2270,7 @@ function sepBy(combinator, separator) {
|
|
|
2102
2270
|
cur = rec.end;
|
|
2103
2271
|
}
|
|
2104
2272
|
while (cur < input.length) {
|
|
2273
|
+
if (values.length >= max) break;
|
|
2105
2274
|
const loopMark = saveTriviaMark(ctx);
|
|
2106
2275
|
let sepPos = cur;
|
|
2107
2276
|
if (ctx.trivia) {
|
|
@@ -2118,6 +2287,7 @@ function sepBy(combinator, separator) {
|
|
|
2118
2287
|
rollbackTrivia(ctx, loopMark);
|
|
2119
2288
|
break;
|
|
2120
2289
|
}
|
|
2290
|
+
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
2121
2291
|
let nextPos = sep.span.end;
|
|
2122
2292
|
if (ctx.trivia) {
|
|
2123
2293
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
@@ -2139,12 +2309,18 @@ function sepBy(combinator, separator) {
|
|
|
2139
2309
|
cur = rec.end;
|
|
2140
2310
|
continue;
|
|
2141
2311
|
}
|
|
2312
|
+
if (sepMark !== void 0) {
|
|
2313
|
+
rollbackTrivia(ctx, sepMark);
|
|
2314
|
+
cur = sep.span.end;
|
|
2315
|
+
break;
|
|
2316
|
+
}
|
|
2142
2317
|
rollbackTrivia(ctx, loopMark);
|
|
2143
2318
|
break;
|
|
2144
2319
|
}
|
|
2145
2320
|
values.push(next.value);
|
|
2146
2321
|
cur = next.span.end;
|
|
2147
2322
|
}
|
|
2323
|
+
if (values.length < min) return failAt2(cur);
|
|
2148
2324
|
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
2149
2325
|
}
|
|
2150
2326
|
};
|
|
@@ -2234,6 +2410,7 @@ function markUnusedValues(root) {
|
|
|
2234
2410
|
visit(def.separator, false);
|
|
2235
2411
|
return;
|
|
2236
2412
|
case "not":
|
|
2413
|
+
case "peek":
|
|
2237
2414
|
visit(def.parser, false);
|
|
2238
2415
|
return;
|
|
2239
2416
|
case "grammar":
|
|
@@ -2352,9 +2529,9 @@ function not(combinator) {
|
|
|
2352
2529
|
_meta: meta,
|
|
2353
2530
|
_def: { tag: "not", parser: combinator },
|
|
2354
2531
|
parse(input, pos, ctx) {
|
|
2355
|
-
const mark =
|
|
2532
|
+
const mark = saveLookaheadMark(ctx);
|
|
2356
2533
|
const result = combinator.parse(input, pos, ctx);
|
|
2357
|
-
|
|
2534
|
+
rollbackLookahead(ctx, mark);
|
|
2358
2535
|
if (result.ok) {
|
|
2359
2536
|
return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2360
2537
|
}
|
|
@@ -2363,6 +2540,28 @@ function not(combinator) {
|
|
|
2363
2540
|
};
|
|
2364
2541
|
}
|
|
2365
2542
|
|
|
2543
|
+
// src/combinators/peek.ts
|
|
2544
|
+
function peek(combinator) {
|
|
2545
|
+
const meta = {
|
|
2546
|
+
// A nullable body imposes no first-char constraint — `any()`, not its set.
|
|
2547
|
+
firstSet: matchesEmpty(combinator) ? any() : combinator._meta.firstSet,
|
|
2548
|
+
canMatchNewline: false,
|
|
2549
|
+
isTrivia: false
|
|
2550
|
+
};
|
|
2551
|
+
return {
|
|
2552
|
+
_tag: "peek",
|
|
2553
|
+
_meta: meta,
|
|
2554
|
+
_def: { tag: "peek", parser: combinator },
|
|
2555
|
+
parse(input, pos, ctx) {
|
|
2556
|
+
const mark = saveLookaheadMark(ctx);
|
|
2557
|
+
const result = combinator.parse(input, pos, ctx);
|
|
2558
|
+
rollbackLookahead(ctx, mark);
|
|
2559
|
+
if (result.ok) return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
2560
|
+
return { ok: false, expected: [`peek(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2366
2565
|
// src/compiler/build-arity.ts
|
|
2367
2566
|
var PARAM_LIST_RE = (
|
|
2368
2567
|
// (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
|
|
@@ -2461,6 +2660,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2461
2660
|
case "expect":
|
|
2462
2661
|
case "withCtx":
|
|
2463
2662
|
case "not":
|
|
2663
|
+
case "peek":
|
|
2464
2664
|
return parserHasOwnFields(d.parser, seen);
|
|
2465
2665
|
default:
|
|
2466
2666
|
return false;
|
|
@@ -2500,6 +2700,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2500
2700
|
case "expect":
|
|
2501
2701
|
case "withCtx":
|
|
2502
2702
|
case "not":
|
|
2703
|
+
case "peek":
|
|
2503
2704
|
case "field":
|
|
2504
2705
|
return parserHasTriviaSite(d.parser, seen);
|
|
2505
2706
|
case "choice":
|
|
@@ -2549,6 +2750,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2549
2750
|
case "expect":
|
|
2550
2751
|
case "withCtx":
|
|
2551
2752
|
case "not":
|
|
2753
|
+
case "peek":
|
|
2552
2754
|
return parserEnablesTriviaCapture(d.parser, seen);
|
|
2553
2755
|
// leaf() clears the inner CST/trivia collector and publishes only its own
|
|
2554
2756
|
// terminal result. An inner parser() capture cannot reach this node.
|
|
@@ -2924,7 +3126,7 @@ function leaf(root, fn) {
|
|
|
2924
3126
|
}
|
|
2925
3127
|
|
|
2926
3128
|
// src/version.ts
|
|
2927
|
-
var PARSEMAN_VERSION = "0.
|
|
3129
|
+
var PARSEMAN_VERSION = "0.35.0";
|
|
2928
3130
|
|
|
2929
3131
|
// src/analysis/gating.ts
|
|
2930
3132
|
var isAny = (fs) => fs.kind === "any";
|
|
@@ -2964,14 +3166,15 @@ function firstSetToString(fs) {
|
|
|
2964
3166
|
var ruleNameOf = (p) => p._ruleName;
|
|
2965
3167
|
var SUGGESTIONS = {
|
|
2966
3168
|
"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.",
|
|
2967
|
-
"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.",
|
|
2968
|
-
"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.",
|
|
3169
|
+
"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.",
|
|
3170
|
+
"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.",
|
|
2969
3171
|
"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.",
|
|
2970
3172
|
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
2971
3173
|
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
2972
3174
|
};
|
|
2973
|
-
function classifyBroadArm(arm) {
|
|
3175
|
+
function classifyBroadArm(arm, resolve) {
|
|
2974
3176
|
const seen = /* @__PURE__ */ new Set();
|
|
3177
|
+
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
2975
3178
|
const walk2 = (p) => {
|
|
2976
3179
|
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
2977
3180
|
seen.add(p);
|
|
@@ -2983,6 +3186,10 @@ function classifyBroadArm(arm) {
|
|
|
2983
3186
|
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
2984
3187
|
case "not":
|
|
2985
3188
|
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
3189
|
+
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
3190
|
+
// (or the body is nullable) — walk in for the real cause.
|
|
3191
|
+
case "peek":
|
|
3192
|
+
return walk2(d.parser);
|
|
2986
3193
|
case "scanTo":
|
|
2987
3194
|
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
2988
3195
|
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
@@ -2999,18 +3206,23 @@ function classifyBroadArm(arm) {
|
|
|
2999
3206
|
return walk2(d.parser);
|
|
3000
3207
|
case "lazy": {
|
|
3001
3208
|
const name = p._ruleName;
|
|
3209
|
+
let target;
|
|
3002
3210
|
try {
|
|
3003
|
-
|
|
3004
|
-
if (name !== void 0) return inner ? { cause: "cross-artifact-ref", detail: `via ref g.${name} \u2192 ${inner.detail}` } : null;
|
|
3005
|
-
return inner;
|
|
3211
|
+
target = d.thunk();
|
|
3006
3212
|
} catch {
|
|
3007
|
-
|
|
3213
|
+
const bound = name !== void 0 ? resolve?.(name) : void 0;
|
|
3214
|
+
if (bound === void 0)
|
|
3215
|
+
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
3216
|
+
target = bound;
|
|
3008
3217
|
}
|
|
3218
|
+
const inner = walk2(target);
|
|
3219
|
+
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
3220
|
+
return inner;
|
|
3009
3221
|
}
|
|
3010
3222
|
case "optional":
|
|
3011
3223
|
case "many": {
|
|
3012
3224
|
const inner = walk2(d.parser);
|
|
3013
|
-
return
|
|
3225
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
3014
3226
|
}
|
|
3015
3227
|
case "oneOrMore":
|
|
3016
3228
|
case "transform":
|
|
@@ -3032,13 +3244,13 @@ function classifyBroadArm(arm) {
|
|
|
3032
3244
|
sawNullablePrefix = true;
|
|
3033
3245
|
continue;
|
|
3034
3246
|
}
|
|
3035
|
-
if (isAny(firstSetOf(t))) {
|
|
3247
|
+
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
|
|
3036
3248
|
const inner = walk2(t);
|
|
3037
3249
|
if (sawNullablePrefix)
|
|
3038
|
-
return
|
|
3250
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
3039
3251
|
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
3040
3252
|
}
|
|
3041
|
-
if (matchesEmpty(t)) {
|
|
3253
|
+
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve)) {
|
|
3042
3254
|
sawNullablePrefix = true;
|
|
3043
3255
|
continue;
|
|
3044
3256
|
}
|
|
@@ -3049,7 +3261,7 @@ function classifyBroadArm(arm) {
|
|
|
3049
3261
|
case "choice": {
|
|
3050
3262
|
for (const a of d.parsers) {
|
|
3051
3263
|
const r = walk2(a);
|
|
3052
|
-
if (r) return
|
|
3264
|
+
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
3053
3265
|
}
|
|
3054
3266
|
return null;
|
|
3055
3267
|
}
|
|
@@ -3086,7 +3298,7 @@ function detectAntiPatterns(rule, arms) {
|
|
|
3086
3298
|
kind: "double-not",
|
|
3087
3299
|
rule,
|
|
3088
3300
|
armIndex: i,
|
|
3089
|
-
message: "not(not(...)) hand-rolls automatic first-char gating and MISCOMPILES among shared-first-char sibling arms (its first-set is ANY, poisoning dispatch).
|
|
3301
|
+
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."
|
|
3090
3302
|
});
|
|
3091
3303
|
} else {
|
|
3092
3304
|
out.push({
|
|
@@ -3109,6 +3321,9 @@ function detectAntiPatterns(rule, arms) {
|
|
|
3109
3321
|
return out;
|
|
3110
3322
|
}
|
|
3111
3323
|
function analyzeGating(entry, opts) {
|
|
3324
|
+
return analyzeGatingRules([[ruleNameOf(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3325
|
+
}
|
|
3326
|
+
function analyzeGatingRules(ruleMap, opts) {
|
|
3112
3327
|
const raw = [];
|
|
3113
3328
|
const antiPatterns = [];
|
|
3114
3329
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -3118,7 +3333,7 @@ function analyzeGating(entry, opts) {
|
|
|
3118
3333
|
const d = p._def;
|
|
3119
3334
|
const rule = ruleNameOf(p) ?? enclosingRule;
|
|
3120
3335
|
if (d.tag === "choice") {
|
|
3121
|
-
raw.push({ g: analyzeChoice(p, d, rule), rule });
|
|
3336
|
+
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
3122
3337
|
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
3123
3338
|
}
|
|
3124
3339
|
const rec = d;
|
|
@@ -3135,7 +3350,7 @@ function analyzeGating(entry, opts) {
|
|
|
3135
3350
|
}
|
|
3136
3351
|
for (const k of kids) visit(k, rule);
|
|
3137
3352
|
};
|
|
3138
|
-
visit(
|
|
3353
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf(root) ?? name);
|
|
3139
3354
|
const perRule = /* @__PURE__ */ new Map();
|
|
3140
3355
|
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3141
3356
|
const seenInRule = /* @__PURE__ */ new Map();
|
|
@@ -3151,18 +3366,19 @@ function analyzeGating(entry, opts) {
|
|
|
3151
3366
|
});
|
|
3152
3367
|
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
3153
3368
|
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
3154
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted);
|
|
3369
|
+
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
3155
3370
|
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
3371
|
+
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
3156
3372
|
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
3157
|
-
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, acceptedUnused, choices, antiPatterns };
|
|
3373
|
+
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, deferred, acceptedUnused, choices, antiPatterns };
|
|
3158
3374
|
}
|
|
3159
|
-
function analyzeChoice(p, d, rule) {
|
|
3375
|
+
function analyzeChoice(p, d, rule, resolve) {
|
|
3160
3376
|
const arms = d.parsers;
|
|
3161
3377
|
const shallow = arms.map((a) => a._meta.firstSet);
|
|
3162
|
-
const deep = arms.map((a) => firstSetOf(a));
|
|
3378
|
+
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve));
|
|
3163
3379
|
const anyArms = deep.map((fs, index) => ({ fs, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
3164
|
-
const { cause, detail } = classifyBroadArm(arms[index]);
|
|
3165
|
-
return { index, cause, detail, shallowAnyOnly: false, suggestion: SUGGESTIONS[cause] };
|
|
3380
|
+
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve);
|
|
3381
|
+
return { index, cause, detail, shallowAnyOnly: false, unresolvedExternal: unresolvedExternal === true, suggestion: SUGGESTIONS[cause] };
|
|
3166
3382
|
});
|
|
3167
3383
|
const overlaps = [];
|
|
3168
3384
|
for (let i = 0; i < deep.length; i++)
|
|
@@ -3178,10 +3394,12 @@ function analyzeChoice(p, d, rule) {
|
|
|
3178
3394
|
if (d.disjoint) gates = "yes";
|
|
3179
3395
|
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
3180
3396
|
else gates = "no";
|
|
3397
|
+
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
3181
3398
|
return {
|
|
3182
3399
|
rule,
|
|
3183
3400
|
strategy: d.strategy?.tag ?? "firstMatch",
|
|
3184
3401
|
gates,
|
|
3402
|
+
deferred,
|
|
3185
3403
|
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
3186
3404
|
anyArms,
|
|
3187
3405
|
overlaps
|
|
@@ -3348,6 +3566,7 @@ function children(def, winners) {
|
|
|
3348
3566
|
case "field":
|
|
3349
3567
|
case "grammar":
|
|
3350
3568
|
case "not":
|
|
3569
|
+
case "peek":
|
|
3351
3570
|
case "node":
|
|
3352
3571
|
case "guard":
|
|
3353
3572
|
case "withCtx":
|
|
@@ -5098,9 +5317,9 @@ function planDisjointDispatch(parsers) {
|
|
|
5098
5317
|
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
5099
5318
|
}
|
|
5100
5319
|
function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
|
|
5101
|
-
const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
|
|
5102
|
-
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
|
|
5103
|
-
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
5320
|
+
const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
|
|
5321
|
+
const base = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${mL}) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${mR}) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${mTl}) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
|
|
5322
|
+
return mLg ? `${base}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : base;
|
|
5104
5323
|
}
|
|
5105
5324
|
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
5106
5325
|
if (seen.has(p)) return false;
|
|
@@ -5113,6 +5332,9 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5113
5332
|
case "keywords":
|
|
5114
5333
|
case "guard":
|
|
5115
5334
|
case "not":
|
|
5335
|
+
// peek(): emitted under a non-capturing probe ctx and zero-width on both
|
|
5336
|
+
// outcomes, so it can never leave a partial capture behind.
|
|
5337
|
+
case "peek":
|
|
5116
5338
|
case "trivia":
|
|
5117
5339
|
case "token":
|
|
5118
5340
|
case "leaf":
|
|
@@ -5184,6 +5406,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5184
5406
|
case "leaf":
|
|
5185
5407
|
return true;
|
|
5186
5408
|
case "not":
|
|
5409
|
+
case "peek":
|
|
5187
5410
|
case "guard":
|
|
5188
5411
|
case "trivia":
|
|
5189
5412
|
case "unknown":
|
|
@@ -5310,6 +5533,7 @@ function emitKeywordsFast(def, ctx, pos) {
|
|
|
5310
5533
|
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
5311
5534
|
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
5312
5535
|
if (def.caseInsensitive && def.boundary) return null;
|
|
5536
|
+
if (def.caseInsensitive && def.words.some((w) => w.split("").some((c) => c.charCodeAt(0) > 127))) return null;
|
|
5313
5537
|
let boundary = null;
|
|
5314
5538
|
if (def.boundary) {
|
|
5315
5539
|
let body = def.boundary;
|
|
@@ -5357,7 +5581,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
5357
5581
|
if (fast) return fast;
|
|
5358
5582
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
5359
5583
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
5360
|
-
const flags = def.caseInsensitive ? "
|
|
5584
|
+
const flags = def.caseInsensitive ? "iy" : "uy";
|
|
5361
5585
|
const source = `(?:${alt})${boundary}`;
|
|
5362
5586
|
const key = `${source}/${flags}`;
|
|
5363
5587
|
let rName = ctx.regexMap.get(key);
|
|
@@ -5457,7 +5681,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
5457
5681
|
const endAfterV = v(ctx, "_sea");
|
|
5458
5682
|
stmts.push(
|
|
5459
5683
|
`${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
|
|
5460
|
-
`${ind(ctx)}if (${endAfterV} > ${scanEndV}) { ${curV} = ${endAfterV} } else { if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; }`
|
|
5684
|
+
`${ind(ctx)}if (${endAfterV} > ${scanEndV}) { ${curV} = ${endAfterV} } else { if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; }`
|
|
5461
5685
|
);
|
|
5462
5686
|
valueVars.push(r2.valueVar);
|
|
5463
5687
|
continue;
|
|
@@ -5503,6 +5727,7 @@ function failsAtStart(p) {
|
|
|
5503
5727
|
case "keywords":
|
|
5504
5728
|
case "guard":
|
|
5505
5729
|
case "not":
|
|
5730
|
+
case "peek":
|
|
5506
5731
|
return true;
|
|
5507
5732
|
case "transform":
|
|
5508
5733
|
case "label":
|
|
@@ -5916,14 +6141,17 @@ function emitMany(def, ctx, pos) {
|
|
|
5916
6141
|
];
|
|
5917
6142
|
const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
|
|
5918
6143
|
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
|
|
5919
|
-
|
|
6144
|
+
for (let i = 0; i < def.min; i++) {
|
|
5920
6145
|
const firstR = emit(def.parser, ctx, curV);
|
|
5921
6146
|
stmts.push(...firstR.stmts);
|
|
5922
6147
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
5923
6148
|
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
5924
6149
|
}
|
|
6150
|
+
const maxV = def.max === void 0 ? null : wantValue ? `${arrV}.length` : v(ctx, "_cnt");
|
|
6151
|
+
if (maxV !== null && !wantValue) stmts.push(`${ind(ctx)}let ${maxV} = ${def.min}`);
|
|
5925
6152
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
5926
6153
|
ctx.indent++;
|
|
6154
|
+
if (maxV !== null) stmts.push(`${ind(ctx)}if (${maxV} >= ${def.max}) break`);
|
|
5927
6155
|
let itemPos = curV;
|
|
5928
6156
|
let rollback = "";
|
|
5929
6157
|
if (ctx.activeTrivia) {
|
|
@@ -5940,7 +6168,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5940
6168
|
`${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
|
|
5941
6169
|
);
|
|
5942
6170
|
itemPos = npV;
|
|
5943
|
-
rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
|
|
6171
|
+
rollback = `if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
|
|
5944
6172
|
} else {
|
|
5945
6173
|
const trivFn = ensureTriviaFn(ctx);
|
|
5946
6174
|
const npV = v(ctx, "_np");
|
|
@@ -5964,7 +6192,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5964
6192
|
`${ind(ctx)}if (!${iterOk}) {`,
|
|
5965
6193
|
`${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
|
|
5966
6194
|
`${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
|
|
5967
|
-
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
|
|
6195
|
+
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : maxV !== null ? [`${ind(ctx)} ${maxV}++`] : [],
|
|
5968
6196
|
`${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
|
|
5969
6197
|
`${ind(ctx)} ${curV} = ${rrV}.end`,
|
|
5970
6198
|
`${ind(ctx)} continue`,
|
|
@@ -5977,6 +6205,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5977
6205
|
stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
5978
6206
|
}
|
|
5979
6207
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
6208
|
+
else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
|
|
5980
6209
|
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
5981
6210
|
ctx.indent--;
|
|
5982
6211
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -6002,7 +6231,7 @@ function emitAttempt(p, def, ctx, pos) {
|
|
|
6002
6231
|
const log = v(ctx, "_atg");
|
|
6003
6232
|
const fields = v(ctx, "_atf");
|
|
6004
6233
|
const errors = v(ctx, "_ate");
|
|
6005
|
-
const rollback = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${log}; if (_ctx._fields) _ctx._fields.length = ${fields}; if (_ctx._errors) _ctx._errors.length = ${errors}`;
|
|
6234
|
+
const rollback = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${leaves}) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${raw}) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${trivia2}) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${log}) _ctx._triviaLog.length = ${log}; if (_ctx._fields && _ctx._fields.length !== ${fields}) _ctx._fields.length = ${fields}; if (_ctx._errors && _ctx._errors.length !== ${errors}) _ctx._errors.length = ${errors}`;
|
|
6006
6235
|
const traceId = ctx.coverage?.plan.attempts.get(p);
|
|
6007
6236
|
const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
|
|
6008
6237
|
const preGuard = [];
|
|
@@ -6041,8 +6270,8 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6041
6270
|
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
6042
6271
|
...firstStmts
|
|
6043
6272
|
];
|
|
6044
|
-
const failItem = (nextOkVar, itemPosVar,
|
|
6045
|
-
if (!rec) return [
|
|
6273
|
+
const failItem = (nextOkVar, itemPosVar, breakStmt) => {
|
|
6274
|
+
if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) ${breakStmt === "break" ? "break" : `{ ${breakStmt} }`}`];
|
|
6046
6275
|
const rr = v(ctx, "_rr");
|
|
6047
6276
|
return [
|
|
6048
6277
|
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
@@ -6050,10 +6279,24 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6050
6279
|
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
6051
6280
|
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
6052
6281
|
`${ind(ctx)} }`,
|
|
6053
|
-
`${ind(ctx)} ${
|
|
6282
|
+
`${ind(ctx)} ${breakStmt}`,
|
|
6054
6283
|
`${ind(ctx)}}`
|
|
6055
6284
|
];
|
|
6056
6285
|
};
|
|
6286
|
+
const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
|
|
6287
|
+
const postSepMarks = () => {
|
|
6288
|
+
if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
|
|
6289
|
+
const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg");
|
|
6290
|
+
return {
|
|
6291
|
+
decl: [
|
|
6292
|
+
`${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
|
|
6293
|
+
`${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
|
|
6294
|
+
`${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
|
|
6295
|
+
`${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`
|
|
6296
|
+
],
|
|
6297
|
+
rb: `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${lv}) _ctx._cstLeaves.length = ${lv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${rw}) _ctx._cstRawChildren.length = ${rw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${tl}) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${lg}) _ctx._triviaLog.length = ${lg}; `
|
|
6298
|
+
};
|
|
6299
|
+
};
|
|
6057
6300
|
if (rec) {
|
|
6058
6301
|
const rr0 = v(ctx, "_rr0");
|
|
6059
6302
|
const didV = v(ctx, "_did");
|
|
@@ -6079,6 +6322,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6079
6322
|
);
|
|
6080
6323
|
ctx.indent++;
|
|
6081
6324
|
}
|
|
6325
|
+
if (def.max !== void 0) stmts.push(`${ind(ctx)}if (${arrV}.length >= ${def.max}) break`);
|
|
6082
6326
|
let sepAtPos = curV;
|
|
6083
6327
|
if (ctx.activeTrivia) {
|
|
6084
6328
|
if (ctx.capturing) {
|
|
@@ -6096,15 +6340,17 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6096
6340
|
`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
|
|
6097
6341
|
);
|
|
6098
6342
|
sepAtPos = spV;
|
|
6099
|
-
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}; `;
|
|
6343
|
+
const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
|
|
6100
6344
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
6101
6345
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
6346
|
+
const post = postSepMarks();
|
|
6347
|
+
stmts.push(...post.decl);
|
|
6102
6348
|
const npV = v(ctx, "_np");
|
|
6103
6349
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
6104
6350
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6105
6351
|
stmts.push(
|
|
6106
6352
|
...nextStmts,
|
|
6107
|
-
...failItem(nextOk, npV, rollbackToSep,
|
|
6353
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
6108
6354
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6109
6355
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6110
6356
|
);
|
|
@@ -6120,7 +6366,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6120
6366
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6121
6367
|
stmts.push(
|
|
6122
6368
|
...nextStmts,
|
|
6123
|
-
...failItem(nextOk, npV, "",
|
|
6369
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
|
|
6124
6370
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6125
6371
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6126
6372
|
);
|
|
@@ -6136,11 +6382,13 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6136
6382
|
}
|
|
6137
6383
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
6138
6384
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
6139
|
-
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
6385
|
+
const nextRb = markLv ? `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markRw}) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
6386
|
+
const post = postSepMarks();
|
|
6387
|
+
stmts.push(...post.decl);
|
|
6140
6388
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
6141
6389
|
stmts.push(
|
|
6142
6390
|
...nextStmts,
|
|
6143
|
-
...failItem(nextOk, sepEnd, nextRb,
|
|
6391
|
+
...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
6144
6392
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6145
6393
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6146
6394
|
);
|
|
@@ -6149,6 +6397,9 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6149
6397
|
stmts.push(`${ind(ctx)}}`);
|
|
6150
6398
|
ctx.indent--;
|
|
6151
6399
|
stmts.push(`${ind(ctx)}}`);
|
|
6400
|
+
if (def.min >= 1) {
|
|
6401
|
+
stmts.push(...emitIfFail(ctx, `${arrV}.length < ${def.min}`, failArrBody(ctx, deriveExpectedArr([def.parser]), curV)));
|
|
6402
|
+
}
|
|
6152
6403
|
return { stmts, valueVar: arrV, endVar: curV };
|
|
6153
6404
|
}
|
|
6154
6405
|
function emitScanTo(def, ctx, pos) {
|
|
@@ -6200,17 +6451,44 @@ function emitScanTo(def, ctx, pos) {
|
|
|
6200
6451
|
return { stmts, valueVar: valV, endVar: curV };
|
|
6201
6452
|
}
|
|
6202
6453
|
function emitNot(def, ctx, pos) {
|
|
6454
|
+
const sinksLive = !!ctx.capturing || !!ctx.recovery;
|
|
6455
|
+
const leaves = sinksLive ? v(ctx, "_ntl") : null;
|
|
6456
|
+
const raw = sinksLive ? v(ctx, "_ntr") : null;
|
|
6457
|
+
const tl = sinksLive ? v(ctx, "_ntt") : null;
|
|
6458
|
+
const log = sinksLive ? v(ctx, "_ntg") : null;
|
|
6459
|
+
const fields = sinksLive ? v(ctx, "_ntf") : null;
|
|
6460
|
+
const errors = sinksLive ? v(ctx, "_nte") : null;
|
|
6203
6461
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
6204
6462
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
6205
6463
|
return {
|
|
6206
6464
|
stmts: [
|
|
6465
|
+
...sinksLive ? [
|
|
6466
|
+
`${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`
|
|
6467
|
+
] : [],
|
|
6207
6468
|
...stmts,
|
|
6469
|
+
...sinksLive ? [
|
|
6470
|
+
`${ind(ctx)}if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${leaves}) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${raw}) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${tl}) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${log}) _ctx._triviaLog.length = ${log}; if (_ctx._fields && _ctx._fields.length !== ${fields}) _ctx._fields.length = ${fields}; if (_ctx._errors && _ctx._errors.length !== ${errors}) _ctx._errors.length = ${errors}`
|
|
6471
|
+
] : [],
|
|
6208
6472
|
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
6209
6473
|
],
|
|
6210
6474
|
valueVar: "null",
|
|
6211
6475
|
endVar: pos
|
|
6212
6476
|
};
|
|
6213
6477
|
}
|
|
6478
|
+
function emitPeek(def, ctx, pos) {
|
|
6479
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
6480
|
+
const { stmts, okVar } = emitFallible(def.parser, probeCtx, pos, true);
|
|
6481
|
+
ctx.vars = probeCtx.vars;
|
|
6482
|
+
const label2 = JSON.stringify(`peek(${def.parser._tag})`);
|
|
6483
|
+
return {
|
|
6484
|
+
stmts: [
|
|
6485
|
+
...stmts,
|
|
6486
|
+
...emitIfFail(ctx, `!${okVar}`, failBody(ctx, label2, pos))
|
|
6487
|
+
],
|
|
6488
|
+
valueVar: "null",
|
|
6489
|
+
endVar: pos
|
|
6490
|
+
};
|
|
6491
|
+
}
|
|
6214
6492
|
function escapeTokenLiteral(value) {
|
|
6215
6493
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6216
6494
|
}
|
|
@@ -6498,9 +6776,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
6498
6776
|
return { stmts, valueVar: vv, endVar: ev };
|
|
6499
6777
|
}
|
|
6500
6778
|
function emitLazy(p, def, ctx, pos) {
|
|
6501
|
-
const
|
|
6779
|
+
const canonical2 = ctx.ruleNames?.get(p);
|
|
6502
6780
|
const usage = ctx.lazyUsage;
|
|
6503
|
-
if (!
|
|
6781
|
+
if (!canonical2 && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
6504
6782
|
let resolved;
|
|
6505
6783
|
try {
|
|
6506
6784
|
resolved = def.thunk();
|
|
@@ -6510,7 +6788,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
6510
6788
|
return emit(resolved, ctx, pos);
|
|
6511
6789
|
}
|
|
6512
6790
|
if (!ctx.namedParsers.has(p)) {
|
|
6513
|
-
const fnName2 =
|
|
6791
|
+
const fnName2 = canonical2 ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
6514
6792
|
ctx.namedParsers.set(p, fnName2);
|
|
6515
6793
|
let resolved;
|
|
6516
6794
|
try {
|
|
@@ -6793,6 +7071,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
6793
7071
|
}
|
|
6794
7072
|
case "not":
|
|
6795
7073
|
return emitNot(def, ctx, pos);
|
|
7074
|
+
case "peek":
|
|
7075
|
+
return emitPeek(def, ctx, pos);
|
|
6796
7076
|
case "node":
|
|
6797
7077
|
return emitNode(def, ctx, pos);
|
|
6798
7078
|
case "scanTo":
|
|
@@ -6881,6 +7161,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6881
7161
|
case "many":
|
|
6882
7162
|
case "oneOrMore":
|
|
6883
7163
|
case "not":
|
|
7164
|
+
case "peek":
|
|
6884
7165
|
case "transform":
|
|
6885
7166
|
return hasNodeDef(d.parser, seen);
|
|
6886
7167
|
case "skip":
|
|
@@ -6932,17 +7213,18 @@ function childrenOf(def) {
|
|
|
6932
7213
|
case "field":
|
|
6933
7214
|
case "grammar":
|
|
6934
7215
|
case "not":
|
|
7216
|
+
case "peek":
|
|
6935
7217
|
case "node":
|
|
6936
7218
|
case "withCtx":
|
|
6937
7219
|
case "expect":
|
|
6938
7220
|
return [def.parser];
|
|
6939
|
-
// emitMany's
|
|
6940
|
-
// (
|
|
6941
|
-
// emit() call
|
|
7221
|
+
// emitMany's min>=1 branch and emitSepBy both codegen `def.parser` more than
|
|
7222
|
+
// once (`min` mandatory matches, then again inside the repeat loop) — each is a
|
|
7223
|
+
// real emit() call site, so the usage analysis must see one edge per site or it
|
|
6942
7224
|
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
6943
|
-
// referenced from
|
|
7225
|
+
// referenced from several positions within this one compiled function.
|
|
6944
7226
|
case "oneOrMore":
|
|
6945
|
-
return
|
|
7227
|
+
return Array.from({ length: def.min + 1 }, () => def.parser);
|
|
6946
7228
|
case "sepBy":
|
|
6947
7229
|
return [def.parser, def.parser, def.separator];
|
|
6948
7230
|
case "skip":
|
|
@@ -7088,12 +7370,41 @@ function resolveGatingLevel(opt) {
|
|
|
7088
7370
|
return "warn";
|
|
7089
7371
|
}
|
|
7090
7372
|
function runGatingDiagnostic(combinator, opt) {
|
|
7373
|
+
return reportGating(opt, (o) => analyzeGating(combinator, o));
|
|
7374
|
+
}
|
|
7375
|
+
function runGatingDiagnosticRules(ruleMap, opt) {
|
|
7376
|
+
return reportGating(opt, (o) => analyzeGatingRules(ruleMap, o));
|
|
7377
|
+
}
|
|
7378
|
+
function runFusedGatingDiagnostic(getRuleMaps, opt) {
|
|
7379
|
+
if (resolveGatingLevel(opt) === "off") return void 0;
|
|
7380
|
+
const ruleMaps = getRuleMaps();
|
|
7381
|
+
if (!ruleMaps.some((map) => hasExternalRuleRef(map))) return void 0;
|
|
7382
|
+
const winners = /* @__PURE__ */ new Map();
|
|
7383
|
+
for (const map of ruleMaps) for (const [name, rule] of map) {
|
|
7384
|
+
if (rule._def.tag === "lazy") {
|
|
7385
|
+
try {
|
|
7386
|
+
rule._def.thunk();
|
|
7387
|
+
} catch {
|
|
7388
|
+
continue;
|
|
7389
|
+
}
|
|
7390
|
+
}
|
|
7391
|
+
winners.set(name, rule);
|
|
7392
|
+
}
|
|
7393
|
+
const entries = [...winners];
|
|
7394
|
+
return reportGating(opt, (o) => {
|
|
7395
|
+
const deferredHere = new Set(analyzeGatingRules(entries, o).deferred.map((c) => c.id));
|
|
7396
|
+
const fused = analyzeGatingRules(entries, { ...o, resolveRef: (name) => winners.get(name) });
|
|
7397
|
+
return { ...fused, ungated: fused.ungated.filter((c) => deferredHere.has(c.id)), antiPatterns: [] };
|
|
7398
|
+
});
|
|
7399
|
+
}
|
|
7400
|
+
function reportGating(opt, analyze) {
|
|
7091
7401
|
const level = resolveGatingLevel(opt);
|
|
7092
7402
|
if (level === "off") return void 0;
|
|
7093
|
-
const
|
|
7403
|
+
const obj = opt !== null && typeof opt === "object" ? opt : void 0;
|
|
7404
|
+
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;
|
|
7094
7405
|
let report;
|
|
7095
7406
|
try {
|
|
7096
|
-
report =
|
|
7407
|
+
report = analyze(analyzeOpts);
|
|
7097
7408
|
} catch {
|
|
7098
7409
|
return void 0;
|
|
7099
7410
|
}
|
|
@@ -7230,6 +7541,7 @@ function publicRuleWrapperSource(rule, fnSource) {
|
|
|
7230
7541
|
}
|
|
7231
7542
|
function compileLinkable(ruleMapArg, ns, opts) {
|
|
7232
7543
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
7544
|
+
if (opts?.gating !== void 0) runGatingDiagnosticRules(ruleMapArg, opts.gating);
|
|
7233
7545
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
7234
7546
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
7235
7547
|
const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
@@ -7286,6 +7598,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7286
7598
|
fnNameToKey.set(fn, key);
|
|
7287
7599
|
}
|
|
7288
7600
|
ctx.ruleNames = ruleNames;
|
|
7601
|
+
const externalRefs = /* @__PURE__ */ new Set();
|
|
7289
7602
|
{
|
|
7290
7603
|
const scanned = /* @__PURE__ */ new Set();
|
|
7291
7604
|
const scanExternal = (p) => {
|
|
@@ -7301,11 +7614,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7301
7614
|
}
|
|
7302
7615
|
if (resolved === void 0) {
|
|
7303
7616
|
const name = p._ruleName;
|
|
7304
|
-
if (name
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7617
|
+
if (name) {
|
|
7618
|
+
externalRefs.add(p);
|
|
7619
|
+
if (!ctx.namedParsers.has(p)) {
|
|
7620
|
+
assertRuleName(name);
|
|
7621
|
+
const fn = `_r_${name}`;
|
|
7622
|
+
ruleNames.set(p, fn);
|
|
7623
|
+
ctx.namedParsers.set(p, fn);
|
|
7624
|
+
}
|
|
7309
7625
|
}
|
|
7310
7626
|
return;
|
|
7311
7627
|
}
|
|
@@ -7418,12 +7734,32 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7418
7734
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
7419
7735
|
needsHostReads: !!ctx.needsHostReads,
|
|
7420
7736
|
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
7421
|
-
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
|
|
7737
|
+
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
|
|
7422
7738
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
7423
7739
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
7424
7740
|
};
|
|
7425
7741
|
}
|
|
7426
|
-
function
|
|
7742
|
+
function hasExternalRuleRef(ruleMap) {
|
|
7743
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7744
|
+
const visit = (p) => {
|
|
7745
|
+
if (seen.has(p)) return false;
|
|
7746
|
+
seen.add(p);
|
|
7747
|
+
const def = p._def;
|
|
7748
|
+
if (def.tag === "lazy") {
|
|
7749
|
+
let resolved;
|
|
7750
|
+
try {
|
|
7751
|
+
resolved = def.thunk();
|
|
7752
|
+
} catch {
|
|
7753
|
+
resolved = void 0;
|
|
7754
|
+
}
|
|
7755
|
+
if (resolved === void 0) return p._ruleName !== void 0;
|
|
7756
|
+
return visit(resolved);
|
|
7757
|
+
}
|
|
7758
|
+
return childrenOf(def).some(visit);
|
|
7759
|
+
};
|
|
7760
|
+
return ruleMap.some(([, rule]) => visit(rule));
|
|
7761
|
+
}
|
|
7762
|
+
function hasSemanticReduction(roots, externalRefs) {
|
|
7427
7763
|
const seen = /* @__PURE__ */ new Set();
|
|
7428
7764
|
const visit = (parser2) => {
|
|
7429
7765
|
if (seen.has(parser2)) return false;
|
|
@@ -7434,6 +7770,7 @@ function hasSemanticReduction(roots) {
|
|
|
7434
7770
|
if (def.tag === "guard" || def.tag === "withCtx") return true;
|
|
7435
7771
|
if (def.tag === "node" && def.build !== void 0) return true;
|
|
7436
7772
|
if (def.tag === "lazy") {
|
|
7773
|
+
if (externalRefs?.has(parser2)) return false;
|
|
7437
7774
|
try {
|
|
7438
7775
|
return visit(def.thunk());
|
|
7439
7776
|
} catch {
|
|
@@ -7525,6 +7862,7 @@ function childrenOf2(def) {
|
|
|
7525
7862
|
case "label":
|
|
7526
7863
|
case "field":
|
|
7527
7864
|
case "not":
|
|
7865
|
+
case "peek":
|
|
7528
7866
|
case "node":
|
|
7529
7867
|
case "withCtx":
|
|
7530
7868
|
case "expect":
|
|
@@ -7558,6 +7896,13 @@ function lazyTarget(c) {
|
|
|
7558
7896
|
}
|
|
7559
7897
|
var Unserializable = class extends Error {
|
|
7560
7898
|
};
|
|
7899
|
+
function repeatOpts(min, max, trailing) {
|
|
7900
|
+
const parts = [];
|
|
7901
|
+
if (min !== 0) parts.push(`min: ${min}`);
|
|
7902
|
+
if (max !== void 0) parts.push(`max: ${max}`);
|
|
7903
|
+
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
7904
|
+
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
7905
|
+
}
|
|
7561
7906
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
7562
7907
|
try {
|
|
7563
7908
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -7661,6 +8006,7 @@ function evalRuleMapIR(ir) {
|
|
|
7661
8006
|
"optional",
|
|
7662
8007
|
"sepBy",
|
|
7663
8008
|
"not",
|
|
8009
|
+
"peek",
|
|
7664
8010
|
"node",
|
|
7665
8011
|
"parser",
|
|
7666
8012
|
"scanTo",
|
|
@@ -7693,6 +8039,7 @@ function evalRuleMapIR(ir) {
|
|
|
7693
8039
|
optional,
|
|
7694
8040
|
sepBy,
|
|
7695
8041
|
not,
|
|
8042
|
+
peek,
|
|
7696
8043
|
node,
|
|
7697
8044
|
parser,
|
|
7698
8045
|
scanTo,
|
|
@@ -7867,17 +8214,21 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
7867
8214
|
return `_gch([${items.join(", ")}])`;
|
|
7868
8215
|
}
|
|
7869
8216
|
case "many":
|
|
7870
|
-
return `many(${kid(def.parser)})`;
|
|
8217
|
+
return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
8218
|
+
// A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
|
|
8219
|
+
// it through `many` so the bounds survive (oneOrMore defaults min to 1).
|
|
7871
8220
|
case "oneOrMore":
|
|
7872
|
-
return `oneOrMore(${kid(def.parser)})`;
|
|
8221
|
+
return def.min === 1 && def.max === void 0 ? `oneOrMore(${kid(def.parser)})` : `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
7873
8222
|
case "optional":
|
|
7874
8223
|
return `optional(${kid(def.parser)})`;
|
|
7875
8224
|
case "attempt":
|
|
7876
8225
|
return `attempt(${kid(def.parser)})`;
|
|
7877
8226
|
case "sepBy":
|
|
7878
|
-
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
8227
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)}${repeatOpts(def.min, def.max, def.trailing)})`;
|
|
7879
8228
|
case "not":
|
|
7880
8229
|
return `not(${kid(def.parser)})`;
|
|
8230
|
+
case "peek":
|
|
8231
|
+
return `peek(${kid(def.parser)})`;
|
|
7881
8232
|
case "trivia":
|
|
7882
8233
|
return `trivia(${kid(def.parser)})`;
|
|
7883
8234
|
case "token":
|
|
@@ -8070,6 +8421,9 @@ var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCover
|
|
|
8070
8421
|
function isIRPiece(p) {
|
|
8071
8422
|
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
8072
8423
|
}
|
|
8424
|
+
function carriedRuleMaps(carried) {
|
|
8425
|
+
return carried.filter(isIRPiece).map((p) => evalRuleMapIR(p.ir));
|
|
8426
|
+
}
|
|
8073
8427
|
function coverageRulesOf(carried) {
|
|
8074
8428
|
const winners = {};
|
|
8075
8429
|
for (const piece of carried) {
|
|
@@ -8154,6 +8508,7 @@ function compose(items) {
|
|
|
8154
8508
|
const used = /* @__PURE__ */ new Set();
|
|
8155
8509
|
const trivia2 = composingTriviaOf(items);
|
|
8156
8510
|
const carried = items.flatMap((item) => itemCarried(item, used, trivia2));
|
|
8511
|
+
runFusedGatingDiagnostic(() => carriedRuleMaps(carried));
|
|
8157
8512
|
const pieces = carried.map((p) => materializePiece(p, trivia2));
|
|
8158
8513
|
const map = fuseRules(pieces);
|
|
8159
8514
|
Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
|
|
@@ -8986,7 +9341,7 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
8986
9341
|
};
|
|
8987
9342
|
}
|
|
8988
9343
|
case "sepBy":
|
|
8989
|
-
return sepBy(build(def.parser), build(def.separator));
|
|
9344
|
+
return sepBy(build(def.parser), build(def.separator), { min: def.min, ...def.max === void 0 ? {} : { max: def.max }, ...def.trailing === void 0 ? {} : { trailing: def.trailing } });
|
|
8990
9345
|
case "transform":
|
|
8991
9346
|
return transform(build(def.parser), def.fn);
|
|
8992
9347
|
case "skip":
|
|
@@ -9007,6 +9362,8 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
9007
9362
|
}, build(def.parser));
|
|
9008
9363
|
case "not":
|
|
9009
9364
|
return not(build(def.parser));
|
|
9365
|
+
case "peek":
|
|
9366
|
+
return peek(build(def.parser));
|
|
9010
9367
|
case "node": {
|
|
9011
9368
|
const opts = { ...def.unwrap ? { unwrap: true } : {}, ...def.collapse ? { collapse: true } : {}, ...def.captureTrivia ? { captureTrivia: true } : {}, ...def.trailingTrivia ? { trailingTrivia: true } : {} };
|
|
9012
9369
|
return def.type === void 0 ? node(build(def.parser), def.build, opts) : node(def.type, build(def.parser), def.build, opts);
|