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.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// src/combinators/first-set.ts
|
|
2
|
+
function resolveNamedRef(p, resolve) {
|
|
3
|
+
if (resolve === void 0) return void 0;
|
|
4
|
+
const name = p._ruleName;
|
|
5
|
+
return name === void 0 ? void 0 : resolve(name);
|
|
6
|
+
}
|
|
2
7
|
function union(a, b) {
|
|
3
8
|
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
4
9
|
if (a.kind === "empty") return b;
|
|
@@ -36,10 +41,10 @@ function any() {
|
|
|
36
41
|
function empty() {
|
|
37
42
|
return { kind: "empty" };
|
|
38
43
|
}
|
|
39
|
-
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
44
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
40
45
|
if (seen.has(p)) return true;
|
|
41
46
|
seen.add(p);
|
|
42
|
-
const me = (c) => matchesEmpty(c, seen);
|
|
47
|
+
const me = (c) => matchesEmpty(c, seen, resolve);
|
|
43
48
|
const d = p._def;
|
|
44
49
|
switch (d.tag) {
|
|
45
50
|
case "literal":
|
|
@@ -56,8 +61,16 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
56
61
|
case "many":
|
|
57
62
|
case "optional":
|
|
58
63
|
case "not":
|
|
64
|
+
case "peek":
|
|
59
65
|
return true;
|
|
60
66
|
// zero repetitions / absent / lookahead
|
|
67
|
+
// Default `sepBy` is `(item (sep item)*)?` — it MATCHES THE EMPTY STRING.
|
|
68
|
+
// Any `min >= 1` requires that many ITEMS, so it is nullable only when the
|
|
69
|
+
// item is. (Keying this off `min === 1` reported every `{ min: 2 }` list as
|
|
70
|
+
// nullable — safe, but wrong, and it put a bogus `nullable-prefix` note on
|
|
71
|
+
// the gating diagnostic for a list that can never match empty.)
|
|
72
|
+
case "sepBy":
|
|
73
|
+
return d.min >= 1 ? me(d.parser) : true;
|
|
61
74
|
case "oneOrMore":
|
|
62
75
|
return me(d.parser);
|
|
63
76
|
case "sequence":
|
|
@@ -81,7 +94,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
81
94
|
try {
|
|
82
95
|
return me(d.thunk());
|
|
83
96
|
} catch {
|
|
84
|
-
|
|
97
|
+
const t = resolveNamedRef(p, resolve);
|
|
98
|
+
return t ? me(t) : true;
|
|
85
99
|
}
|
|
86
100
|
default:
|
|
87
101
|
return true;
|
|
@@ -90,18 +104,45 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
90
104
|
function isZeroWidthAssertion(p) {
|
|
91
105
|
return p._def.tag === "not";
|
|
92
106
|
}
|
|
107
|
+
function isPositiveLookahead(p) {
|
|
108
|
+
return p._def.tag === "peek";
|
|
109
|
+
}
|
|
110
|
+
function narrowBy(acc, constraint) {
|
|
111
|
+
if (constraint.kind === "any") return acc;
|
|
112
|
+
if (acc === null) return constraint;
|
|
113
|
+
if (acc.kind === "any") return constraint;
|
|
114
|
+
if (acc.kind === "empty" || constraint.kind === "empty") return { kind: "empty" };
|
|
115
|
+
const ranges = [];
|
|
116
|
+
for (const a of acc.ranges) for (const b of constraint.ranges) {
|
|
117
|
+
const lo = Math.max(a.lo, b.lo);
|
|
118
|
+
const hi = Math.min(a.hi, b.hi);
|
|
119
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
120
|
+
}
|
|
121
|
+
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
122
|
+
}
|
|
123
|
+
function applyAssertion(fs, assertion) {
|
|
124
|
+
if (assertion === null) return fs;
|
|
125
|
+
if (fs.kind === "empty") return assertion;
|
|
126
|
+
return narrowBy(fs, assertion) ?? fs;
|
|
127
|
+
}
|
|
93
128
|
function sequenceFirstSet(parsers) {
|
|
94
129
|
let fs = empty();
|
|
130
|
+
let assertion = null;
|
|
95
131
|
for (const p of parsers) {
|
|
132
|
+
if (isPositiveLookahead(p)) {
|
|
133
|
+
assertion = narrowBy(assertion, p._meta.firstSet);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
96
136
|
if (!isZeroWidthAssertion(p)) fs = union(fs, p._meta.firstSet);
|
|
97
|
-
if (!matchesEmpty(p)) return fs;
|
|
137
|
+
if (!matchesEmpty(p)) return applyAssertion(fs, assertion);
|
|
98
138
|
}
|
|
99
|
-
return fs;
|
|
139
|
+
return applyAssertion(fs, assertion);
|
|
100
140
|
}
|
|
101
|
-
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
141
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
102
142
|
if (seen.has(p)) return any();
|
|
103
143
|
seen.add(p);
|
|
104
|
-
const fs = (c) => firstSetOf(c, seen);
|
|
144
|
+
const fs = (c) => firstSetOf(c, seen, resolve);
|
|
145
|
+
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve);
|
|
105
146
|
const d = p._def;
|
|
106
147
|
switch (d.tag) {
|
|
107
148
|
case "literal":
|
|
@@ -113,7 +154,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
113
154
|
try {
|
|
114
155
|
return fs(d.thunk());
|
|
115
156
|
} catch {
|
|
116
|
-
|
|
157
|
+
const t = resolveNamedRef(p, resolve);
|
|
158
|
+
return t ? fs(t) : any();
|
|
117
159
|
}
|
|
118
160
|
case "choice": {
|
|
119
161
|
let out = empty();
|
|
@@ -122,11 +164,20 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
122
164
|
}
|
|
123
165
|
case "sequence": {
|
|
124
166
|
let out = empty();
|
|
167
|
+
let assertion = null;
|
|
125
168
|
for (const term of d.parsers) {
|
|
169
|
+
if (isPositiveLookahead(term)) {
|
|
170
|
+
assertion = narrowBy(assertion, fs(term));
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
126
173
|
if (!isZeroWidthAssertion(term)) out = union(out, fs(term));
|
|
127
|
-
if (!
|
|
174
|
+
if (!empties(term)) return applyAssertion(out, assertion);
|
|
128
175
|
}
|
|
129
|
-
return out;
|
|
176
|
+
return applyAssertion(out, assertion);
|
|
177
|
+
}
|
|
178
|
+
case "peek": {
|
|
179
|
+
const inner = d.parser;
|
|
180
|
+
return empties(inner) ? any() : fs(inner);
|
|
130
181
|
}
|
|
131
182
|
case "oneOrMore":
|
|
132
183
|
case "many":
|
|
@@ -142,6 +193,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
142
193
|
return fs(d.parser);
|
|
143
194
|
case "sepBy":
|
|
144
195
|
return fs(d.parser);
|
|
196
|
+
// both min 0 and min 1 start with the item
|
|
145
197
|
case "skip":
|
|
146
198
|
return fs(d.main);
|
|
147
199
|
default:
|
|
@@ -283,28 +335,28 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
|
|
|
283
335
|
else if (len === 1) {
|
|
284
336
|
b[keySingle] = arr[0];
|
|
285
337
|
b[keyMulti] = void 0;
|
|
286
|
-
} else arr.length = len;
|
|
338
|
+
} else if (arr.length !== len) arr.length = len;
|
|
287
339
|
return;
|
|
288
340
|
}
|
|
289
341
|
if (len === 0) b[keySingle] = void 0;
|
|
290
342
|
}
|
|
291
343
|
function rollbackCstCapture(ctx, mark) {
|
|
292
|
-
if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
|
|
344
|
+
if (ctx._errors && mark.errors !== void 0 && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors;
|
|
293
345
|
const b = ctx._cstBuf;
|
|
294
346
|
if (b) {
|
|
295
347
|
rollbackBufList(b, "raw", "rawSingle", mark.raw);
|
|
296
348
|
rollbackBufList(b, "ch", "single", mark.leaves);
|
|
297
349
|
if (b.tl) {
|
|
298
350
|
if (mark.tlog === 0) b.tl = void 0;
|
|
299
|
-
else b.tl.length = mark.tlog;
|
|
351
|
+
else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog;
|
|
300
352
|
}
|
|
301
|
-
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
353
|
+
if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields;
|
|
302
354
|
return;
|
|
303
355
|
}
|
|
304
|
-
if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
|
|
305
|
-
if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
|
|
306
|
-
if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
|
|
307
|
-
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
356
|
+
if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw;
|
|
357
|
+
if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog;
|
|
358
|
+
if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves;
|
|
359
|
+
if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields;
|
|
308
360
|
}
|
|
309
361
|
function pushCstTriviaEntry(ctx, start, end, kindIndex) {
|
|
310
362
|
const insertIdx = cstRawLen(ctx);
|
|
@@ -826,9 +878,46 @@ function firstSetFromRegex(source) {
|
|
|
826
878
|
return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
|
|
827
879
|
}
|
|
828
880
|
|
|
881
|
+
// src/combinators/case-fold.ts
|
|
882
|
+
function canonical(ch2) {
|
|
883
|
+
const u = ch2.toUpperCase();
|
|
884
|
+
if (u.length !== 1) return ch2;
|
|
885
|
+
if (ch2.codePointAt(0) >= 128 && u.codePointAt(0) < 128) return ch2;
|
|
886
|
+
return u;
|
|
887
|
+
}
|
|
888
|
+
var foldIndex = null;
|
|
889
|
+
function buildFoldIndex() {
|
|
890
|
+
const byCanonical = /* @__PURE__ */ new Map();
|
|
891
|
+
for (let cp = 0; cp < 65536; cp++) {
|
|
892
|
+
if (cp >= 55296 && cp <= 57343) continue;
|
|
893
|
+
const key = canonical(String.fromCharCode(cp));
|
|
894
|
+
const bucket = byCanonical.get(key);
|
|
895
|
+
if (bucket) bucket.push(cp);
|
|
896
|
+
else byCanonical.set(key, [cp]);
|
|
897
|
+
}
|
|
898
|
+
const index = /* @__PURE__ */ new Map();
|
|
899
|
+
for (const members of byCanonical.values()) {
|
|
900
|
+
if (members.length < 2) continue;
|
|
901
|
+
for (const cp of members) index.set(cp, members);
|
|
902
|
+
}
|
|
903
|
+
return index;
|
|
904
|
+
}
|
|
905
|
+
function caseFoldVariants(cp) {
|
|
906
|
+
if (cp < 128) {
|
|
907
|
+
if (cp >= 65 && cp <= 90) return [cp, cp + 32];
|
|
908
|
+
if (cp >= 97 && cp <= 122) return [cp, cp - 32];
|
|
909
|
+
return [cp];
|
|
910
|
+
}
|
|
911
|
+
if (cp > 65535) return [cp];
|
|
912
|
+
foldIndex ??= buildFoldIndex();
|
|
913
|
+
return foldIndex.get(cp) ?? [cp];
|
|
914
|
+
}
|
|
915
|
+
|
|
829
916
|
// src/combinators/keywords.ts
|
|
830
|
-
function word(str,
|
|
831
|
-
|
|
917
|
+
function word(str, boundaryOrOpts, opts) {
|
|
918
|
+
const boundary = typeof boundaryOrOpts === "string" ? boundaryOrOpts : "_0-9A-Za-z";
|
|
919
|
+
const rest = typeof boundaryOrOpts === "object" && boundaryOrOpts !== null ? boundaryOrOpts : opts;
|
|
920
|
+
return keywords([str], { ...rest, boundary });
|
|
832
921
|
}
|
|
833
922
|
function makeWord(boundary = "_0-9A-Za-z") {
|
|
834
923
|
return (str) => keywords([str], { boundary });
|
|
@@ -840,7 +929,7 @@ function keywords(words, opts = {}) {
|
|
|
840
929
|
const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
|
|
841
930
|
const alt = sorted.map(escapeRe).join("|");
|
|
842
931
|
const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
|
|
843
|
-
const flags = opts.caseInsensitive ? "
|
|
932
|
+
const flags = opts.caseInsensitive ? "iy" : "uy";
|
|
844
933
|
const re = new RegExp(`(?:${alt})${boundary}`, flags);
|
|
845
934
|
let firstSet2 = empty();
|
|
846
935
|
for (const w of sorted) {
|
|
@@ -848,10 +937,7 @@ function keywords(words, opts = {}) {
|
|
|
848
937
|
const cp = w.codePointAt(0);
|
|
849
938
|
firstSet2 = union(firstSet2, fromChar(cp));
|
|
850
939
|
if (opts.caseInsensitive) {
|
|
851
|
-
const
|
|
852
|
-
const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
|
|
853
|
-
if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
|
|
854
|
-
if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
|
|
940
|
+
for (const v2 of caseFoldVariants(cp)) firstSet2 = union(firstSet2, fromChar(v2));
|
|
855
941
|
}
|
|
856
942
|
}
|
|
857
943
|
const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
|
|
@@ -963,6 +1049,10 @@ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSet
|
|
|
963
1049
|
|
|
964
1050
|
// src/combinators/expect.ts
|
|
965
1051
|
function deriveExpected(c) {
|
|
1052
|
+
return derive(c, /* @__PURE__ */ new Set());
|
|
1053
|
+
}
|
|
1054
|
+
function derive(c, seen) {
|
|
1055
|
+
const deriveExpected2 = (p) => derive(p, seen);
|
|
966
1056
|
const def = c._def;
|
|
967
1057
|
switch (def.tag) {
|
|
968
1058
|
case "literal":
|
|
@@ -974,20 +1064,32 @@ function deriveExpected(c) {
|
|
|
974
1064
|
case "label":
|
|
975
1065
|
return [def.label];
|
|
976
1066
|
case "choice":
|
|
977
|
-
return def.parsers.flatMap(
|
|
978
|
-
|
|
979
|
-
|
|
1067
|
+
return def.parsers.flatMap(deriveExpected2);
|
|
1068
|
+
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
1069
|
+
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
1070
|
+
// able to fail first — deriving from term 0 alone named a token the parse
|
|
1071
|
+
// never actually required. A leading `not(…)` is zero-width and contributes
|
|
1072
|
+
// nothing (its own expectation is about what must NOT be here), but it is
|
|
1073
|
+
// nullable, so keep scanning past it. Stop at the first term that must match.
|
|
1074
|
+
case "sequence": {
|
|
1075
|
+
const out = [];
|
|
1076
|
+
for (const term of def.parsers) {
|
|
1077
|
+
if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
|
|
1078
|
+
if (!matchesEmpty(term)) break;
|
|
1079
|
+
}
|
|
1080
|
+
return out;
|
|
1081
|
+
}
|
|
980
1082
|
case "attempt":
|
|
981
|
-
return
|
|
1083
|
+
return deriveExpected2(def.parser);
|
|
982
1084
|
// Delegating wrappers whose first-token failure is their inner parser's — must
|
|
983
1085
|
// pass through so a start-failure (and the first-set-miss fast-path in attempt/
|
|
984
1086
|
// node, which reads this) names the real expected token, not the wrapper tag.
|
|
985
1087
|
case "field":
|
|
986
1088
|
case "withCtx":
|
|
987
1089
|
case "recover":
|
|
988
|
-
return
|
|
1090
|
+
return deriveExpected2(def.parser);
|
|
989
1091
|
case "skip":
|
|
990
|
-
return
|
|
1092
|
+
return deriveExpected2(def.main);
|
|
991
1093
|
case "expect":
|
|
992
1094
|
return def.expected;
|
|
993
1095
|
case "node":
|
|
@@ -1000,13 +1102,18 @@ function deriveExpected(c) {
|
|
|
1000
1102
|
case "oneOrMore":
|
|
1001
1103
|
case "transform":
|
|
1002
1104
|
case "not":
|
|
1003
|
-
|
|
1105
|
+
case "peek":
|
|
1106
|
+
return deriveExpected2(def.parser);
|
|
1004
1107
|
case "lazy": {
|
|
1005
1108
|
const name = c._ruleName;
|
|
1109
|
+
if (seen.has(c)) return name ? [name] : [];
|
|
1110
|
+
seen.add(c);
|
|
1006
1111
|
try {
|
|
1007
|
-
return
|
|
1112
|
+
return deriveExpected2(def.thunk());
|
|
1008
1113
|
} catch {
|
|
1009
1114
|
return name ? [name] : [];
|
|
1115
|
+
} finally {
|
|
1116
|
+
seen.delete(c);
|
|
1010
1117
|
}
|
|
1011
1118
|
}
|
|
1012
1119
|
default:
|
|
@@ -1137,14 +1244,14 @@ function choice(...args) {
|
|
|
1137
1244
|
const result = parsers[i].parse(input, pos, ctx);
|
|
1138
1245
|
if (!result.ok) {
|
|
1139
1246
|
rollbackCstCapture(ctx, mark);
|
|
1140
|
-
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
1247
|
+
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1141
1248
|
expected.push(...result.expected);
|
|
1142
1249
|
continue;
|
|
1143
1250
|
}
|
|
1144
1251
|
const checks = autoNot[i];
|
|
1145
1252
|
if (checks && autoNotFires(input, result.span.end, checks)) {
|
|
1146
1253
|
rollbackCstCapture(ctx, mark);
|
|
1147
|
-
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
1254
|
+
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1148
1255
|
continue;
|
|
1149
1256
|
}
|
|
1150
1257
|
return result;
|
|
@@ -1471,7 +1578,14 @@ function saveTriviaMark(ctx) {
|
|
|
1471
1578
|
}
|
|
1472
1579
|
function rollbackTrivia(ctx, mark) {
|
|
1473
1580
|
rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
|
|
1474
|
-
if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
|
|
1581
|
+
if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
|
|
1582
|
+
}
|
|
1583
|
+
function saveLookaheadMark(ctx) {
|
|
1584
|
+
return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
|
|
1585
|
+
}
|
|
1586
|
+
function rollbackLookahead(ctx, mark) {
|
|
1587
|
+
rollbackTrivia(ctx, mark);
|
|
1588
|
+
if (ctx._probe) ctx._probe.best = mark.probeBest;
|
|
1475
1589
|
}
|
|
1476
1590
|
function scanWithLabels(input, cur, ctx) {
|
|
1477
1591
|
const triviaP = ctx.trivia;
|
|
@@ -1832,7 +1946,7 @@ function attempt(parser2) {
|
|
|
1832
1946
|
const result = parser2.parse(input, pos, ctx);
|
|
1833
1947
|
if (result.ok) return result;
|
|
1834
1948
|
rollbackCstCapture(ctx, mark);
|
|
1835
|
-
if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
|
|
1949
|
+
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
1836
1950
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
1837
1951
|
}
|
|
1838
1952
|
};
|
|
@@ -1870,13 +1984,26 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1870
1984
|
}
|
|
1871
1985
|
return { value: result.value, end: result.span.end };
|
|
1872
1986
|
}
|
|
1873
|
-
function
|
|
1987
|
+
function resolveBounds(what, opts) {
|
|
1988
|
+
const min = opts.min ?? 0;
|
|
1989
|
+
const max = opts.max ?? Infinity;
|
|
1990
|
+
const bad = (msg) => {
|
|
1991
|
+
throw new RangeError(`parseman: ${what} ${msg}`);
|
|
1992
|
+
};
|
|
1993
|
+
if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
|
|
1994
|
+
if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
|
|
1995
|
+
if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
|
|
1996
|
+
return { min, max };
|
|
1997
|
+
}
|
|
1998
|
+
function many(combinator, opts = {}) {
|
|
1999
|
+
const { min, max } = resolveBounds("many()", opts);
|
|
2000
|
+
if (min >= 1) return atLeast(combinator, min, max);
|
|
1874
2001
|
const meta = {
|
|
1875
2002
|
firstSet: combinator._meta.firstSet,
|
|
1876
2003
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1877
2004
|
isTrivia: false
|
|
1878
2005
|
};
|
|
1879
|
-
const def = { tag: "many", parser: combinator, min: 0 };
|
|
2006
|
+
const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
|
|
1880
2007
|
let expected;
|
|
1881
2008
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1882
2009
|
return {
|
|
@@ -1886,7 +2013,9 @@ function many(combinator) {
|
|
|
1886
2013
|
parse(input, pos, ctx) {
|
|
1887
2014
|
const values = def.valueUnused ? void 0 : [];
|
|
1888
2015
|
let cur = pos;
|
|
2016
|
+
let count = 0;
|
|
1889
2017
|
while (cur < input.length) {
|
|
2018
|
+
if (count >= max) break;
|
|
1890
2019
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1891
2020
|
if (item === "stop") break;
|
|
1892
2021
|
if ("fail" in item) {
|
|
@@ -1897,23 +2026,28 @@ function many(combinator) {
|
|
|
1897
2026
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1898
2027
|
if (values !== void 0) values.push(error);
|
|
1899
2028
|
captureError(ctx, error);
|
|
2029
|
+
count++;
|
|
1900
2030
|
cur = end;
|
|
1901
2031
|
continue;
|
|
1902
2032
|
}
|
|
1903
2033
|
if (values !== void 0) values.push(item.value);
|
|
2034
|
+
count++;
|
|
1904
2035
|
cur = item.end;
|
|
1905
2036
|
}
|
|
1906
2037
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1907
2038
|
}
|
|
1908
2039
|
};
|
|
1909
2040
|
}
|
|
1910
|
-
function oneOrMore(combinator) {
|
|
2041
|
+
function oneOrMore(combinator, opts = {}) {
|
|
2042
|
+
return many(combinator, { ...opts, min: opts.min ?? 1 });
|
|
2043
|
+
}
|
|
2044
|
+
function atLeast(combinator, min, max) {
|
|
1911
2045
|
const meta = {
|
|
1912
2046
|
firstSet: combinator._meta.firstSet,
|
|
1913
2047
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1914
2048
|
isTrivia: false
|
|
1915
2049
|
};
|
|
1916
|
-
const def = { tag: "oneOrMore", parser: combinator, min:
|
|
2050
|
+
const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
|
|
1917
2051
|
let expected;
|
|
1918
2052
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1919
2053
|
return {
|
|
@@ -1925,7 +2059,19 @@ function oneOrMore(combinator) {
|
|
|
1925
2059
|
if (!first.ok) return first;
|
|
1926
2060
|
const values = def.valueUnused ? void 0 : [first.value];
|
|
1927
2061
|
let cur = first.span.end;
|
|
2062
|
+
let count = 1;
|
|
2063
|
+
while (count < min) {
|
|
2064
|
+
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2065
|
+
if (item === "stop" || "fail" in item) {
|
|
2066
|
+
expected ??= deriveExpected(combinator);
|
|
2067
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
2068
|
+
}
|
|
2069
|
+
if (values !== void 0) values.push(item.value);
|
|
2070
|
+
cur = item.end;
|
|
2071
|
+
count++;
|
|
2072
|
+
}
|
|
1928
2073
|
while (cur < input.length) {
|
|
2074
|
+
if (count >= max) break;
|
|
1929
2075
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1930
2076
|
if (item === "stop") break;
|
|
1931
2077
|
if ("fail" in item) {
|
|
@@ -1936,10 +2082,12 @@ function oneOrMore(combinator) {
|
|
|
1936
2082
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1937
2083
|
if (values !== void 0) values.push(error);
|
|
1938
2084
|
captureError(ctx, error);
|
|
2085
|
+
count++;
|
|
1939
2086
|
cur = end;
|
|
1940
2087
|
continue;
|
|
1941
2088
|
}
|
|
1942
2089
|
if (values !== void 0) values.push(item.value);
|
|
2090
|
+
count++;
|
|
1943
2091
|
cur = item.end;
|
|
1944
2092
|
}
|
|
1945
2093
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
@@ -1969,17 +2117,33 @@ function optional(combinator) {
|
|
|
1969
2117
|
}
|
|
1970
2118
|
};
|
|
1971
2119
|
}
|
|
1972
|
-
function
|
|
2120
|
+
function oneOrMoreSep(combinator, separator, opts = {}) {
|
|
2121
|
+
return sepBy(combinator, separator, { ...opts, min: opts.min ?? 1 });
|
|
2122
|
+
}
|
|
2123
|
+
function sepBy(combinator, separator, opts = {}) {
|
|
2124
|
+
const { min, max } = resolveBounds("sepBy()", opts);
|
|
2125
|
+
const trailing = opts.trailing ?? "forbid";
|
|
1973
2126
|
const meta = {
|
|
1974
2127
|
firstSet: combinator._meta.firstSet,
|
|
1975
2128
|
canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
|
|
1976
2129
|
isTrivia: false
|
|
1977
2130
|
};
|
|
1978
2131
|
let expected;
|
|
2132
|
+
const failAt2 = (at) => {
|
|
2133
|
+
expected ??= deriveExpected(combinator);
|
|
2134
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
|
|
2135
|
+
};
|
|
1979
2136
|
return {
|
|
1980
2137
|
_tag: "sepBy",
|
|
1981
2138
|
_meta: meta,
|
|
1982
|
-
_def: {
|
|
2139
|
+
_def: {
|
|
2140
|
+
tag: "sepBy",
|
|
2141
|
+
parser: combinator,
|
|
2142
|
+
separator,
|
|
2143
|
+
min,
|
|
2144
|
+
...max === Infinity ? {} : { max },
|
|
2145
|
+
...trailing === "forbid" ? {} : { trailing }
|
|
2146
|
+
},
|
|
1983
2147
|
parse(input, pos, ctx) {
|
|
1984
2148
|
const first = combinator.parse(input, pos, ctx);
|
|
1985
2149
|
const values = [];
|
|
@@ -1990,6 +2154,7 @@ function sepBy(combinator, separator) {
|
|
|
1990
2154
|
} else {
|
|
1991
2155
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
1992
2156
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2157
|
+
if (min >= 1) return failAt2(pos);
|
|
1993
2158
|
return { ok: true, value: [], span: { start: pos, end: pos } };
|
|
1994
2159
|
}
|
|
1995
2160
|
expected ??= deriveExpected(combinator);
|
|
@@ -1999,6 +2164,7 @@ function sepBy(combinator, separator) {
|
|
|
1999
2164
|
cur = rec.end;
|
|
2000
2165
|
}
|
|
2001
2166
|
while (cur < input.length) {
|
|
2167
|
+
if (values.length >= max) break;
|
|
2002
2168
|
const loopMark = saveTriviaMark(ctx);
|
|
2003
2169
|
let sepPos = cur;
|
|
2004
2170
|
if (ctx.trivia) {
|
|
@@ -2015,6 +2181,7 @@ function sepBy(combinator, separator) {
|
|
|
2015
2181
|
rollbackTrivia(ctx, loopMark);
|
|
2016
2182
|
break;
|
|
2017
2183
|
}
|
|
2184
|
+
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
2018
2185
|
let nextPos = sep.span.end;
|
|
2019
2186
|
if (ctx.trivia) {
|
|
2020
2187
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
@@ -2036,12 +2203,18 @@ function sepBy(combinator, separator) {
|
|
|
2036
2203
|
cur = rec.end;
|
|
2037
2204
|
continue;
|
|
2038
2205
|
}
|
|
2206
|
+
if (sepMark !== void 0) {
|
|
2207
|
+
rollbackTrivia(ctx, sepMark);
|
|
2208
|
+
cur = sep.span.end;
|
|
2209
|
+
break;
|
|
2210
|
+
}
|
|
2039
2211
|
rollbackTrivia(ctx, loopMark);
|
|
2040
2212
|
break;
|
|
2041
2213
|
}
|
|
2042
2214
|
values.push(next.value);
|
|
2043
2215
|
cur = next.span.end;
|
|
2044
2216
|
}
|
|
2217
|
+
if (values.length < min) return failAt2(cur);
|
|
2045
2218
|
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
2046
2219
|
}
|
|
2047
2220
|
};
|
|
@@ -2131,6 +2304,7 @@ function markUnusedValues(root) {
|
|
|
2131
2304
|
visit(def.separator, false);
|
|
2132
2305
|
return;
|
|
2133
2306
|
case "not":
|
|
2307
|
+
case "peek":
|
|
2134
2308
|
visit(def.parser, false);
|
|
2135
2309
|
return;
|
|
2136
2310
|
case "grammar":
|
|
@@ -2249,9 +2423,9 @@ function not(combinator) {
|
|
|
2249
2423
|
_meta: meta,
|
|
2250
2424
|
_def: { tag: "not", parser: combinator },
|
|
2251
2425
|
parse(input, pos, ctx) {
|
|
2252
|
-
const mark =
|
|
2426
|
+
const mark = saveLookaheadMark(ctx);
|
|
2253
2427
|
const result = combinator.parse(input, pos, ctx);
|
|
2254
|
-
|
|
2428
|
+
rollbackLookahead(ctx, mark);
|
|
2255
2429
|
if (result.ok) {
|
|
2256
2430
|
return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2257
2431
|
}
|
|
@@ -2260,6 +2434,28 @@ function not(combinator) {
|
|
|
2260
2434
|
};
|
|
2261
2435
|
}
|
|
2262
2436
|
|
|
2437
|
+
// src/combinators/peek.ts
|
|
2438
|
+
function peek(combinator) {
|
|
2439
|
+
const meta = {
|
|
2440
|
+
// A nullable body imposes no first-char constraint — `any()`, not its set.
|
|
2441
|
+
firstSet: matchesEmpty(combinator) ? any() : combinator._meta.firstSet,
|
|
2442
|
+
canMatchNewline: false,
|
|
2443
|
+
isTrivia: false
|
|
2444
|
+
};
|
|
2445
|
+
return {
|
|
2446
|
+
_tag: "peek",
|
|
2447
|
+
_meta: meta,
|
|
2448
|
+
_def: { tag: "peek", parser: combinator },
|
|
2449
|
+
parse(input, pos, ctx) {
|
|
2450
|
+
const mark = saveLookaheadMark(ctx);
|
|
2451
|
+
const result = combinator.parse(input, pos, ctx);
|
|
2452
|
+
rollbackLookahead(ctx, mark);
|
|
2453
|
+
if (result.ok) return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
2454
|
+
return { ok: false, expected: [`peek(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2263
2459
|
// src/compiler/build-arity.ts
|
|
2264
2460
|
var PARAM_LIST_RE = (
|
|
2265
2461
|
// (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
|
|
@@ -2358,6 +2554,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2358
2554
|
case "expect":
|
|
2359
2555
|
case "withCtx":
|
|
2360
2556
|
case "not":
|
|
2557
|
+
case "peek":
|
|
2361
2558
|
return parserHasOwnFields(d.parser, seen);
|
|
2362
2559
|
default:
|
|
2363
2560
|
return false;
|
|
@@ -2397,6 +2594,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2397
2594
|
case "expect":
|
|
2398
2595
|
case "withCtx":
|
|
2399
2596
|
case "not":
|
|
2597
|
+
case "peek":
|
|
2400
2598
|
case "field":
|
|
2401
2599
|
return parserHasTriviaSite(d.parser, seen);
|
|
2402
2600
|
case "choice":
|
|
@@ -2446,6 +2644,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2446
2644
|
case "expect":
|
|
2447
2645
|
case "withCtx":
|
|
2448
2646
|
case "not":
|
|
2647
|
+
case "peek":
|
|
2449
2648
|
return parserEnablesTriviaCapture(d.parser, seen);
|
|
2450
2649
|
// leaf() clears the inner CST/trivia collector and publishes only its own
|
|
2451
2650
|
// terminal result. An inner parser() capture cannot reach this node.
|
|
@@ -2821,7 +3020,7 @@ function leaf(root, fn) {
|
|
|
2821
3020
|
}
|
|
2822
3021
|
|
|
2823
3022
|
// src/version.ts
|
|
2824
|
-
var PARSEMAN_VERSION = "0.
|
|
3023
|
+
var PARSEMAN_VERSION = "0.35.0";
|
|
2825
3024
|
|
|
2826
3025
|
// src/analysis/gating.ts
|
|
2827
3026
|
var isAny = (fs) => fs.kind === "any";
|
|
@@ -2861,14 +3060,15 @@ function firstSetToString(fs) {
|
|
|
2861
3060
|
var ruleNameOf = (p) => p._ruleName;
|
|
2862
3061
|
var SUGGESTIONS = {
|
|
2863
3062
|
"broad-recognizer": "if this arm leads with a keyword regex, use word('kw', boundary) / keywords([...]) \u2014 they expose an EXACT, gating first-set and lower to the same charCodeAt scan. A genuine scanTo fallback is fine; accept it in the gating snapshot if intentional.",
|
|
2864
|
-
"leading-not": "let the arm lead with its actual consuming terminal (first-sets gate it automatically); keep not(...) only as a TRAILING boundary. Do not hand-roll not(not(...)) to fake gating.",
|
|
2865
|
-
"nullable-prefix": "a leading optional/many lets a later, broad term start the arm. Split the empty case into its own arm, or gate on the prefix first char.",
|
|
3063
|
+
"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.",
|
|
3064
|
+
"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.",
|
|
2866
3065
|
"cross-artifact-ref": "parseman >=0.32.0 resolves a g.Foo ref first-set at fuse time; if still ANY the target rule is itself ungated \u2014 analyze it and give it a concrete non-nullable lead.",
|
|
2867
3066
|
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
2868
3067
|
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
2869
3068
|
};
|
|
2870
|
-
function classifyBroadArm(arm) {
|
|
3069
|
+
function classifyBroadArm(arm, resolve) {
|
|
2871
3070
|
const seen = /* @__PURE__ */ new Set();
|
|
3071
|
+
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
2872
3072
|
const walk2 = (p) => {
|
|
2873
3073
|
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
2874
3074
|
seen.add(p);
|
|
@@ -2880,6 +3080,10 @@ function classifyBroadArm(arm) {
|
|
|
2880
3080
|
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
2881
3081
|
case "not":
|
|
2882
3082
|
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
3083
|
+
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
3084
|
+
// (or the body is nullable) — walk in for the real cause.
|
|
3085
|
+
case "peek":
|
|
3086
|
+
return walk2(d.parser);
|
|
2883
3087
|
case "scanTo":
|
|
2884
3088
|
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
2885
3089
|
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
@@ -2896,18 +3100,23 @@ function classifyBroadArm(arm) {
|
|
|
2896
3100
|
return walk2(d.parser);
|
|
2897
3101
|
case "lazy": {
|
|
2898
3102
|
const name = p._ruleName;
|
|
3103
|
+
let target;
|
|
2899
3104
|
try {
|
|
2900
|
-
|
|
2901
|
-
if (name !== void 0) return inner ? { cause: "cross-artifact-ref", detail: `via ref g.${name} \u2192 ${inner.detail}` } : null;
|
|
2902
|
-
return inner;
|
|
3105
|
+
target = d.thunk();
|
|
2903
3106
|
} catch {
|
|
2904
|
-
|
|
3107
|
+
const bound = name !== void 0 ? resolve?.(name) : void 0;
|
|
3108
|
+
if (bound === void 0)
|
|
3109
|
+
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
3110
|
+
target = bound;
|
|
2905
3111
|
}
|
|
3112
|
+
const inner = walk2(target);
|
|
3113
|
+
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
3114
|
+
return inner;
|
|
2906
3115
|
}
|
|
2907
3116
|
case "optional":
|
|
2908
3117
|
case "many": {
|
|
2909
3118
|
const inner = walk2(d.parser);
|
|
2910
|
-
return
|
|
3119
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
2911
3120
|
}
|
|
2912
3121
|
case "oneOrMore":
|
|
2913
3122
|
case "transform":
|
|
@@ -2929,13 +3138,13 @@ function classifyBroadArm(arm) {
|
|
|
2929
3138
|
sawNullablePrefix = true;
|
|
2930
3139
|
continue;
|
|
2931
3140
|
}
|
|
2932
|
-
if (isAny(firstSetOf(t))) {
|
|
3141
|
+
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
|
|
2933
3142
|
const inner = walk2(t);
|
|
2934
3143
|
if (sawNullablePrefix)
|
|
2935
|
-
return
|
|
3144
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
2936
3145
|
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
2937
3146
|
}
|
|
2938
|
-
if (matchesEmpty(t)) {
|
|
3147
|
+
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve)) {
|
|
2939
3148
|
sawNullablePrefix = true;
|
|
2940
3149
|
continue;
|
|
2941
3150
|
}
|
|
@@ -2946,7 +3155,7 @@ function classifyBroadArm(arm) {
|
|
|
2946
3155
|
case "choice": {
|
|
2947
3156
|
for (const a of d.parsers) {
|
|
2948
3157
|
const r = walk2(a);
|
|
2949
|
-
if (r) return
|
|
3158
|
+
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
2950
3159
|
}
|
|
2951
3160
|
return null;
|
|
2952
3161
|
}
|
|
@@ -2983,7 +3192,7 @@ function detectAntiPatterns(rule, arms) {
|
|
|
2983
3192
|
kind: "double-not",
|
|
2984
3193
|
rule,
|
|
2985
3194
|
armIndex: i,
|
|
2986
|
-
message: "not(not(...)) hand-rolls automatic first-char gating and MISCOMPILES among shared-first-char sibling arms (its first-set is ANY, poisoning dispatch).
|
|
3195
|
+
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."
|
|
2987
3196
|
});
|
|
2988
3197
|
} else {
|
|
2989
3198
|
out.push({
|
|
@@ -3006,6 +3215,9 @@ function detectAntiPatterns(rule, arms) {
|
|
|
3006
3215
|
return out;
|
|
3007
3216
|
}
|
|
3008
3217
|
function analyzeGating(entry, opts) {
|
|
3218
|
+
return analyzeGatingRules([[ruleNameOf(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3219
|
+
}
|
|
3220
|
+
function analyzeGatingRules(ruleMap, opts) {
|
|
3009
3221
|
const raw = [];
|
|
3010
3222
|
const antiPatterns = [];
|
|
3011
3223
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -3015,7 +3227,7 @@ function analyzeGating(entry, opts) {
|
|
|
3015
3227
|
const d = p._def;
|
|
3016
3228
|
const rule = ruleNameOf(p) ?? enclosingRule;
|
|
3017
3229
|
if (d.tag === "choice") {
|
|
3018
|
-
raw.push({ g: analyzeChoice(p, d, rule), rule });
|
|
3230
|
+
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
3019
3231
|
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
3020
3232
|
}
|
|
3021
3233
|
const rec = d;
|
|
@@ -3032,7 +3244,7 @@ function analyzeGating(entry, opts) {
|
|
|
3032
3244
|
}
|
|
3033
3245
|
for (const k of kids) visit(k, rule);
|
|
3034
3246
|
};
|
|
3035
|
-
visit(
|
|
3247
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf(root) ?? name);
|
|
3036
3248
|
const perRule = /* @__PURE__ */ new Map();
|
|
3037
3249
|
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3038
3250
|
const seenInRule = /* @__PURE__ */ new Map();
|
|
@@ -3048,18 +3260,19 @@ function analyzeGating(entry, opts) {
|
|
|
3048
3260
|
});
|
|
3049
3261
|
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
3050
3262
|
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
3051
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted);
|
|
3263
|
+
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
3052
3264
|
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
3265
|
+
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
3053
3266
|
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
3054
|
-
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, acceptedUnused, choices, antiPatterns };
|
|
3267
|
+
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, deferred, acceptedUnused, choices, antiPatterns };
|
|
3055
3268
|
}
|
|
3056
|
-
function analyzeChoice(p, d, rule) {
|
|
3269
|
+
function analyzeChoice(p, d, rule, resolve) {
|
|
3057
3270
|
const arms = d.parsers;
|
|
3058
3271
|
const shallow = arms.map((a) => a._meta.firstSet);
|
|
3059
|
-
const deep = arms.map((a) => firstSetOf(a));
|
|
3272
|
+
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve));
|
|
3060
3273
|
const anyArms = deep.map((fs, index) => ({ fs, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
3061
|
-
const { cause, detail } = classifyBroadArm(arms[index]);
|
|
3062
|
-
return { index, cause, detail, shallowAnyOnly: false, suggestion: SUGGESTIONS[cause] };
|
|
3274
|
+
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve);
|
|
3275
|
+
return { index, cause, detail, shallowAnyOnly: false, unresolvedExternal: unresolvedExternal === true, suggestion: SUGGESTIONS[cause] };
|
|
3063
3276
|
});
|
|
3064
3277
|
const overlaps = [];
|
|
3065
3278
|
for (let i = 0; i < deep.length; i++)
|
|
@@ -3075,10 +3288,12 @@ function analyzeChoice(p, d, rule) {
|
|
|
3075
3288
|
if (d.disjoint) gates = "yes";
|
|
3076
3289
|
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
3077
3290
|
else gates = "no";
|
|
3291
|
+
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
3078
3292
|
return {
|
|
3079
3293
|
rule,
|
|
3080
3294
|
strategy: d.strategy?.tag ?? "firstMatch",
|
|
3081
3295
|
gates,
|
|
3296
|
+
deferred,
|
|
3082
3297
|
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
3083
3298
|
anyArms,
|
|
3084
3299
|
overlaps
|
|
@@ -3245,6 +3460,7 @@ function children(def, winners) {
|
|
|
3245
3460
|
case "field":
|
|
3246
3461
|
case "grammar":
|
|
3247
3462
|
case "not":
|
|
3463
|
+
case "peek":
|
|
3248
3464
|
case "node":
|
|
3249
3465
|
case "guard":
|
|
3250
3466
|
case "withCtx":
|
|
@@ -4995,9 +5211,9 @@ function planDisjointDispatch(parsers) {
|
|
|
4995
5211
|
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
4996
5212
|
}
|
|
4997
5213
|
function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
|
|
4998
|
-
const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
|
|
4999
|
-
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
|
|
5000
|
-
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
5214
|
+
const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
|
|
5215
|
+
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}`;
|
|
5216
|
+
return mLg ? `${base}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : base;
|
|
5001
5217
|
}
|
|
5002
5218
|
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
5003
5219
|
if (seen.has(p)) return false;
|
|
@@ -5010,6 +5226,9 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5010
5226
|
case "keywords":
|
|
5011
5227
|
case "guard":
|
|
5012
5228
|
case "not":
|
|
5229
|
+
// peek(): emitted under a non-capturing probe ctx and zero-width on both
|
|
5230
|
+
// outcomes, so it can never leave a partial capture behind.
|
|
5231
|
+
case "peek":
|
|
5013
5232
|
case "trivia":
|
|
5014
5233
|
case "token":
|
|
5015
5234
|
case "leaf":
|
|
@@ -5081,6 +5300,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5081
5300
|
case "leaf":
|
|
5082
5301
|
return true;
|
|
5083
5302
|
case "not":
|
|
5303
|
+
case "peek":
|
|
5084
5304
|
case "guard":
|
|
5085
5305
|
case "trivia":
|
|
5086
5306
|
case "unknown":
|
|
@@ -5207,6 +5427,7 @@ function emitKeywordsFast(def, ctx, pos) {
|
|
|
5207
5427
|
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
5208
5428
|
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
5209
5429
|
if (def.caseInsensitive && def.boundary) return null;
|
|
5430
|
+
if (def.caseInsensitive && def.words.some((w) => w.split("").some((c) => c.charCodeAt(0) > 127))) return null;
|
|
5210
5431
|
let boundary = null;
|
|
5211
5432
|
if (def.boundary) {
|
|
5212
5433
|
let body = def.boundary;
|
|
@@ -5254,7 +5475,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
5254
5475
|
if (fast) return fast;
|
|
5255
5476
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
5256
5477
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
5257
|
-
const flags = def.caseInsensitive ? "
|
|
5478
|
+
const flags = def.caseInsensitive ? "iy" : "uy";
|
|
5258
5479
|
const source = `(?:${alt})${boundary}`;
|
|
5259
5480
|
const key = `${source}/${flags}`;
|
|
5260
5481
|
let rName = ctx.regexMap.get(key);
|
|
@@ -5354,7 +5575,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
5354
5575
|
const endAfterV = v(ctx, "_sea");
|
|
5355
5576
|
stmts.push(
|
|
5356
5577
|
`${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
|
|
5357
|
-
`${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}; }`
|
|
5578
|
+
`${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}; }`
|
|
5358
5579
|
);
|
|
5359
5580
|
valueVars.push(r2.valueVar);
|
|
5360
5581
|
continue;
|
|
@@ -5400,6 +5621,7 @@ function failsAtStart(p) {
|
|
|
5400
5621
|
case "keywords":
|
|
5401
5622
|
case "guard":
|
|
5402
5623
|
case "not":
|
|
5624
|
+
case "peek":
|
|
5403
5625
|
return true;
|
|
5404
5626
|
case "transform":
|
|
5405
5627
|
case "label":
|
|
@@ -5813,14 +6035,17 @@ function emitMany(def, ctx, pos) {
|
|
|
5813
6035
|
];
|
|
5814
6036
|
const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
|
|
5815
6037
|
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
|
|
5816
|
-
|
|
6038
|
+
for (let i = 0; i < def.min; i++) {
|
|
5817
6039
|
const firstR = emit(def.parser, ctx, curV);
|
|
5818
6040
|
stmts.push(...firstR.stmts);
|
|
5819
6041
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
5820
6042
|
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
5821
6043
|
}
|
|
6044
|
+
const maxV = def.max === void 0 ? null : wantValue ? `${arrV}.length` : v(ctx, "_cnt");
|
|
6045
|
+
if (maxV !== null && !wantValue) stmts.push(`${ind(ctx)}let ${maxV} = ${def.min}`);
|
|
5822
6046
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
5823
6047
|
ctx.indent++;
|
|
6048
|
+
if (maxV !== null) stmts.push(`${ind(ctx)}if (${maxV} >= ${def.max}) break`);
|
|
5824
6049
|
let itemPos = curV;
|
|
5825
6050
|
let rollback = "";
|
|
5826
6051
|
if (ctx.activeTrivia) {
|
|
@@ -5837,7 +6062,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5837
6062
|
`${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
|
|
5838
6063
|
);
|
|
5839
6064
|
itemPos = npV;
|
|
5840
|
-
rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
|
|
6065
|
+
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}; `;
|
|
5841
6066
|
} else {
|
|
5842
6067
|
const trivFn = ensureTriviaFn(ctx);
|
|
5843
6068
|
const npV = v(ctx, "_np");
|
|
@@ -5861,7 +6086,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5861
6086
|
`${ind(ctx)}if (!${iterOk}) {`,
|
|
5862
6087
|
`${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
|
|
5863
6088
|
`${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
|
|
5864
|
-
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
|
|
6089
|
+
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : maxV !== null ? [`${ind(ctx)} ${maxV}++`] : [],
|
|
5865
6090
|
`${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
|
|
5866
6091
|
`${ind(ctx)} ${curV} = ${rrV}.end`,
|
|
5867
6092
|
`${ind(ctx)} continue`,
|
|
@@ -5874,6 +6099,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5874
6099
|
stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
5875
6100
|
}
|
|
5876
6101
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
6102
|
+
else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
|
|
5877
6103
|
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
5878
6104
|
ctx.indent--;
|
|
5879
6105
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -5899,7 +6125,7 @@ function emitAttempt(p, def, ctx, pos) {
|
|
|
5899
6125
|
const log = v(ctx, "_atg");
|
|
5900
6126
|
const fields = v(ctx, "_atf");
|
|
5901
6127
|
const errors = v(ctx, "_ate");
|
|
5902
|
-
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}`;
|
|
6128
|
+
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}`;
|
|
5903
6129
|
const traceId = ctx.coverage?.plan.attempts.get(p);
|
|
5904
6130
|
const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
|
|
5905
6131
|
const preGuard = [];
|
|
@@ -5938,8 +6164,8 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5938
6164
|
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
5939
6165
|
...firstStmts
|
|
5940
6166
|
];
|
|
5941
|
-
const failItem = (nextOkVar, itemPosVar,
|
|
5942
|
-
if (!rec) return [
|
|
6167
|
+
const failItem = (nextOkVar, itemPosVar, breakStmt) => {
|
|
6168
|
+
if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) ${breakStmt === "break" ? "break" : `{ ${breakStmt} }`}`];
|
|
5943
6169
|
const rr = v(ctx, "_rr");
|
|
5944
6170
|
return [
|
|
5945
6171
|
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
@@ -5947,10 +6173,24 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5947
6173
|
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
5948
6174
|
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
5949
6175
|
`${ind(ctx)} }`,
|
|
5950
|
-
`${ind(ctx)} ${
|
|
6176
|
+
`${ind(ctx)} ${breakStmt}`,
|
|
5951
6177
|
`${ind(ctx)}}`
|
|
5952
6178
|
];
|
|
5953
6179
|
};
|
|
6180
|
+
const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
|
|
6181
|
+
const postSepMarks = () => {
|
|
6182
|
+
if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
|
|
6183
|
+
const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg");
|
|
6184
|
+
return {
|
|
6185
|
+
decl: [
|
|
6186
|
+
`${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
|
|
6187
|
+
`${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
|
|
6188
|
+
`${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
|
|
6189
|
+
`${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`
|
|
6190
|
+
],
|
|
6191
|
+
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}; `
|
|
6192
|
+
};
|
|
6193
|
+
};
|
|
5954
6194
|
if (rec) {
|
|
5955
6195
|
const rr0 = v(ctx, "_rr0");
|
|
5956
6196
|
const didV = v(ctx, "_did");
|
|
@@ -5976,6 +6216,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5976
6216
|
);
|
|
5977
6217
|
ctx.indent++;
|
|
5978
6218
|
}
|
|
6219
|
+
if (def.max !== void 0) stmts.push(`${ind(ctx)}if (${arrV}.length >= ${def.max}) break`);
|
|
5979
6220
|
let sepAtPos = curV;
|
|
5980
6221
|
if (ctx.activeTrivia) {
|
|
5981
6222
|
if (ctx.capturing) {
|
|
@@ -5993,15 +6234,17 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5993
6234
|
`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
|
|
5994
6235
|
);
|
|
5995
6236
|
sepAtPos = spV;
|
|
5996
|
-
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}; `;
|
|
6237
|
+
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}; `;
|
|
5997
6238
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
5998
6239
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
6240
|
+
const post = postSepMarks();
|
|
6241
|
+
stmts.push(...post.decl);
|
|
5999
6242
|
const npV = v(ctx, "_np");
|
|
6000
6243
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
6001
6244
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6002
6245
|
stmts.push(
|
|
6003
6246
|
...nextStmts,
|
|
6004
|
-
...failItem(nextOk, npV, rollbackToSep,
|
|
6247
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
6005
6248
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6006
6249
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6007
6250
|
);
|
|
@@ -6017,7 +6260,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6017
6260
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6018
6261
|
stmts.push(
|
|
6019
6262
|
...nextStmts,
|
|
6020
|
-
...failItem(nextOk, npV, "",
|
|
6263
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
|
|
6021
6264
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6022
6265
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6023
6266
|
);
|
|
@@ -6033,11 +6276,13 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6033
6276
|
}
|
|
6034
6277
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
6035
6278
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
6036
|
-
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
6279
|
+
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}; ` : "";
|
|
6280
|
+
const post = postSepMarks();
|
|
6281
|
+
stmts.push(...post.decl);
|
|
6037
6282
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
6038
6283
|
stmts.push(
|
|
6039
6284
|
...nextStmts,
|
|
6040
|
-
...failItem(nextOk, sepEnd, nextRb,
|
|
6285
|
+
...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
6041
6286
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6042
6287
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6043
6288
|
);
|
|
@@ -6046,6 +6291,9 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6046
6291
|
stmts.push(`${ind(ctx)}}`);
|
|
6047
6292
|
ctx.indent--;
|
|
6048
6293
|
stmts.push(`${ind(ctx)}}`);
|
|
6294
|
+
if (def.min >= 1) {
|
|
6295
|
+
stmts.push(...emitIfFail(ctx, `${arrV}.length < ${def.min}`, failArrBody(ctx, deriveExpectedArr([def.parser]), curV)));
|
|
6296
|
+
}
|
|
6049
6297
|
return { stmts, valueVar: arrV, endVar: curV };
|
|
6050
6298
|
}
|
|
6051
6299
|
function emitScanTo(def, ctx, pos) {
|
|
@@ -6097,17 +6345,44 @@ function emitScanTo(def, ctx, pos) {
|
|
|
6097
6345
|
return { stmts, valueVar: valV, endVar: curV };
|
|
6098
6346
|
}
|
|
6099
6347
|
function emitNot(def, ctx, pos) {
|
|
6348
|
+
const sinksLive = !!ctx.capturing || !!ctx.recovery;
|
|
6349
|
+
const leaves = sinksLive ? v(ctx, "_ntl") : null;
|
|
6350
|
+
const raw = sinksLive ? v(ctx, "_ntr") : null;
|
|
6351
|
+
const tl = sinksLive ? v(ctx, "_ntt") : null;
|
|
6352
|
+
const log = sinksLive ? v(ctx, "_ntg") : null;
|
|
6353
|
+
const fields = sinksLive ? v(ctx, "_ntf") : null;
|
|
6354
|
+
const errors = sinksLive ? v(ctx, "_nte") : null;
|
|
6100
6355
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
6101
6356
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
6102
6357
|
return {
|
|
6103
6358
|
stmts: [
|
|
6359
|
+
...sinksLive ? [
|
|
6360
|
+
`${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`
|
|
6361
|
+
] : [],
|
|
6104
6362
|
...stmts,
|
|
6363
|
+
...sinksLive ? [
|
|
6364
|
+
`${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}`
|
|
6365
|
+
] : [],
|
|
6105
6366
|
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
6106
6367
|
],
|
|
6107
6368
|
valueVar: "null",
|
|
6108
6369
|
endVar: pos
|
|
6109
6370
|
};
|
|
6110
6371
|
}
|
|
6372
|
+
function emitPeek(def, ctx, pos) {
|
|
6373
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
6374
|
+
const { stmts, okVar } = emitFallible(def.parser, probeCtx, pos, true);
|
|
6375
|
+
ctx.vars = probeCtx.vars;
|
|
6376
|
+
const label2 = JSON.stringify(`peek(${def.parser._tag})`);
|
|
6377
|
+
return {
|
|
6378
|
+
stmts: [
|
|
6379
|
+
...stmts,
|
|
6380
|
+
...emitIfFail(ctx, `!${okVar}`, failBody(ctx, label2, pos))
|
|
6381
|
+
],
|
|
6382
|
+
valueVar: "null",
|
|
6383
|
+
endVar: pos
|
|
6384
|
+
};
|
|
6385
|
+
}
|
|
6111
6386
|
function escapeTokenLiteral(value) {
|
|
6112
6387
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6113
6388
|
}
|
|
@@ -6395,9 +6670,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
6395
6670
|
return { stmts, valueVar: vv, endVar: ev };
|
|
6396
6671
|
}
|
|
6397
6672
|
function emitLazy(p, def, ctx, pos) {
|
|
6398
|
-
const
|
|
6673
|
+
const canonical2 = ctx.ruleNames?.get(p);
|
|
6399
6674
|
const usage = ctx.lazyUsage;
|
|
6400
|
-
if (!
|
|
6675
|
+
if (!canonical2 && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
6401
6676
|
let resolved;
|
|
6402
6677
|
try {
|
|
6403
6678
|
resolved = def.thunk();
|
|
@@ -6407,7 +6682,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
6407
6682
|
return emit(resolved, ctx, pos);
|
|
6408
6683
|
}
|
|
6409
6684
|
if (!ctx.namedParsers.has(p)) {
|
|
6410
|
-
const fnName2 =
|
|
6685
|
+
const fnName2 = canonical2 ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
6411
6686
|
ctx.namedParsers.set(p, fnName2);
|
|
6412
6687
|
let resolved;
|
|
6413
6688
|
try {
|
|
@@ -6690,6 +6965,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
6690
6965
|
}
|
|
6691
6966
|
case "not":
|
|
6692
6967
|
return emitNot(def, ctx, pos);
|
|
6968
|
+
case "peek":
|
|
6969
|
+
return emitPeek(def, ctx, pos);
|
|
6693
6970
|
case "node":
|
|
6694
6971
|
return emitNode(def, ctx, pos);
|
|
6695
6972
|
case "scanTo":
|
|
@@ -6778,6 +7055,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6778
7055
|
case "many":
|
|
6779
7056
|
case "oneOrMore":
|
|
6780
7057
|
case "not":
|
|
7058
|
+
case "peek":
|
|
6781
7059
|
case "transform":
|
|
6782
7060
|
return hasNodeDef(d.parser, seen);
|
|
6783
7061
|
case "skip":
|
|
@@ -6829,17 +7107,18 @@ function childrenOf(def) {
|
|
|
6829
7107
|
case "field":
|
|
6830
7108
|
case "grammar":
|
|
6831
7109
|
case "not":
|
|
7110
|
+
case "peek":
|
|
6832
7111
|
case "node":
|
|
6833
7112
|
case "withCtx":
|
|
6834
7113
|
case "expect":
|
|
6835
7114
|
return [def.parser];
|
|
6836
|
-
// emitMany's
|
|
6837
|
-
// (
|
|
6838
|
-
// emit() call
|
|
7115
|
+
// emitMany's min>=1 branch and emitSepBy both codegen `def.parser` more than
|
|
7116
|
+
// once (`min` mandatory matches, then again inside the repeat loop) — each is a
|
|
7117
|
+
// real emit() call site, so the usage analysis must see one edge per site or it
|
|
6839
7118
|
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
6840
|
-
// referenced from
|
|
7119
|
+
// referenced from several positions within this one compiled function.
|
|
6841
7120
|
case "oneOrMore":
|
|
6842
|
-
return
|
|
7121
|
+
return Array.from({ length: def.min + 1 }, () => def.parser);
|
|
6843
7122
|
case "sepBy":
|
|
6844
7123
|
return [def.parser, def.parser, def.separator];
|
|
6845
7124
|
case "skip":
|
|
@@ -6985,12 +7264,41 @@ function resolveGatingLevel(opt) {
|
|
|
6985
7264
|
return "warn";
|
|
6986
7265
|
}
|
|
6987
7266
|
function runGatingDiagnostic(combinator, opt) {
|
|
7267
|
+
return reportGating(opt, (o) => analyzeGating(combinator, o));
|
|
7268
|
+
}
|
|
7269
|
+
function runGatingDiagnosticRules(ruleMap, opt) {
|
|
7270
|
+
return reportGating(opt, (o) => analyzeGatingRules(ruleMap, o));
|
|
7271
|
+
}
|
|
7272
|
+
function runFusedGatingDiagnostic(getRuleMaps, opt) {
|
|
7273
|
+
if (resolveGatingLevel(opt) === "off") return void 0;
|
|
7274
|
+
const ruleMaps = getRuleMaps();
|
|
7275
|
+
if (!ruleMaps.some((map) => hasExternalRuleRef(map))) return void 0;
|
|
7276
|
+
const winners = /* @__PURE__ */ new Map();
|
|
7277
|
+
for (const map of ruleMaps) for (const [name, rule] of map) {
|
|
7278
|
+
if (rule._def.tag === "lazy") {
|
|
7279
|
+
try {
|
|
7280
|
+
rule._def.thunk();
|
|
7281
|
+
} catch {
|
|
7282
|
+
continue;
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
winners.set(name, rule);
|
|
7286
|
+
}
|
|
7287
|
+
const entries = [...winners];
|
|
7288
|
+
return reportGating(opt, (o) => {
|
|
7289
|
+
const deferredHere = new Set(analyzeGatingRules(entries, o).deferred.map((c) => c.id));
|
|
7290
|
+
const fused = analyzeGatingRules(entries, { ...o, resolveRef: (name) => winners.get(name) });
|
|
7291
|
+
return { ...fused, ungated: fused.ungated.filter((c) => deferredHere.has(c.id)), antiPatterns: [] };
|
|
7292
|
+
});
|
|
7293
|
+
}
|
|
7294
|
+
function reportGating(opt, analyze) {
|
|
6988
7295
|
const level = resolveGatingLevel(opt);
|
|
6989
7296
|
if (level === "off") return void 0;
|
|
6990
|
-
const
|
|
7297
|
+
const obj = opt !== null && typeof opt === "object" ? opt : void 0;
|
|
7298
|
+
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;
|
|
6991
7299
|
let report;
|
|
6992
7300
|
try {
|
|
6993
|
-
report =
|
|
7301
|
+
report = analyze(analyzeOpts);
|
|
6994
7302
|
} catch {
|
|
6995
7303
|
return void 0;
|
|
6996
7304
|
}
|
|
@@ -7127,6 +7435,7 @@ function publicRuleWrapperSource(rule, fnSource) {
|
|
|
7127
7435
|
}
|
|
7128
7436
|
function compileLinkable(ruleMapArg, ns, opts) {
|
|
7129
7437
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
7438
|
+
if (opts?.gating !== void 0) runGatingDiagnosticRules(ruleMapArg, opts.gating);
|
|
7130
7439
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
7131
7440
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
7132
7441
|
const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
@@ -7183,6 +7492,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7183
7492
|
fnNameToKey.set(fn, key);
|
|
7184
7493
|
}
|
|
7185
7494
|
ctx.ruleNames = ruleNames;
|
|
7495
|
+
const externalRefs = /* @__PURE__ */ new Set();
|
|
7186
7496
|
{
|
|
7187
7497
|
const scanned = /* @__PURE__ */ new Set();
|
|
7188
7498
|
const scanExternal = (p) => {
|
|
@@ -7198,11 +7508,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7198
7508
|
}
|
|
7199
7509
|
if (resolved === void 0) {
|
|
7200
7510
|
const name = p._ruleName;
|
|
7201
|
-
if (name
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7511
|
+
if (name) {
|
|
7512
|
+
externalRefs.add(p);
|
|
7513
|
+
if (!ctx.namedParsers.has(p)) {
|
|
7514
|
+
assertRuleName(name);
|
|
7515
|
+
const fn = `_r_${name}`;
|
|
7516
|
+
ruleNames.set(p, fn);
|
|
7517
|
+
ctx.namedParsers.set(p, fn);
|
|
7518
|
+
}
|
|
7206
7519
|
}
|
|
7207
7520
|
return;
|
|
7208
7521
|
}
|
|
@@ -7315,12 +7628,32 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7315
7628
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
7316
7629
|
needsHostReads: !!ctx.needsHostReads,
|
|
7317
7630
|
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
7318
|
-
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
|
|
7631
|
+
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
|
|
7319
7632
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
7320
7633
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
7321
7634
|
};
|
|
7322
7635
|
}
|
|
7323
|
-
function
|
|
7636
|
+
function hasExternalRuleRef(ruleMap) {
|
|
7637
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7638
|
+
const visit = (p) => {
|
|
7639
|
+
if (seen.has(p)) return false;
|
|
7640
|
+
seen.add(p);
|
|
7641
|
+
const def = p._def;
|
|
7642
|
+
if (def.tag === "lazy") {
|
|
7643
|
+
let resolved;
|
|
7644
|
+
try {
|
|
7645
|
+
resolved = def.thunk();
|
|
7646
|
+
} catch {
|
|
7647
|
+
resolved = void 0;
|
|
7648
|
+
}
|
|
7649
|
+
if (resolved === void 0) return p._ruleName !== void 0;
|
|
7650
|
+
return visit(resolved);
|
|
7651
|
+
}
|
|
7652
|
+
return childrenOf(def).some(visit);
|
|
7653
|
+
};
|
|
7654
|
+
return ruleMap.some(([, rule]) => visit(rule));
|
|
7655
|
+
}
|
|
7656
|
+
function hasSemanticReduction(roots, externalRefs) {
|
|
7324
7657
|
const seen = /* @__PURE__ */ new Set();
|
|
7325
7658
|
const visit = (parser2) => {
|
|
7326
7659
|
if (seen.has(parser2)) return false;
|
|
@@ -7331,6 +7664,7 @@ function hasSemanticReduction(roots) {
|
|
|
7331
7664
|
if (def.tag === "guard" || def.tag === "withCtx") return true;
|
|
7332
7665
|
if (def.tag === "node" && def.build !== void 0) return true;
|
|
7333
7666
|
if (def.tag === "lazy") {
|
|
7667
|
+
if (externalRefs?.has(parser2)) return false;
|
|
7334
7668
|
try {
|
|
7335
7669
|
return visit(def.thunk());
|
|
7336
7670
|
} catch {
|
|
@@ -7422,6 +7756,7 @@ function childrenOf2(def) {
|
|
|
7422
7756
|
case "label":
|
|
7423
7757
|
case "field":
|
|
7424
7758
|
case "not":
|
|
7759
|
+
case "peek":
|
|
7425
7760
|
case "node":
|
|
7426
7761
|
case "withCtx":
|
|
7427
7762
|
case "expect":
|
|
@@ -7455,6 +7790,13 @@ function lazyTarget(c) {
|
|
|
7455
7790
|
}
|
|
7456
7791
|
var Unserializable = class extends Error {
|
|
7457
7792
|
};
|
|
7793
|
+
function repeatOpts(min, max, trailing) {
|
|
7794
|
+
const parts = [];
|
|
7795
|
+
if (min !== 0) parts.push(`min: ${min}`);
|
|
7796
|
+
if (max !== void 0) parts.push(`max: ${max}`);
|
|
7797
|
+
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
7798
|
+
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
7799
|
+
}
|
|
7458
7800
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
7459
7801
|
try {
|
|
7460
7802
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -7558,6 +7900,7 @@ function evalRuleMapIR(ir) {
|
|
|
7558
7900
|
"optional",
|
|
7559
7901
|
"sepBy",
|
|
7560
7902
|
"not",
|
|
7903
|
+
"peek",
|
|
7561
7904
|
"node",
|
|
7562
7905
|
"parser",
|
|
7563
7906
|
"scanTo",
|
|
@@ -7590,6 +7933,7 @@ function evalRuleMapIR(ir) {
|
|
|
7590
7933
|
optional,
|
|
7591
7934
|
sepBy,
|
|
7592
7935
|
not,
|
|
7936
|
+
peek,
|
|
7593
7937
|
node,
|
|
7594
7938
|
parser,
|
|
7595
7939
|
scanTo,
|
|
@@ -7764,17 +8108,21 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
7764
8108
|
return `_gch([${items.join(", ")}])`;
|
|
7765
8109
|
}
|
|
7766
8110
|
case "many":
|
|
7767
|
-
return `many(${kid(def.parser)})`;
|
|
8111
|
+
return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
8112
|
+
// A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
|
|
8113
|
+
// it through `many` so the bounds survive (oneOrMore defaults min to 1).
|
|
7768
8114
|
case "oneOrMore":
|
|
7769
|
-
return `oneOrMore(${kid(def.parser)})`;
|
|
8115
|
+
return def.min === 1 && def.max === void 0 ? `oneOrMore(${kid(def.parser)})` : `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
7770
8116
|
case "optional":
|
|
7771
8117
|
return `optional(${kid(def.parser)})`;
|
|
7772
8118
|
case "attempt":
|
|
7773
8119
|
return `attempt(${kid(def.parser)})`;
|
|
7774
8120
|
case "sepBy":
|
|
7775
|
-
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
8121
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)}${repeatOpts(def.min, def.max, def.trailing)})`;
|
|
7776
8122
|
case "not":
|
|
7777
8123
|
return `not(${kid(def.parser)})`;
|
|
8124
|
+
case "peek":
|
|
8125
|
+
return `peek(${kid(def.parser)})`;
|
|
7778
8126
|
case "trivia":
|
|
7779
8127
|
return `trivia(${kid(def.parser)})`;
|
|
7780
8128
|
case "token":
|
|
@@ -7967,6 +8315,9 @@ var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCover
|
|
|
7967
8315
|
function isIRPiece(p) {
|
|
7968
8316
|
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
7969
8317
|
}
|
|
8318
|
+
function carriedRuleMaps(carried) {
|
|
8319
|
+
return carried.filter(isIRPiece).map((p) => evalRuleMapIR(p.ir));
|
|
8320
|
+
}
|
|
7970
8321
|
function coverageRulesOf(carried) {
|
|
7971
8322
|
const winners = {};
|
|
7972
8323
|
for (const piece of carried) {
|
|
@@ -8051,6 +8402,7 @@ function compose(items) {
|
|
|
8051
8402
|
const used = /* @__PURE__ */ new Set();
|
|
8052
8403
|
const trivia2 = composingTriviaOf(items);
|
|
8053
8404
|
const carried = items.flatMap((item) => itemCarried(item, used, trivia2));
|
|
8405
|
+
runFusedGatingDiagnostic(() => carriedRuleMaps(carried));
|
|
8054
8406
|
const pieces = carried.map((p) => materializePiece(p, trivia2));
|
|
8055
8407
|
const map = fuseRules(pieces);
|
|
8056
8408
|
Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
|
|
@@ -8883,7 +9235,7 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
8883
9235
|
};
|
|
8884
9236
|
}
|
|
8885
9237
|
case "sepBy":
|
|
8886
|
-
return sepBy(build(def.parser), build(def.separator));
|
|
9238
|
+
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 } });
|
|
8887
9239
|
case "transform":
|
|
8888
9240
|
return transform(build(def.parser), def.fn);
|
|
8889
9241
|
case "skip":
|
|
@@ -8904,6 +9256,8 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
8904
9256
|
}, build(def.parser));
|
|
8905
9257
|
case "not":
|
|
8906
9258
|
return not(build(def.parser));
|
|
9259
|
+
case "peek":
|
|
9260
|
+
return peek(build(def.parser));
|
|
8907
9261
|
case "node": {
|
|
8908
9262
|
const opts = { ...def.unwrap ? { unwrap: true } : {}, ...def.collapse ? { collapse: true } : {}, ...def.captureTrivia ? { captureTrivia: true } : {}, ...def.trailingTrivia ? { trailingTrivia: true } : {} };
|
|
8909
9263
|
return def.type === void 0 ? node(build(def.parser), def.build, opts) : node(def.type, build(def.parser), def.build, opts);
|
|
@@ -9278,6 +9632,7 @@ export {
|
|
|
9278
9632
|
absolutize,
|
|
9279
9633
|
absolutizeCST,
|
|
9280
9634
|
analyzeGating,
|
|
9635
|
+
analyzeGatingRules,
|
|
9281
9636
|
annotateSpan,
|
|
9282
9637
|
applyEdit,
|
|
9283
9638
|
attempt,
|
|
@@ -9325,10 +9680,12 @@ export {
|
|
|
9325
9680
|
not,
|
|
9326
9681
|
offsetToLineCol,
|
|
9327
9682
|
oneOrMore,
|
|
9683
|
+
oneOrMoreSep,
|
|
9328
9684
|
optional,
|
|
9329
9685
|
parse,
|
|
9330
9686
|
parseDoc,
|
|
9331
9687
|
parser,
|
|
9688
|
+
peek,
|
|
9332
9689
|
ref,
|
|
9333
9690
|
regex,
|
|
9334
9691
|
relativize,
|