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/plugin/index.js
CHANGED
|
@@ -7,6 +7,11 @@ import { ResolverFactory } from "oxc-resolver";
|
|
|
7
7
|
import MagicString from "magic-string";
|
|
8
8
|
|
|
9
9
|
// src/combinators/first-set.ts
|
|
10
|
+
function resolveNamedRef(p, resolve2) {
|
|
11
|
+
if (resolve2 === void 0) return void 0;
|
|
12
|
+
const name = p._ruleName;
|
|
13
|
+
return name === void 0 ? void 0 : resolve2(name);
|
|
14
|
+
}
|
|
10
15
|
function union(a, b) {
|
|
11
16
|
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
12
17
|
if (a.kind === "empty") return b;
|
|
@@ -44,10 +49,10 @@ function any() {
|
|
|
44
49
|
function empty() {
|
|
45
50
|
return { kind: "empty" };
|
|
46
51
|
}
|
|
47
|
-
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
52
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
48
53
|
if (seen.has(p)) return true;
|
|
49
54
|
seen.add(p);
|
|
50
|
-
const me = (c) => matchesEmpty(c, seen);
|
|
55
|
+
const me = (c) => matchesEmpty(c, seen, resolve2);
|
|
51
56
|
const d = p._def;
|
|
52
57
|
switch (d.tag) {
|
|
53
58
|
case "literal":
|
|
@@ -64,8 +69,16 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
64
69
|
case "many":
|
|
65
70
|
case "optional":
|
|
66
71
|
case "not":
|
|
72
|
+
case "peek":
|
|
67
73
|
return true;
|
|
68
74
|
// zero repetitions / absent / lookahead
|
|
75
|
+
// Default `sepBy` is `(item (sep item)*)?` — it MATCHES THE EMPTY STRING.
|
|
76
|
+
// Any `min >= 1` requires that many ITEMS, so it is nullable only when the
|
|
77
|
+
// item is. (Keying this off `min === 1` reported every `{ min: 2 }` list as
|
|
78
|
+
// nullable — safe, but wrong, and it put a bogus `nullable-prefix` note on
|
|
79
|
+
// the gating diagnostic for a list that can never match empty.)
|
|
80
|
+
case "sepBy":
|
|
81
|
+
return d.min >= 1 ? me(d.parser) : true;
|
|
69
82
|
case "oneOrMore":
|
|
70
83
|
return me(d.parser);
|
|
71
84
|
case "sequence":
|
|
@@ -89,7 +102,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
89
102
|
try {
|
|
90
103
|
return me(d.thunk());
|
|
91
104
|
} catch {
|
|
92
|
-
|
|
105
|
+
const t = resolveNamedRef(p, resolve2);
|
|
106
|
+
return t ? me(t) : true;
|
|
93
107
|
}
|
|
94
108
|
default:
|
|
95
109
|
return true;
|
|
@@ -98,18 +112,45 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
98
112
|
function isZeroWidthAssertion(p) {
|
|
99
113
|
return p._def.tag === "not";
|
|
100
114
|
}
|
|
115
|
+
function isPositiveLookahead(p) {
|
|
116
|
+
return p._def.tag === "peek";
|
|
117
|
+
}
|
|
118
|
+
function narrowBy(acc, constraint) {
|
|
119
|
+
if (constraint.kind === "any") return acc;
|
|
120
|
+
if (acc === null) return constraint;
|
|
121
|
+
if (acc.kind === "any") return constraint;
|
|
122
|
+
if (acc.kind === "empty" || constraint.kind === "empty") return { kind: "empty" };
|
|
123
|
+
const ranges = [];
|
|
124
|
+
for (const a of acc.ranges) for (const b of constraint.ranges) {
|
|
125
|
+
const lo = Math.max(a.lo, b.lo);
|
|
126
|
+
const hi = Math.min(a.hi, b.hi);
|
|
127
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
128
|
+
}
|
|
129
|
+
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
130
|
+
}
|
|
131
|
+
function applyAssertion(fs2, assertion) {
|
|
132
|
+
if (assertion === null) return fs2;
|
|
133
|
+
if (fs2.kind === "empty") return assertion;
|
|
134
|
+
return narrowBy(fs2, assertion) ?? fs2;
|
|
135
|
+
}
|
|
101
136
|
function sequenceFirstSet(parsers) {
|
|
102
137
|
let fs2 = empty();
|
|
138
|
+
let assertion = null;
|
|
103
139
|
for (const p of parsers) {
|
|
140
|
+
if (isPositiveLookahead(p)) {
|
|
141
|
+
assertion = narrowBy(assertion, p._meta.firstSet);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
104
144
|
if (!isZeroWidthAssertion(p)) fs2 = union(fs2, p._meta.firstSet);
|
|
105
|
-
if (!matchesEmpty(p)) return fs2;
|
|
145
|
+
if (!matchesEmpty(p)) return applyAssertion(fs2, assertion);
|
|
106
146
|
}
|
|
107
|
-
return fs2;
|
|
147
|
+
return applyAssertion(fs2, assertion);
|
|
108
148
|
}
|
|
109
|
-
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
149
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
110
150
|
if (seen.has(p)) return any();
|
|
111
151
|
seen.add(p);
|
|
112
|
-
const fs2 = (c) => firstSetOf(c, seen);
|
|
152
|
+
const fs2 = (c) => firstSetOf(c, seen, resolve2);
|
|
153
|
+
const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve2);
|
|
113
154
|
const d = p._def;
|
|
114
155
|
switch (d.tag) {
|
|
115
156
|
case "literal":
|
|
@@ -121,7 +162,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
121
162
|
try {
|
|
122
163
|
return fs2(d.thunk());
|
|
123
164
|
} catch {
|
|
124
|
-
|
|
165
|
+
const t = resolveNamedRef(p, resolve2);
|
|
166
|
+
return t ? fs2(t) : any();
|
|
125
167
|
}
|
|
126
168
|
case "choice": {
|
|
127
169
|
let out = empty();
|
|
@@ -130,11 +172,20 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
130
172
|
}
|
|
131
173
|
case "sequence": {
|
|
132
174
|
let out = empty();
|
|
175
|
+
let assertion = null;
|
|
133
176
|
for (const term of d.parsers) {
|
|
177
|
+
if (isPositiveLookahead(term)) {
|
|
178
|
+
assertion = narrowBy(assertion, fs2(term));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
134
181
|
if (!isZeroWidthAssertion(term)) out = union(out, fs2(term));
|
|
135
|
-
if (!
|
|
182
|
+
if (!empties(term)) return applyAssertion(out, assertion);
|
|
136
183
|
}
|
|
137
|
-
return out;
|
|
184
|
+
return applyAssertion(out, assertion);
|
|
185
|
+
}
|
|
186
|
+
case "peek": {
|
|
187
|
+
const inner = d.parser;
|
|
188
|
+
return empties(inner) ? any() : fs2(inner);
|
|
138
189
|
}
|
|
139
190
|
case "oneOrMore":
|
|
140
191
|
case "many":
|
|
@@ -150,6 +201,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
150
201
|
return fs2(d.parser);
|
|
151
202
|
case "sepBy":
|
|
152
203
|
return fs2(d.parser);
|
|
204
|
+
// both min 0 and min 1 start with the item
|
|
153
205
|
case "skip":
|
|
154
206
|
return fs2(d.main);
|
|
155
207
|
default:
|
|
@@ -328,28 +380,28 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
|
|
|
328
380
|
else if (len === 1) {
|
|
329
381
|
b[keySingle] = arr[0];
|
|
330
382
|
b[keyMulti] = void 0;
|
|
331
|
-
} else arr.length = len;
|
|
383
|
+
} else if (arr.length !== len) arr.length = len;
|
|
332
384
|
return;
|
|
333
385
|
}
|
|
334
386
|
if (len === 0) b[keySingle] = void 0;
|
|
335
387
|
}
|
|
336
388
|
function rollbackCstCapture(ctx, mark) {
|
|
337
|
-
if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
|
|
389
|
+
if (ctx._errors && mark.errors !== void 0 && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors;
|
|
338
390
|
const b = ctx._cstBuf;
|
|
339
391
|
if (b) {
|
|
340
392
|
rollbackBufList(b, "raw", "rawSingle", mark.raw);
|
|
341
393
|
rollbackBufList(b, "ch", "single", mark.leaves);
|
|
342
394
|
if (b.tl) {
|
|
343
395
|
if (mark.tlog === 0) b.tl = void 0;
|
|
344
|
-
else b.tl.length = mark.tlog;
|
|
396
|
+
else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog;
|
|
345
397
|
}
|
|
346
|
-
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
398
|
+
if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields;
|
|
347
399
|
return;
|
|
348
400
|
}
|
|
349
|
-
if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
|
|
350
|
-
if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
|
|
351
|
-
if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
|
|
352
|
-
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
401
|
+
if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw;
|
|
402
|
+
if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog;
|
|
403
|
+
if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves;
|
|
404
|
+
if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields;
|
|
353
405
|
}
|
|
354
406
|
function pushCstTriviaEntry(ctx, start, end, kindIndex) {
|
|
355
407
|
const insertIdx = cstRawLen(ctx);
|
|
@@ -871,9 +923,46 @@ function firstSetFromRegex(source) {
|
|
|
871
923
|
return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
|
|
872
924
|
}
|
|
873
925
|
|
|
926
|
+
// src/combinators/case-fold.ts
|
|
927
|
+
function canonical(ch2) {
|
|
928
|
+
const u = ch2.toUpperCase();
|
|
929
|
+
if (u.length !== 1) return ch2;
|
|
930
|
+
if (ch2.codePointAt(0) >= 128 && u.codePointAt(0) < 128) return ch2;
|
|
931
|
+
return u;
|
|
932
|
+
}
|
|
933
|
+
var foldIndex = null;
|
|
934
|
+
function buildFoldIndex() {
|
|
935
|
+
const byCanonical = /* @__PURE__ */ new Map();
|
|
936
|
+
for (let cp = 0; cp < 65536; cp++) {
|
|
937
|
+
if (cp >= 55296 && cp <= 57343) continue;
|
|
938
|
+
const key = canonical(String.fromCharCode(cp));
|
|
939
|
+
const bucket = byCanonical.get(key);
|
|
940
|
+
if (bucket) bucket.push(cp);
|
|
941
|
+
else byCanonical.set(key, [cp]);
|
|
942
|
+
}
|
|
943
|
+
const index = /* @__PURE__ */ new Map();
|
|
944
|
+
for (const members of byCanonical.values()) {
|
|
945
|
+
if (members.length < 2) continue;
|
|
946
|
+
for (const cp of members) index.set(cp, members);
|
|
947
|
+
}
|
|
948
|
+
return index;
|
|
949
|
+
}
|
|
950
|
+
function caseFoldVariants(cp) {
|
|
951
|
+
if (cp < 128) {
|
|
952
|
+
if (cp >= 65 && cp <= 90) return [cp, cp + 32];
|
|
953
|
+
if (cp >= 97 && cp <= 122) return [cp, cp - 32];
|
|
954
|
+
return [cp];
|
|
955
|
+
}
|
|
956
|
+
if (cp > 65535) return [cp];
|
|
957
|
+
foldIndex ??= buildFoldIndex();
|
|
958
|
+
return foldIndex.get(cp) ?? [cp];
|
|
959
|
+
}
|
|
960
|
+
|
|
874
961
|
// src/combinators/keywords.ts
|
|
875
|
-
function word(str,
|
|
876
|
-
|
|
962
|
+
function word(str, boundaryOrOpts, opts) {
|
|
963
|
+
const boundary = typeof boundaryOrOpts === "string" ? boundaryOrOpts : "_0-9A-Za-z";
|
|
964
|
+
const rest = typeof boundaryOrOpts === "object" && boundaryOrOpts !== null ? boundaryOrOpts : opts;
|
|
965
|
+
return keywords([str], { ...rest, boundary });
|
|
877
966
|
}
|
|
878
967
|
function escapeRe(s) {
|
|
879
968
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -882,7 +971,7 @@ function keywords(words, opts = {}) {
|
|
|
882
971
|
const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
|
|
883
972
|
const alt = sorted.map(escapeRe).join("|");
|
|
884
973
|
const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
|
|
885
|
-
const flags = opts.caseInsensitive ? "
|
|
974
|
+
const flags = opts.caseInsensitive ? "iy" : "uy";
|
|
886
975
|
const re = new RegExp(`(?:${alt})${boundary}`, flags);
|
|
887
976
|
let firstSet2 = empty();
|
|
888
977
|
for (const w of sorted) {
|
|
@@ -890,10 +979,7 @@ function keywords(words, opts = {}) {
|
|
|
890
979
|
const cp = w.codePointAt(0);
|
|
891
980
|
firstSet2 = union(firstSet2, fromChar(cp));
|
|
892
981
|
if (opts.caseInsensitive) {
|
|
893
|
-
const
|
|
894
|
-
const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
|
|
895
|
-
if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
|
|
896
|
-
if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
|
|
982
|
+
for (const v2 of caseFoldVariants(cp)) firstSet2 = union(firstSet2, fromChar(v2));
|
|
897
983
|
}
|
|
898
984
|
}
|
|
899
985
|
const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
|
|
@@ -1004,6 +1090,10 @@ function captureError(ctx, error) {
|
|
|
1004
1090
|
|
|
1005
1091
|
// src/combinators/expect.ts
|
|
1006
1092
|
function deriveExpected(c) {
|
|
1093
|
+
return derive(c, /* @__PURE__ */ new Set());
|
|
1094
|
+
}
|
|
1095
|
+
function derive(c, seen) {
|
|
1096
|
+
const deriveExpected2 = (p) => derive(p, seen);
|
|
1007
1097
|
const def = c._def;
|
|
1008
1098
|
switch (def.tag) {
|
|
1009
1099
|
case "literal":
|
|
@@ -1015,20 +1105,32 @@ function deriveExpected(c) {
|
|
|
1015
1105
|
case "label":
|
|
1016
1106
|
return [def.label];
|
|
1017
1107
|
case "choice":
|
|
1018
|
-
return def.parsers.flatMap(
|
|
1019
|
-
|
|
1020
|
-
|
|
1108
|
+
return def.parsers.flatMap(deriveExpected2);
|
|
1109
|
+
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
1110
|
+
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
1111
|
+
// able to fail first — deriving from term 0 alone named a token the parse
|
|
1112
|
+
// never actually required. A leading `not(…)` is zero-width and contributes
|
|
1113
|
+
// nothing (its own expectation is about what must NOT be here), but it is
|
|
1114
|
+
// nullable, so keep scanning past it. Stop at the first term that must match.
|
|
1115
|
+
case "sequence": {
|
|
1116
|
+
const out = [];
|
|
1117
|
+
for (const term of def.parsers) {
|
|
1118
|
+
if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
|
|
1119
|
+
if (!matchesEmpty(term)) break;
|
|
1120
|
+
}
|
|
1121
|
+
return out;
|
|
1122
|
+
}
|
|
1021
1123
|
case "attempt":
|
|
1022
|
-
return
|
|
1124
|
+
return deriveExpected2(def.parser);
|
|
1023
1125
|
// Delegating wrappers whose first-token failure is their inner parser's — must
|
|
1024
1126
|
// pass through so a start-failure (and the first-set-miss fast-path in attempt/
|
|
1025
1127
|
// node, which reads this) names the real expected token, not the wrapper tag.
|
|
1026
1128
|
case "field":
|
|
1027
1129
|
case "withCtx":
|
|
1028
1130
|
case "recover":
|
|
1029
|
-
return
|
|
1131
|
+
return deriveExpected2(def.parser);
|
|
1030
1132
|
case "skip":
|
|
1031
|
-
return
|
|
1133
|
+
return deriveExpected2(def.main);
|
|
1032
1134
|
case "expect":
|
|
1033
1135
|
return def.expected;
|
|
1034
1136
|
case "node":
|
|
@@ -1041,13 +1143,18 @@ function deriveExpected(c) {
|
|
|
1041
1143
|
case "oneOrMore":
|
|
1042
1144
|
case "transform":
|
|
1043
1145
|
case "not":
|
|
1044
|
-
|
|
1146
|
+
case "peek":
|
|
1147
|
+
return deriveExpected2(def.parser);
|
|
1045
1148
|
case "lazy": {
|
|
1046
1149
|
const name = c._ruleName;
|
|
1150
|
+
if (seen.has(c)) return name ? [name] : [];
|
|
1151
|
+
seen.add(c);
|
|
1047
1152
|
try {
|
|
1048
|
-
return
|
|
1153
|
+
return deriveExpected2(def.thunk());
|
|
1049
1154
|
} catch {
|
|
1050
1155
|
return name ? [name] : [];
|
|
1156
|
+
} finally {
|
|
1157
|
+
seen.delete(c);
|
|
1051
1158
|
}
|
|
1052
1159
|
}
|
|
1053
1160
|
default:
|
|
@@ -1175,14 +1282,14 @@ function choice(...args) {
|
|
|
1175
1282
|
const result = parsers[i].parse(input, pos, ctx);
|
|
1176
1283
|
if (!result.ok) {
|
|
1177
1284
|
rollbackCstCapture(ctx, mark);
|
|
1178
|
-
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
1285
|
+
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1179
1286
|
expected.push(...result.expected);
|
|
1180
1287
|
continue;
|
|
1181
1288
|
}
|
|
1182
1289
|
const checks = autoNot[i];
|
|
1183
1290
|
if (checks && autoNotFires(input, result.span.end, checks)) {
|
|
1184
1291
|
rollbackCstCapture(ctx, mark);
|
|
1185
|
-
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
1292
|
+
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1186
1293
|
continue;
|
|
1187
1294
|
}
|
|
1188
1295
|
return result;
|
|
@@ -1509,7 +1616,14 @@ function saveTriviaMark(ctx) {
|
|
|
1509
1616
|
}
|
|
1510
1617
|
function rollbackTrivia(ctx, mark) {
|
|
1511
1618
|
rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
|
|
1512
|
-
if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
|
|
1619
|
+
if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
|
|
1620
|
+
}
|
|
1621
|
+
function saveLookaheadMark(ctx) {
|
|
1622
|
+
return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
|
|
1623
|
+
}
|
|
1624
|
+
function rollbackLookahead(ctx, mark) {
|
|
1625
|
+
rollbackTrivia(ctx, mark);
|
|
1626
|
+
if (ctx._probe) ctx._probe.best = mark.probeBest;
|
|
1513
1627
|
}
|
|
1514
1628
|
function scanWithLabels(input, cur, ctx) {
|
|
1515
1629
|
const triviaP = ctx.trivia;
|
|
@@ -1870,7 +1984,7 @@ function attempt(parser2) {
|
|
|
1870
1984
|
const result = parser2.parse(input, pos, ctx);
|
|
1871
1985
|
if (result.ok) return result;
|
|
1872
1986
|
rollbackCstCapture(ctx, mark);
|
|
1873
|
-
if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
|
|
1987
|
+
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
1874
1988
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
1875
1989
|
}
|
|
1876
1990
|
};
|
|
@@ -1908,13 +2022,26 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1908
2022
|
}
|
|
1909
2023
|
return { value: result.value, end: result.span.end };
|
|
1910
2024
|
}
|
|
1911
|
-
function
|
|
2025
|
+
function resolveBounds(what, opts) {
|
|
2026
|
+
const min = opts.min ?? 0;
|
|
2027
|
+
const max = opts.max ?? Infinity;
|
|
2028
|
+
const bad = (msg) => {
|
|
2029
|
+
throw new RangeError(`parseman: ${what} ${msg}`);
|
|
2030
|
+
};
|
|
2031
|
+
if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
|
|
2032
|
+
if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
|
|
2033
|
+
if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
|
|
2034
|
+
return { min, max };
|
|
2035
|
+
}
|
|
2036
|
+
function many(combinator, opts = {}) {
|
|
2037
|
+
const { min, max } = resolveBounds("many()", opts);
|
|
2038
|
+
if (min >= 1) return atLeast(combinator, min, max);
|
|
1912
2039
|
const meta = {
|
|
1913
2040
|
firstSet: combinator._meta.firstSet,
|
|
1914
2041
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1915
2042
|
isTrivia: false
|
|
1916
2043
|
};
|
|
1917
|
-
const def = { tag: "many", parser: combinator, min: 0 };
|
|
2044
|
+
const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
|
|
1918
2045
|
let expected;
|
|
1919
2046
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1920
2047
|
return {
|
|
@@ -1924,7 +2051,9 @@ function many(combinator) {
|
|
|
1924
2051
|
parse(input, pos, ctx) {
|
|
1925
2052
|
const values = def.valueUnused ? void 0 : [];
|
|
1926
2053
|
let cur = pos;
|
|
2054
|
+
let count = 0;
|
|
1927
2055
|
while (cur < input.length) {
|
|
2056
|
+
if (count >= max) break;
|
|
1928
2057
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1929
2058
|
if (item === "stop") break;
|
|
1930
2059
|
if ("fail" in item) {
|
|
@@ -1935,23 +2064,28 @@ function many(combinator) {
|
|
|
1935
2064
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1936
2065
|
if (values !== void 0) values.push(error);
|
|
1937
2066
|
captureError(ctx, error);
|
|
2067
|
+
count++;
|
|
1938
2068
|
cur = end;
|
|
1939
2069
|
continue;
|
|
1940
2070
|
}
|
|
1941
2071
|
if (values !== void 0) values.push(item.value);
|
|
2072
|
+
count++;
|
|
1942
2073
|
cur = item.end;
|
|
1943
2074
|
}
|
|
1944
2075
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1945
2076
|
}
|
|
1946
2077
|
};
|
|
1947
2078
|
}
|
|
1948
|
-
function oneOrMore(combinator) {
|
|
2079
|
+
function oneOrMore(combinator, opts = {}) {
|
|
2080
|
+
return many(combinator, { ...opts, min: opts.min ?? 1 });
|
|
2081
|
+
}
|
|
2082
|
+
function atLeast(combinator, min, max) {
|
|
1949
2083
|
const meta = {
|
|
1950
2084
|
firstSet: combinator._meta.firstSet,
|
|
1951
2085
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1952
2086
|
isTrivia: false
|
|
1953
2087
|
};
|
|
1954
|
-
const def = { tag: "oneOrMore", parser: combinator, min:
|
|
2088
|
+
const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
|
|
1955
2089
|
let expected;
|
|
1956
2090
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1957
2091
|
return {
|
|
@@ -1963,7 +2097,19 @@ function oneOrMore(combinator) {
|
|
|
1963
2097
|
if (!first.ok) return first;
|
|
1964
2098
|
const values = def.valueUnused ? void 0 : [first.value];
|
|
1965
2099
|
let cur = first.span.end;
|
|
2100
|
+
let count = 1;
|
|
2101
|
+
while (count < min) {
|
|
2102
|
+
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2103
|
+
if (item === "stop" || "fail" in item) {
|
|
2104
|
+
expected ??= deriveExpected(combinator);
|
|
2105
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
2106
|
+
}
|
|
2107
|
+
if (values !== void 0) values.push(item.value);
|
|
2108
|
+
cur = item.end;
|
|
2109
|
+
count++;
|
|
2110
|
+
}
|
|
1966
2111
|
while (cur < input.length) {
|
|
2112
|
+
if (count >= max) break;
|
|
1967
2113
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1968
2114
|
if (item === "stop") break;
|
|
1969
2115
|
if ("fail" in item) {
|
|
@@ -1974,10 +2120,12 @@ function oneOrMore(combinator) {
|
|
|
1974
2120
|
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1975
2121
|
if (values !== void 0) values.push(error);
|
|
1976
2122
|
captureError(ctx, error);
|
|
2123
|
+
count++;
|
|
1977
2124
|
cur = end;
|
|
1978
2125
|
continue;
|
|
1979
2126
|
}
|
|
1980
2127
|
if (values !== void 0) values.push(item.value);
|
|
2128
|
+
count++;
|
|
1981
2129
|
cur = item.end;
|
|
1982
2130
|
}
|
|
1983
2131
|
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
@@ -2007,17 +2155,33 @@ function optional(combinator) {
|
|
|
2007
2155
|
}
|
|
2008
2156
|
};
|
|
2009
2157
|
}
|
|
2010
|
-
function
|
|
2158
|
+
function oneOrMoreSep(combinator, separator, opts = {}) {
|
|
2159
|
+
return sepBy(combinator, separator, { ...opts, min: opts.min ?? 1 });
|
|
2160
|
+
}
|
|
2161
|
+
function sepBy(combinator, separator, opts = {}) {
|
|
2162
|
+
const { min, max } = resolveBounds("sepBy()", opts);
|
|
2163
|
+
const trailing = opts.trailing ?? "forbid";
|
|
2011
2164
|
const meta = {
|
|
2012
2165
|
firstSet: combinator._meta.firstSet,
|
|
2013
2166
|
canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
|
|
2014
2167
|
isTrivia: false
|
|
2015
2168
|
};
|
|
2016
2169
|
let expected;
|
|
2170
|
+
const failAt2 = (at) => {
|
|
2171
|
+
expected ??= deriveExpected(combinator);
|
|
2172
|
+
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
|
|
2173
|
+
};
|
|
2017
2174
|
return {
|
|
2018
2175
|
_tag: "sepBy",
|
|
2019
2176
|
_meta: meta,
|
|
2020
|
-
_def: {
|
|
2177
|
+
_def: {
|
|
2178
|
+
tag: "sepBy",
|
|
2179
|
+
parser: combinator,
|
|
2180
|
+
separator,
|
|
2181
|
+
min,
|
|
2182
|
+
...max === Infinity ? {} : { max },
|
|
2183
|
+
...trailing === "forbid" ? {} : { trailing }
|
|
2184
|
+
},
|
|
2021
2185
|
parse(input, pos, ctx) {
|
|
2022
2186
|
const first = combinator.parse(input, pos, ctx);
|
|
2023
2187
|
const values = [];
|
|
@@ -2028,6 +2192,7 @@ function sepBy(combinator, separator) {
|
|
|
2028
2192
|
} else {
|
|
2029
2193
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2030
2194
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2195
|
+
if (min >= 1) return failAt2(pos);
|
|
2031
2196
|
return { ok: true, value: [], span: { start: pos, end: pos } };
|
|
2032
2197
|
}
|
|
2033
2198
|
expected ??= deriveExpected(combinator);
|
|
@@ -2037,6 +2202,7 @@ function sepBy(combinator, separator) {
|
|
|
2037
2202
|
cur = rec.end;
|
|
2038
2203
|
}
|
|
2039
2204
|
while (cur < input.length) {
|
|
2205
|
+
if (values.length >= max) break;
|
|
2040
2206
|
const loopMark = saveTriviaMark(ctx);
|
|
2041
2207
|
let sepPos = cur;
|
|
2042
2208
|
if (ctx.trivia) {
|
|
@@ -2053,6 +2219,7 @@ function sepBy(combinator, separator) {
|
|
|
2053
2219
|
rollbackTrivia(ctx, loopMark);
|
|
2054
2220
|
break;
|
|
2055
2221
|
}
|
|
2222
|
+
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
2056
2223
|
let nextPos = sep2.span.end;
|
|
2057
2224
|
if (ctx.trivia) {
|
|
2058
2225
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
@@ -2074,12 +2241,18 @@ function sepBy(combinator, separator) {
|
|
|
2074
2241
|
cur = rec.end;
|
|
2075
2242
|
continue;
|
|
2076
2243
|
}
|
|
2244
|
+
if (sepMark !== void 0) {
|
|
2245
|
+
rollbackTrivia(ctx, sepMark);
|
|
2246
|
+
cur = sep2.span.end;
|
|
2247
|
+
break;
|
|
2248
|
+
}
|
|
2077
2249
|
rollbackTrivia(ctx, loopMark);
|
|
2078
2250
|
break;
|
|
2079
2251
|
}
|
|
2080
2252
|
values.push(next.value);
|
|
2081
2253
|
cur = next.span.end;
|
|
2082
2254
|
}
|
|
2255
|
+
if (values.length < min) return failAt2(cur);
|
|
2083
2256
|
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
2084
2257
|
}
|
|
2085
2258
|
};
|
|
@@ -2132,6 +2305,7 @@ function markUnusedValues(root) {
|
|
|
2132
2305
|
visit(def.separator, false);
|
|
2133
2306
|
return;
|
|
2134
2307
|
case "not":
|
|
2308
|
+
case "peek":
|
|
2135
2309
|
visit(def.parser, false);
|
|
2136
2310
|
return;
|
|
2137
2311
|
case "grammar":
|
|
@@ -2250,9 +2424,9 @@ function not(combinator) {
|
|
|
2250
2424
|
_meta: meta,
|
|
2251
2425
|
_def: { tag: "not", parser: combinator },
|
|
2252
2426
|
parse(input, pos, ctx) {
|
|
2253
|
-
const mark =
|
|
2427
|
+
const mark = saveLookaheadMark(ctx);
|
|
2254
2428
|
const result = combinator.parse(input, pos, ctx);
|
|
2255
|
-
|
|
2429
|
+
rollbackLookahead(ctx, mark);
|
|
2256
2430
|
if (result.ok) {
|
|
2257
2431
|
return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2258
2432
|
}
|
|
@@ -2261,6 +2435,28 @@ function not(combinator) {
|
|
|
2261
2435
|
};
|
|
2262
2436
|
}
|
|
2263
2437
|
|
|
2438
|
+
// src/combinators/peek.ts
|
|
2439
|
+
function peek(combinator) {
|
|
2440
|
+
const meta = {
|
|
2441
|
+
// A nullable body imposes no first-char constraint — `any()`, not its set.
|
|
2442
|
+
firstSet: matchesEmpty(combinator) ? any() : combinator._meta.firstSet,
|
|
2443
|
+
canMatchNewline: false,
|
|
2444
|
+
isTrivia: false
|
|
2445
|
+
};
|
|
2446
|
+
return {
|
|
2447
|
+
_tag: "peek",
|
|
2448
|
+
_meta: meta,
|
|
2449
|
+
_def: { tag: "peek", parser: combinator },
|
|
2450
|
+
parse(input, pos, ctx) {
|
|
2451
|
+
const mark = saveLookaheadMark(ctx);
|
|
2452
|
+
const result = combinator.parse(input, pos, ctx);
|
|
2453
|
+
rollbackLookahead(ctx, mark);
|
|
2454
|
+
if (result.ok) return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
2455
|
+
return { ok: false, expected: [`peek(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2264
2460
|
// src/compiler/build-arity.ts
|
|
2265
2461
|
var PARAM_LIST_RE = (
|
|
2266
2462
|
// (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
|
|
@@ -2359,6 +2555,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2359
2555
|
case "expect":
|
|
2360
2556
|
case "withCtx":
|
|
2361
2557
|
case "not":
|
|
2558
|
+
case "peek":
|
|
2362
2559
|
return parserHasOwnFields(d.parser, seen);
|
|
2363
2560
|
default:
|
|
2364
2561
|
return false;
|
|
@@ -2398,6 +2595,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2398
2595
|
case "expect":
|
|
2399
2596
|
case "withCtx":
|
|
2400
2597
|
case "not":
|
|
2598
|
+
case "peek":
|
|
2401
2599
|
case "field":
|
|
2402
2600
|
return parserHasTriviaSite(d.parser, seen);
|
|
2403
2601
|
case "choice":
|
|
@@ -2447,6 +2645,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2447
2645
|
case "expect":
|
|
2448
2646
|
case "withCtx":
|
|
2449
2647
|
case "not":
|
|
2648
|
+
case "peek":
|
|
2450
2649
|
return parserEnablesTriviaCapture(d.parser, seen);
|
|
2451
2650
|
// leaf() clears the inner CST/trivia collector and publishes only its own
|
|
2452
2651
|
// terminal result. An inner parser() capture cannot reach this node.
|
|
@@ -2795,7 +2994,7 @@ function leaf(root, fn) {
|
|
|
2795
2994
|
}
|
|
2796
2995
|
|
|
2797
2996
|
// src/version.ts
|
|
2798
|
-
var PARSEMAN_VERSION = "0.
|
|
2997
|
+
var PARSEMAN_VERSION = "0.35.0";
|
|
2799
2998
|
|
|
2800
2999
|
// src/analysis/gating.ts
|
|
2801
3000
|
var isAny = (fs2) => fs2.kind === "any";
|
|
@@ -2835,14 +3034,15 @@ function firstSetToString(fs2) {
|
|
|
2835
3034
|
var ruleNameOf = (p) => p._ruleName;
|
|
2836
3035
|
var SUGGESTIONS = {
|
|
2837
3036
|
"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.",
|
|
2838
|
-
"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.",
|
|
2839
|
-
"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.",
|
|
3037
|
+
"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.",
|
|
3038
|
+
"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.",
|
|
2840
3039
|
"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.",
|
|
2841
3040
|
"opaque-wrapper": "this wrapper (guard/withCtx/recover) contributes no first char; put a concrete leading terminal before it.",
|
|
2842
3041
|
"ref-cycle": "a recursive ref resolved to ANY; ensure the recursion has a concrete terminal lead on the base case."
|
|
2843
3042
|
};
|
|
2844
|
-
function classifyBroadArm(arm) {
|
|
3043
|
+
function classifyBroadArm(arm, resolve2) {
|
|
2845
3044
|
const seen = /* @__PURE__ */ new Set();
|
|
3045
|
+
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
2846
3046
|
const walk2 = (p) => {
|
|
2847
3047
|
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
2848
3048
|
seen.add(p);
|
|
@@ -2854,6 +3054,10 @@ function classifyBroadArm(arm) {
|
|
|
2854
3054
|
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
2855
3055
|
case "not":
|
|
2856
3056
|
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
3057
|
+
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
3058
|
+
// (or the body is nullable) — walk in for the real cause.
|
|
3059
|
+
case "peek":
|
|
3060
|
+
return walk2(d.parser);
|
|
2857
3061
|
case "scanTo":
|
|
2858
3062
|
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
2859
3063
|
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
@@ -2870,18 +3074,23 @@ function classifyBroadArm(arm) {
|
|
|
2870
3074
|
return walk2(d.parser);
|
|
2871
3075
|
case "lazy": {
|
|
2872
3076
|
const name = p._ruleName;
|
|
3077
|
+
let target;
|
|
2873
3078
|
try {
|
|
2874
|
-
|
|
2875
|
-
if (name !== void 0) return inner ? { cause: "cross-artifact-ref", detail: `via ref g.${name} \u2192 ${inner.detail}` } : null;
|
|
2876
|
-
return inner;
|
|
3079
|
+
target = d.thunk();
|
|
2877
3080
|
} catch {
|
|
2878
|
-
|
|
3081
|
+
const bound = name !== void 0 ? resolve2?.(name) : void 0;
|
|
3082
|
+
if (bound === void 0)
|
|
3083
|
+
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
3084
|
+
target = bound;
|
|
2879
3085
|
}
|
|
3086
|
+
const inner = walk2(target);
|
|
3087
|
+
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
3088
|
+
return inner;
|
|
2880
3089
|
}
|
|
2881
3090
|
case "optional":
|
|
2882
3091
|
case "many": {
|
|
2883
3092
|
const inner = walk2(d.parser);
|
|
2884
|
-
return
|
|
3093
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
2885
3094
|
}
|
|
2886
3095
|
case "oneOrMore":
|
|
2887
3096
|
case "transform":
|
|
@@ -2903,13 +3112,13 @@ function classifyBroadArm(arm) {
|
|
|
2903
3112
|
sawNullablePrefix = true;
|
|
2904
3113
|
continue;
|
|
2905
3114
|
}
|
|
2906
|
-
if (isAny(firstSetOf(t))) {
|
|
3115
|
+
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
|
|
2907
3116
|
const inner = walk2(t);
|
|
2908
3117
|
if (sawNullablePrefix)
|
|
2909
|
-
return
|
|
3118
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
2910
3119
|
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
2911
3120
|
}
|
|
2912
|
-
if (matchesEmpty(t)) {
|
|
3121
|
+
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve2)) {
|
|
2913
3122
|
sawNullablePrefix = true;
|
|
2914
3123
|
continue;
|
|
2915
3124
|
}
|
|
@@ -2920,7 +3129,7 @@ function classifyBroadArm(arm) {
|
|
|
2920
3129
|
case "choice": {
|
|
2921
3130
|
for (const a of d.parsers) {
|
|
2922
3131
|
const r = walk2(a);
|
|
2923
|
-
if (r) return
|
|
3132
|
+
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
2924
3133
|
}
|
|
2925
3134
|
return null;
|
|
2926
3135
|
}
|
|
@@ -2957,7 +3166,7 @@ function detectAntiPatterns(rule, arms) {
|
|
|
2957
3166
|
kind: "double-not",
|
|
2958
3167
|
rule,
|
|
2959
3168
|
armIndex: i,
|
|
2960
|
-
message: "not(not(...)) hand-rolls automatic first-char gating and MISCOMPILES among shared-first-char sibling arms (its first-set is ANY, poisoning dispatch).
|
|
3169
|
+
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."
|
|
2961
3170
|
});
|
|
2962
3171
|
} else {
|
|
2963
3172
|
out.push({
|
|
@@ -2980,6 +3189,9 @@ function detectAntiPatterns(rule, arms) {
|
|
|
2980
3189
|
return out;
|
|
2981
3190
|
}
|
|
2982
3191
|
function analyzeGating(entry, opts) {
|
|
3192
|
+
return analyzeGatingRules([[ruleNameOf(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3193
|
+
}
|
|
3194
|
+
function analyzeGatingRules(ruleMap, opts) {
|
|
2983
3195
|
const raw = [];
|
|
2984
3196
|
const antiPatterns = [];
|
|
2985
3197
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -2989,7 +3201,7 @@ function analyzeGating(entry, opts) {
|
|
|
2989
3201
|
const d = p._def;
|
|
2990
3202
|
const rule = ruleNameOf(p) ?? enclosingRule;
|
|
2991
3203
|
if (d.tag === "choice") {
|
|
2992
|
-
raw.push({ g: analyzeChoice(p, d, rule), rule });
|
|
3204
|
+
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
2993
3205
|
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
2994
3206
|
}
|
|
2995
3207
|
const rec = d;
|
|
@@ -3006,7 +3218,7 @@ function analyzeGating(entry, opts) {
|
|
|
3006
3218
|
}
|
|
3007
3219
|
for (const k of kids) visit(k, rule);
|
|
3008
3220
|
};
|
|
3009
|
-
visit(
|
|
3221
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf(root) ?? name);
|
|
3010
3222
|
const perRule = /* @__PURE__ */ new Map();
|
|
3011
3223
|
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3012
3224
|
const seenInRule = /* @__PURE__ */ new Map();
|
|
@@ -3022,18 +3234,19 @@ function analyzeGating(entry, opts) {
|
|
|
3022
3234
|
});
|
|
3023
3235
|
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
3024
3236
|
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
3025
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted);
|
|
3237
|
+
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
3026
3238
|
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
3239
|
+
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
3027
3240
|
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
3028
|
-
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, acceptedUnused, choices, antiPatterns };
|
|
3241
|
+
return { totalChoices: choices.length, gated, recoverable, ungated, accepted, deferred, acceptedUnused, choices, antiPatterns };
|
|
3029
3242
|
}
|
|
3030
|
-
function analyzeChoice(p, d, rule) {
|
|
3243
|
+
function analyzeChoice(p, d, rule, resolve2) {
|
|
3031
3244
|
const arms = d.parsers;
|
|
3032
3245
|
const shallow = arms.map((a) => a._meta.firstSet);
|
|
3033
|
-
const deep = arms.map((a) => firstSetOf(a));
|
|
3246
|
+
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve2));
|
|
3034
3247
|
const anyArms = deep.map((fs2, index) => ({ fs: fs2, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
3035
|
-
const { cause, detail } = classifyBroadArm(arms[index]);
|
|
3036
|
-
return { index, cause, detail, shallowAnyOnly: false, suggestion: SUGGESTIONS[cause] };
|
|
3248
|
+
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve2);
|
|
3249
|
+
return { index, cause, detail, shallowAnyOnly: false, unresolvedExternal: unresolvedExternal === true, suggestion: SUGGESTIONS[cause] };
|
|
3037
3250
|
});
|
|
3038
3251
|
const overlaps = [];
|
|
3039
3252
|
for (let i = 0; i < deep.length; i++)
|
|
@@ -3049,10 +3262,12 @@ function analyzeChoice(p, d, rule) {
|
|
|
3049
3262
|
if (d.disjoint) gates = "yes";
|
|
3050
3263
|
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
3051
3264
|
else gates = "no";
|
|
3265
|
+
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
3052
3266
|
return {
|
|
3053
3267
|
rule,
|
|
3054
3268
|
strategy: d.strategy?.tag ?? "firstMatch",
|
|
3055
3269
|
gates,
|
|
3270
|
+
deferred,
|
|
3056
3271
|
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
3057
3272
|
anyArms,
|
|
3058
3273
|
overlaps
|
|
@@ -3219,6 +3434,7 @@ function children(def, winners) {
|
|
|
3219
3434
|
case "field":
|
|
3220
3435
|
case "grammar":
|
|
3221
3436
|
case "not":
|
|
3437
|
+
case "peek":
|
|
3222
3438
|
case "node":
|
|
3223
3439
|
case "guard":
|
|
3224
3440
|
case "withCtx":
|
|
@@ -4977,9 +5193,9 @@ function planDisjointDispatch(parsers) {
|
|
|
4977
5193
|
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
4978
5194
|
}
|
|
4979
5195
|
function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
|
|
4980
|
-
const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
|
|
4981
|
-
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
|
|
4982
|
-
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
5196
|
+
const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
|
|
5197
|
+
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}`;
|
|
5198
|
+
return mLg ? `${base}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : base;
|
|
4983
5199
|
}
|
|
4984
5200
|
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
4985
5201
|
if (seen.has(p)) return false;
|
|
@@ -4992,6 +5208,9 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4992
5208
|
case "keywords":
|
|
4993
5209
|
case "guard":
|
|
4994
5210
|
case "not":
|
|
5211
|
+
// peek(): emitted under a non-capturing probe ctx and zero-width on both
|
|
5212
|
+
// outcomes, so it can never leave a partial capture behind.
|
|
5213
|
+
case "peek":
|
|
4995
5214
|
case "trivia":
|
|
4996
5215
|
case "token":
|
|
4997
5216
|
case "leaf":
|
|
@@ -5063,6 +5282,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5063
5282
|
case "leaf":
|
|
5064
5283
|
return true;
|
|
5065
5284
|
case "not":
|
|
5285
|
+
case "peek":
|
|
5066
5286
|
case "guard":
|
|
5067
5287
|
case "trivia":
|
|
5068
5288
|
case "unknown":
|
|
@@ -5189,6 +5409,7 @@ function emitKeywordsFast(def, ctx, pos) {
|
|
|
5189
5409
|
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
5190
5410
|
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
5191
5411
|
if (def.caseInsensitive && def.boundary) return null;
|
|
5412
|
+
if (def.caseInsensitive && def.words.some((w) => w.split("").some((c) => c.charCodeAt(0) > 127))) return null;
|
|
5192
5413
|
let boundary = null;
|
|
5193
5414
|
if (def.boundary) {
|
|
5194
5415
|
let body = def.boundary;
|
|
@@ -5236,7 +5457,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
5236
5457
|
if (fast) return fast;
|
|
5237
5458
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
5238
5459
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
5239
|
-
const flags = def.caseInsensitive ? "
|
|
5460
|
+
const flags = def.caseInsensitive ? "iy" : "uy";
|
|
5240
5461
|
const source = `(?:${alt})${boundary}`;
|
|
5241
5462
|
const key = `${source}/${flags}`;
|
|
5242
5463
|
let rName = ctx.regexMap.get(key);
|
|
@@ -5336,7 +5557,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
5336
5557
|
const endAfterV = v(ctx, "_sea");
|
|
5337
5558
|
stmts.push(
|
|
5338
5559
|
`${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
|
|
5339
|
-
`${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}; }`
|
|
5560
|
+
`${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}; }`
|
|
5340
5561
|
);
|
|
5341
5562
|
valueVars.push(r2.valueVar);
|
|
5342
5563
|
continue;
|
|
@@ -5382,6 +5603,7 @@ function failsAtStart(p) {
|
|
|
5382
5603
|
case "keywords":
|
|
5383
5604
|
case "guard":
|
|
5384
5605
|
case "not":
|
|
5606
|
+
case "peek":
|
|
5385
5607
|
return true;
|
|
5386
5608
|
case "transform":
|
|
5387
5609
|
case "label":
|
|
@@ -5795,14 +6017,17 @@ function emitMany(def, ctx, pos) {
|
|
|
5795
6017
|
];
|
|
5796
6018
|
const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
|
|
5797
6019
|
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
|
|
5798
|
-
|
|
6020
|
+
for (let i = 0; i < def.min; i++) {
|
|
5799
6021
|
const firstR = emit(def.parser, ctx, curV);
|
|
5800
6022
|
stmts.push(...firstR.stmts);
|
|
5801
6023
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
5802
6024
|
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
5803
6025
|
}
|
|
6026
|
+
const maxV = def.max === void 0 ? null : wantValue ? `${arrV}.length` : v(ctx, "_cnt");
|
|
6027
|
+
if (maxV !== null && !wantValue) stmts.push(`${ind(ctx)}let ${maxV} = ${def.min}`);
|
|
5804
6028
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
5805
6029
|
ctx.indent++;
|
|
6030
|
+
if (maxV !== null) stmts.push(`${ind(ctx)}if (${maxV} >= ${def.max}) break`);
|
|
5806
6031
|
let itemPos = curV;
|
|
5807
6032
|
let rollback = "";
|
|
5808
6033
|
if (ctx.activeTrivia) {
|
|
@@ -5819,7 +6044,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5819
6044
|
`${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
|
|
5820
6045
|
);
|
|
5821
6046
|
itemPos = npV;
|
|
5822
|
-
rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
|
|
6047
|
+
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}; `;
|
|
5823
6048
|
} else {
|
|
5824
6049
|
const trivFn = ensureTriviaFn(ctx);
|
|
5825
6050
|
const npV = v(ctx, "_np");
|
|
@@ -5843,7 +6068,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5843
6068
|
`${ind(ctx)}if (!${iterOk}) {`,
|
|
5844
6069
|
`${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
|
|
5845
6070
|
`${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
|
|
5846
|
-
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
|
|
6071
|
+
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : maxV !== null ? [`${ind(ctx)} ${maxV}++`] : [],
|
|
5847
6072
|
`${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
|
|
5848
6073
|
`${ind(ctx)} ${curV} = ${rrV}.end`,
|
|
5849
6074
|
`${ind(ctx)} continue`,
|
|
@@ -5856,6 +6081,7 @@ function emitMany(def, ctx, pos) {
|
|
|
5856
6081
|
stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
5857
6082
|
}
|
|
5858
6083
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
6084
|
+
else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
|
|
5859
6085
|
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
5860
6086
|
ctx.indent--;
|
|
5861
6087
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -5881,7 +6107,7 @@ function emitAttempt(p, def, ctx, pos) {
|
|
|
5881
6107
|
const log = v(ctx, "_atg");
|
|
5882
6108
|
const fields = v(ctx, "_atf");
|
|
5883
6109
|
const errors = v(ctx, "_ate");
|
|
5884
|
-
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}`;
|
|
6110
|
+
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}`;
|
|
5885
6111
|
const traceId = ctx.coverage?.plan.attempts.get(p);
|
|
5886
6112
|
const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
|
|
5887
6113
|
const preGuard = [];
|
|
@@ -5920,8 +6146,8 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5920
6146
|
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
5921
6147
|
...firstStmts
|
|
5922
6148
|
];
|
|
5923
|
-
const failItem = (nextOkVar, itemPosVar,
|
|
5924
|
-
if (!rec) return [
|
|
6149
|
+
const failItem = (nextOkVar, itemPosVar, breakStmt) => {
|
|
6150
|
+
if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) ${breakStmt === "break" ? "break" : `{ ${breakStmt} }`}`];
|
|
5925
6151
|
const rr = v(ctx, "_rr");
|
|
5926
6152
|
return [
|
|
5927
6153
|
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
@@ -5929,10 +6155,24 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5929
6155
|
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
5930
6156
|
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
5931
6157
|
`${ind(ctx)} }`,
|
|
5932
|
-
`${ind(ctx)} ${
|
|
6158
|
+
`${ind(ctx)} ${breakStmt}`,
|
|
5933
6159
|
`${ind(ctx)}}`
|
|
5934
6160
|
];
|
|
5935
6161
|
};
|
|
6162
|
+
const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
|
|
6163
|
+
const postSepMarks = () => {
|
|
6164
|
+
if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
|
|
6165
|
+
const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg");
|
|
6166
|
+
return {
|
|
6167
|
+
decl: [
|
|
6168
|
+
`${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
|
|
6169
|
+
`${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
|
|
6170
|
+
`${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
|
|
6171
|
+
`${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`
|
|
6172
|
+
],
|
|
6173
|
+
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}; `
|
|
6174
|
+
};
|
|
6175
|
+
};
|
|
5936
6176
|
if (rec) {
|
|
5937
6177
|
const rr0 = v(ctx, "_rr0");
|
|
5938
6178
|
const didV = v(ctx, "_did");
|
|
@@ -5958,6 +6198,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5958
6198
|
);
|
|
5959
6199
|
ctx.indent++;
|
|
5960
6200
|
}
|
|
6201
|
+
if (def.max !== void 0) stmts.push(`${ind(ctx)}if (${arrV}.length >= ${def.max}) break`);
|
|
5961
6202
|
let sepAtPos = curV;
|
|
5962
6203
|
if (ctx.activeTrivia) {
|
|
5963
6204
|
if (ctx.capturing) {
|
|
@@ -5975,15 +6216,17 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5975
6216
|
`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
|
|
5976
6217
|
);
|
|
5977
6218
|
sepAtPos = spV;
|
|
5978
|
-
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}; `;
|
|
6219
|
+
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}; `;
|
|
5979
6220
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
5980
6221
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
6222
|
+
const post = postSepMarks();
|
|
6223
|
+
stmts.push(...post.decl);
|
|
5981
6224
|
const npV = v(ctx, "_np");
|
|
5982
6225
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
5983
6226
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
5984
6227
|
stmts.push(
|
|
5985
6228
|
...nextStmts,
|
|
5986
|
-
...failItem(nextOk, npV, rollbackToSep,
|
|
6229
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
5987
6230
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
5988
6231
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
5989
6232
|
);
|
|
@@ -5999,7 +6242,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
5999
6242
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
6000
6243
|
stmts.push(
|
|
6001
6244
|
...nextStmts,
|
|
6002
|
-
...failItem(nextOk, npV, "",
|
|
6245
|
+
...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
|
|
6003
6246
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6004
6247
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6005
6248
|
);
|
|
@@ -6015,11 +6258,13 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6015
6258
|
}
|
|
6016
6259
|
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
6017
6260
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
6018
|
-
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
6261
|
+
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}; ` : "";
|
|
6262
|
+
const post = postSepMarks();
|
|
6263
|
+
stmts.push(...post.decl);
|
|
6019
6264
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
6020
6265
|
stmts.push(
|
|
6021
6266
|
...nextStmts,
|
|
6022
|
-
...failItem(nextOk, sepEnd, nextRb,
|
|
6267
|
+
...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
6023
6268
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
6024
6269
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
6025
6270
|
);
|
|
@@ -6028,6 +6273,9 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
6028
6273
|
stmts.push(`${ind(ctx)}}`);
|
|
6029
6274
|
ctx.indent--;
|
|
6030
6275
|
stmts.push(`${ind(ctx)}}`);
|
|
6276
|
+
if (def.min >= 1) {
|
|
6277
|
+
stmts.push(...emitIfFail(ctx, `${arrV}.length < ${def.min}`, failArrBody(ctx, deriveExpectedArr([def.parser]), curV)));
|
|
6278
|
+
}
|
|
6031
6279
|
return { stmts, valueVar: arrV, endVar: curV };
|
|
6032
6280
|
}
|
|
6033
6281
|
function emitScanTo(def, ctx, pos) {
|
|
@@ -6079,17 +6327,44 @@ function emitScanTo(def, ctx, pos) {
|
|
|
6079
6327
|
return { stmts, valueVar: valV, endVar: curV };
|
|
6080
6328
|
}
|
|
6081
6329
|
function emitNot(def, ctx, pos) {
|
|
6330
|
+
const sinksLive = !!ctx.capturing || !!ctx.recovery;
|
|
6331
|
+
const leaves = sinksLive ? v(ctx, "_ntl") : null;
|
|
6332
|
+
const raw = sinksLive ? v(ctx, "_ntr") : null;
|
|
6333
|
+
const tl = sinksLive ? v(ctx, "_ntt") : null;
|
|
6334
|
+
const log = sinksLive ? v(ctx, "_ntg") : null;
|
|
6335
|
+
const fields = sinksLive ? v(ctx, "_ntf") : null;
|
|
6336
|
+
const errors = sinksLive ? v(ctx, "_nte") : null;
|
|
6082
6337
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
6083
6338
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
6084
6339
|
return {
|
|
6085
6340
|
stmts: [
|
|
6341
|
+
...sinksLive ? [
|
|
6342
|
+
`${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`
|
|
6343
|
+
] : [],
|
|
6086
6344
|
...stmts,
|
|
6345
|
+
...sinksLive ? [
|
|
6346
|
+
`${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}`
|
|
6347
|
+
] : [],
|
|
6087
6348
|
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
6088
6349
|
],
|
|
6089
6350
|
valueVar: "null",
|
|
6090
6351
|
endVar: pos
|
|
6091
6352
|
};
|
|
6092
6353
|
}
|
|
6354
|
+
function emitPeek(def, ctx, pos) {
|
|
6355
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
6356
|
+
const { stmts, okVar } = emitFallible(def.parser, probeCtx, pos, true);
|
|
6357
|
+
ctx.vars = probeCtx.vars;
|
|
6358
|
+
const label2 = JSON.stringify(`peek(${def.parser._tag})`);
|
|
6359
|
+
return {
|
|
6360
|
+
stmts: [
|
|
6361
|
+
...stmts,
|
|
6362
|
+
...emitIfFail(ctx, `!${okVar}`, failBody(ctx, label2, pos))
|
|
6363
|
+
],
|
|
6364
|
+
valueVar: "null",
|
|
6365
|
+
endVar: pos
|
|
6366
|
+
};
|
|
6367
|
+
}
|
|
6093
6368
|
function escapeTokenLiteral(value) {
|
|
6094
6369
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6095
6370
|
}
|
|
@@ -6377,9 +6652,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
6377
6652
|
return { stmts, valueVar: vv, endVar: ev };
|
|
6378
6653
|
}
|
|
6379
6654
|
function emitLazy(p, def, ctx, pos) {
|
|
6380
|
-
const
|
|
6655
|
+
const canonical2 = ctx.ruleNames?.get(p);
|
|
6381
6656
|
const usage = ctx.lazyUsage;
|
|
6382
|
-
if (!
|
|
6657
|
+
if (!canonical2 && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
6383
6658
|
let resolved;
|
|
6384
6659
|
try {
|
|
6385
6660
|
resolved = def.thunk();
|
|
@@ -6389,7 +6664,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
6389
6664
|
return emit(resolved, ctx, pos);
|
|
6390
6665
|
}
|
|
6391
6666
|
if (!ctx.namedParsers.has(p)) {
|
|
6392
|
-
const fnName2 =
|
|
6667
|
+
const fnName2 = canonical2 ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
6393
6668
|
ctx.namedParsers.set(p, fnName2);
|
|
6394
6669
|
let resolved;
|
|
6395
6670
|
try {
|
|
@@ -6672,6 +6947,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
6672
6947
|
}
|
|
6673
6948
|
case "not":
|
|
6674
6949
|
return emitNot(def, ctx, pos);
|
|
6950
|
+
case "peek":
|
|
6951
|
+
return emitPeek(def, ctx, pos);
|
|
6675
6952
|
case "node":
|
|
6676
6953
|
return emitNode(def, ctx, pos);
|
|
6677
6954
|
case "scanTo":
|
|
@@ -6760,6 +7037,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6760
7037
|
case "many":
|
|
6761
7038
|
case "oneOrMore":
|
|
6762
7039
|
case "not":
|
|
7040
|
+
case "peek":
|
|
6763
7041
|
case "transform":
|
|
6764
7042
|
return hasNodeDef(d.parser, seen);
|
|
6765
7043
|
case "skip":
|
|
@@ -6811,17 +7089,18 @@ function childrenOf(def) {
|
|
|
6811
7089
|
case "field":
|
|
6812
7090
|
case "grammar":
|
|
6813
7091
|
case "not":
|
|
7092
|
+
case "peek":
|
|
6814
7093
|
case "node":
|
|
6815
7094
|
case "withCtx":
|
|
6816
7095
|
case "expect":
|
|
6817
7096
|
return [def.parser];
|
|
6818
|
-
// emitMany's
|
|
6819
|
-
// (
|
|
6820
|
-
// emit() call
|
|
7097
|
+
// emitMany's min>=1 branch and emitSepBy both codegen `def.parser` more than
|
|
7098
|
+
// once (`min` mandatory matches, then again inside the repeat loop) — each is a
|
|
7099
|
+
// real emit() call site, so the usage analysis must see one edge per site or it
|
|
6821
7100
|
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
6822
|
-
// referenced from
|
|
7101
|
+
// referenced from several positions within this one compiled function.
|
|
6823
7102
|
case "oneOrMore":
|
|
6824
|
-
return
|
|
7103
|
+
return Array.from({ length: def.min + 1 }, () => def.parser);
|
|
6825
7104
|
case "sepBy":
|
|
6826
7105
|
return [def.parser, def.parser, def.separator];
|
|
6827
7106
|
case "skip":
|
|
@@ -6967,12 +7246,41 @@ function resolveGatingLevel(opt) {
|
|
|
6967
7246
|
return "warn";
|
|
6968
7247
|
}
|
|
6969
7248
|
function runGatingDiagnostic(combinator, opt) {
|
|
7249
|
+
return reportGating(opt, (o) => analyzeGating(combinator, o));
|
|
7250
|
+
}
|
|
7251
|
+
function runGatingDiagnosticRules(ruleMap, opt) {
|
|
7252
|
+
return reportGating(opt, (o) => analyzeGatingRules(ruleMap, o));
|
|
7253
|
+
}
|
|
7254
|
+
function runFusedGatingDiagnostic(getRuleMaps, opt) {
|
|
7255
|
+
if (resolveGatingLevel(opt) === "off") return void 0;
|
|
7256
|
+
const ruleMaps = getRuleMaps();
|
|
7257
|
+
if (!ruleMaps.some((map) => hasExternalRuleRef(map))) return void 0;
|
|
7258
|
+
const winners = /* @__PURE__ */ new Map();
|
|
7259
|
+
for (const map of ruleMaps) for (const [name, rule] of map) {
|
|
7260
|
+
if (rule._def.tag === "lazy") {
|
|
7261
|
+
try {
|
|
7262
|
+
rule._def.thunk();
|
|
7263
|
+
} catch {
|
|
7264
|
+
continue;
|
|
7265
|
+
}
|
|
7266
|
+
}
|
|
7267
|
+
winners.set(name, rule);
|
|
7268
|
+
}
|
|
7269
|
+
const entries = [...winners];
|
|
7270
|
+
return reportGating(opt, (o) => {
|
|
7271
|
+
const deferredHere = new Set(analyzeGatingRules(entries, o).deferred.map((c) => c.id));
|
|
7272
|
+
const fused = analyzeGatingRules(entries, { ...o, resolveRef: (name) => winners.get(name) });
|
|
7273
|
+
return { ...fused, ungated: fused.ungated.filter((c) => deferredHere.has(c.id)), antiPatterns: [] };
|
|
7274
|
+
});
|
|
7275
|
+
}
|
|
7276
|
+
function reportGating(opt, analyze) {
|
|
6970
7277
|
const level = resolveGatingLevel(opt);
|
|
6971
7278
|
if (level === "off") return void 0;
|
|
6972
|
-
const
|
|
7279
|
+
const obj = opt !== null && typeof opt === "object" ? opt : void 0;
|
|
7280
|
+
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;
|
|
6973
7281
|
let report;
|
|
6974
7282
|
try {
|
|
6975
|
-
report =
|
|
7283
|
+
report = analyze(analyzeOpts);
|
|
6976
7284
|
} catch {
|
|
6977
7285
|
return void 0;
|
|
6978
7286
|
}
|
|
@@ -7108,6 +7416,7 @@ function publicRuleWrapperSource(rule, fnSource) {
|
|
|
7108
7416
|
return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
|
|
7109
7417
|
}
|
|
7110
7418
|
function compileRuleMap(ruleMap, opts) {
|
|
7419
|
+
runGatingDiagnosticRules(ruleMap, opts?.gating);
|
|
7111
7420
|
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
7112
7421
|
const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
|
|
7113
7422
|
const grammarTrivia = opts?.trivia;
|
|
@@ -7226,6 +7535,7 @@ function compileRuleMap(ruleMap, opts) {
|
|
|
7226
7535
|
}
|
|
7227
7536
|
function compileLinkable(ruleMapArg, ns, opts) {
|
|
7228
7537
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
7538
|
+
if (opts?.gating !== void 0) runGatingDiagnosticRules(ruleMapArg, opts.gating);
|
|
7229
7539
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
7230
7540
|
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
7231
7541
|
const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
|
|
@@ -7282,6 +7592,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7282
7592
|
fnNameToKey.set(fn, key);
|
|
7283
7593
|
}
|
|
7284
7594
|
ctx.ruleNames = ruleNames;
|
|
7595
|
+
const externalRefs = /* @__PURE__ */ new Set();
|
|
7285
7596
|
{
|
|
7286
7597
|
const scanned = /* @__PURE__ */ new Set();
|
|
7287
7598
|
const scanExternal = (p) => {
|
|
@@ -7297,11 +7608,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7297
7608
|
}
|
|
7298
7609
|
if (resolved === void 0) {
|
|
7299
7610
|
const name = p._ruleName;
|
|
7300
|
-
if (name
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7611
|
+
if (name) {
|
|
7612
|
+
externalRefs.add(p);
|
|
7613
|
+
if (!ctx.namedParsers.has(p)) {
|
|
7614
|
+
assertRuleName(name);
|
|
7615
|
+
const fn = `_r_${name}`;
|
|
7616
|
+
ruleNames.set(p, fn);
|
|
7617
|
+
ctx.namedParsers.set(p, fn);
|
|
7618
|
+
}
|
|
7305
7619
|
}
|
|
7306
7620
|
return;
|
|
7307
7621
|
}
|
|
@@ -7414,12 +7728,32 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
7414
7728
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
7415
7729
|
needsHostReads: !!ctx.needsHostReads,
|
|
7416
7730
|
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
7417
|
-
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
|
|
7731
|
+
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
|
|
7418
7732
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
7419
7733
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
7420
7734
|
};
|
|
7421
7735
|
}
|
|
7422
|
-
function
|
|
7736
|
+
function hasExternalRuleRef(ruleMap) {
|
|
7737
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7738
|
+
const visit = (p) => {
|
|
7739
|
+
if (seen.has(p)) return false;
|
|
7740
|
+
seen.add(p);
|
|
7741
|
+
const def = p._def;
|
|
7742
|
+
if (def.tag === "lazy") {
|
|
7743
|
+
let resolved;
|
|
7744
|
+
try {
|
|
7745
|
+
resolved = def.thunk();
|
|
7746
|
+
} catch {
|
|
7747
|
+
resolved = void 0;
|
|
7748
|
+
}
|
|
7749
|
+
if (resolved === void 0) return p._ruleName !== void 0;
|
|
7750
|
+
return visit(resolved);
|
|
7751
|
+
}
|
|
7752
|
+
return childrenOf(def).some(visit);
|
|
7753
|
+
};
|
|
7754
|
+
return ruleMap.some(([, rule]) => visit(rule));
|
|
7755
|
+
}
|
|
7756
|
+
function hasSemanticReduction(roots, externalRefs) {
|
|
7423
7757
|
const seen = /* @__PURE__ */ new Set();
|
|
7424
7758
|
const visit = (parser2) => {
|
|
7425
7759
|
if (seen.has(parser2)) return false;
|
|
@@ -7430,6 +7764,7 @@ function hasSemanticReduction(roots) {
|
|
|
7430
7764
|
if (def.tag === "guard" || def.tag === "withCtx") return true;
|
|
7431
7765
|
if (def.tag === "node" && def.build !== void 0) return true;
|
|
7432
7766
|
if (def.tag === "lazy") {
|
|
7767
|
+
if (externalRefs?.has(parser2)) return false;
|
|
7433
7768
|
try {
|
|
7434
7769
|
return visit(def.thunk());
|
|
7435
7770
|
} catch {
|
|
@@ -7521,6 +7856,7 @@ function childrenOf2(def) {
|
|
|
7521
7856
|
case "label":
|
|
7522
7857
|
case "field":
|
|
7523
7858
|
case "not":
|
|
7859
|
+
case "peek":
|
|
7524
7860
|
case "node":
|
|
7525
7861
|
case "withCtx":
|
|
7526
7862
|
case "expect":
|
|
@@ -7554,6 +7890,13 @@ function lazyTarget(c) {
|
|
|
7554
7890
|
}
|
|
7555
7891
|
var Unserializable = class extends Error {
|
|
7556
7892
|
};
|
|
7893
|
+
function repeatOpts(min, max, trailing) {
|
|
7894
|
+
const parts = [];
|
|
7895
|
+
if (min !== 0) parts.push(`min: ${min}`);
|
|
7896
|
+
if (max !== void 0) parts.push(`max: ${max}`);
|
|
7897
|
+
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
7898
|
+
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
7899
|
+
}
|
|
7557
7900
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
7558
7901
|
try {
|
|
7559
7902
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -7657,6 +8000,7 @@ function evalRuleMapIR(ir) {
|
|
|
7657
8000
|
"optional",
|
|
7658
8001
|
"sepBy",
|
|
7659
8002
|
"not",
|
|
8003
|
+
"peek",
|
|
7660
8004
|
"node",
|
|
7661
8005
|
"parser",
|
|
7662
8006
|
"scanTo",
|
|
@@ -7689,6 +8033,7 @@ function evalRuleMapIR(ir) {
|
|
|
7689
8033
|
optional,
|
|
7690
8034
|
sepBy,
|
|
7691
8035
|
not,
|
|
8036
|
+
peek,
|
|
7692
8037
|
node,
|
|
7693
8038
|
parser,
|
|
7694
8039
|
scanTo,
|
|
@@ -7863,17 +8208,21 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
7863
8208
|
return `_gch([${items.join(", ")}])`;
|
|
7864
8209
|
}
|
|
7865
8210
|
case "many":
|
|
7866
|
-
return `many(${kid(def.parser)})`;
|
|
8211
|
+
return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
8212
|
+
// A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
|
|
8213
|
+
// it through `many` so the bounds survive (oneOrMore defaults min to 1).
|
|
7867
8214
|
case "oneOrMore":
|
|
7868
|
-
return `oneOrMore(${kid(def.parser)})`;
|
|
8215
|
+
return def.min === 1 && def.max === void 0 ? `oneOrMore(${kid(def.parser)})` : `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
7869
8216
|
case "optional":
|
|
7870
8217
|
return `optional(${kid(def.parser)})`;
|
|
7871
8218
|
case "attempt":
|
|
7872
8219
|
return `attempt(${kid(def.parser)})`;
|
|
7873
8220
|
case "sepBy":
|
|
7874
|
-
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
8221
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)}${repeatOpts(def.min, def.max, def.trailing)})`;
|
|
7875
8222
|
case "not":
|
|
7876
8223
|
return `not(${kid(def.parser)})`;
|
|
8224
|
+
case "peek":
|
|
8225
|
+
return `peek(${kid(def.parser)})`;
|
|
7877
8226
|
case "trivia":
|
|
7878
8227
|
return `trivia(${kid(def.parser)})`;
|
|
7879
8228
|
case "token":
|
|
@@ -8309,15 +8658,12 @@ var SUPPORTED = {
|
|
|
8309
8658
|
literal: (...a) => literal(a[0], a[1]),
|
|
8310
8659
|
regex: (...a) => regex(a[0], a[1]),
|
|
8311
8660
|
keywords: (...a) => keywords(a[0], a[1]),
|
|
8312
|
-
word: (...a) => word(a[0], a[1]),
|
|
8661
|
+
word: (...a) => word(a[0], a[1], a[2]),
|
|
8313
8662
|
sequence: (...a) => sequence(...a),
|
|
8314
8663
|
choice: (...a) => choice(...a),
|
|
8315
8664
|
attempt: (...a) => attempt(a[0]),
|
|
8316
|
-
many: (...a) => many(a[0]),
|
|
8317
|
-
oneOrMore: (...a) => oneOrMore(a[0]),
|
|
8318
8665
|
optional: (...a) => optional(a[0]),
|
|
8319
8666
|
skip: (...a) => skip(a[0], a[1]),
|
|
8320
|
-
sepBy: (...a) => sepBy(a[0], a[1]),
|
|
8321
8667
|
trivia: (...a) => trivia(a[0]),
|
|
8322
8668
|
label: (...a) => label(a[0], a[1]),
|
|
8323
8669
|
field: (...a) => field(a[0], a[1]),
|
|
@@ -8493,8 +8839,8 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
8493
8839
|
return null;
|
|
8494
8840
|
}
|
|
8495
8841
|
}
|
|
8496
|
-
if (callee.name === "sepBy") {
|
|
8497
|
-
const [itemArg, sepArg] = node2.arguments;
|
|
8842
|
+
if (callee.name === "sepBy" || callee.name === "oneOrMoreSep") {
|
|
8843
|
+
const [itemArg, sepArg, optsArg] = node2.arguments;
|
|
8498
8844
|
if (!itemArg || !sepArg || itemArg.type === "SpreadElement" || sepArg.type === "SpreadElement") return null;
|
|
8499
8845
|
const itemMfs = [];
|
|
8500
8846
|
const itemCombi = anyValue(itemArg, scope, code, itemMfs);
|
|
@@ -8502,25 +8848,42 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
8502
8848
|
const sepMfs = [];
|
|
8503
8849
|
const sepCombi = anyValue(sepArg, scope, code, sepMfs);
|
|
8504
8850
|
if (!isCombinator(sepCombi)) return null;
|
|
8851
|
+
let opts;
|
|
8852
|
+
if (optsArg) {
|
|
8853
|
+
if (optsArg.type === "SpreadElement") return null;
|
|
8854
|
+
const v2 = anyValue(optsArg, scope, code, []);
|
|
8855
|
+
if (!v2 || typeof v2 !== "object") return null;
|
|
8856
|
+
opts = v2;
|
|
8857
|
+
}
|
|
8505
8858
|
if (mfs) mfs.push(...itemMfs, ...sepMfs, ...itemMfs);
|
|
8506
8859
|
try {
|
|
8507
|
-
return sepBy(itemCombi, sepCombi);
|
|
8860
|
+
return callee.name === "sepBy" ? sepBy(itemCombi, sepCombi, opts) : oneOrMoreSep(itemCombi, sepCombi, opts);
|
|
8508
8861
|
} catch {
|
|
8509
8862
|
return null;
|
|
8510
8863
|
}
|
|
8511
8864
|
}
|
|
8512
|
-
if (callee.name === "oneOrMore") {
|
|
8513
|
-
const [itemArg] = node2.arguments;
|
|
8865
|
+
if (callee.name === "oneOrMore" || callee.name === "many") {
|
|
8866
|
+
const [itemArg, optsArg] = node2.arguments;
|
|
8514
8867
|
if (!itemArg || itemArg.type === "SpreadElement") return null;
|
|
8515
8868
|
const itemMfs = [];
|
|
8516
8869
|
const itemCombi = anyValue(itemArg, scope, code, itemMfs);
|
|
8517
8870
|
if (!isCombinator(itemCombi)) return null;
|
|
8518
|
-
|
|
8871
|
+
let opts;
|
|
8872
|
+
if (optsArg) {
|
|
8873
|
+
if (optsArg.type === "SpreadElement") return null;
|
|
8874
|
+
const v2 = anyValue(optsArg, scope, code, []);
|
|
8875
|
+
if (!v2 || typeof v2 !== "object") return null;
|
|
8876
|
+
opts = v2;
|
|
8877
|
+
}
|
|
8878
|
+
let combi;
|
|
8519
8879
|
try {
|
|
8520
|
-
|
|
8880
|
+
combi = callee.name === "many" ? many(itemCombi, opts) : oneOrMore(itemCombi, opts);
|
|
8521
8881
|
} catch {
|
|
8522
8882
|
return null;
|
|
8523
8883
|
}
|
|
8884
|
+
const min = combi._def.tag === "oneOrMore" ? combi._def.min : 0;
|
|
8885
|
+
if (mfs) for (let i = 0; i <= min; i++) mfs.push(...itemMfs);
|
|
8886
|
+
return combi;
|
|
8524
8887
|
}
|
|
8525
8888
|
if (callee.name === "not") {
|
|
8526
8889
|
const [innerArg] = node2.arguments;
|
|
@@ -8533,6 +8896,17 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
8533
8896
|
return null;
|
|
8534
8897
|
}
|
|
8535
8898
|
}
|
|
8899
|
+
if (callee.name === "peek") {
|
|
8900
|
+
const [innerArg] = node2.arguments;
|
|
8901
|
+
if (!innerArg || innerArg.type === "SpreadElement") return null;
|
|
8902
|
+
const inner = anyValue(innerArg, scope, code, mfs);
|
|
8903
|
+
if (!isCombinator(inner)) return null;
|
|
8904
|
+
try {
|
|
8905
|
+
return peek(inner);
|
|
8906
|
+
} catch {
|
|
8907
|
+
return null;
|
|
8908
|
+
}
|
|
8909
|
+
}
|
|
8536
8910
|
if (callee.name === "balanced") {
|
|
8537
8911
|
const [openArg, closeArg, optsArg] = node2.arguments;
|
|
8538
8912
|
if (!openArg || !closeArg || openArg.type === "SpreadElement" || closeArg.type === "SpreadElement") return null;
|
|
@@ -9162,6 +9536,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9162
9536
|
const warnings = [];
|
|
9163
9537
|
beginLoweringCapture();
|
|
9164
9538
|
let anyUnresolved = false;
|
|
9539
|
+
let keepMacroImport = false;
|
|
9165
9540
|
let runtimeComposeFallback = false;
|
|
9166
9541
|
let ruleMapHolderCounter = 0;
|
|
9167
9542
|
const lineOf = (pos) => {
|
|
@@ -9210,11 +9585,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9210
9585
|
const evaluated = evaluateRulesFactory(init, label2);
|
|
9211
9586
|
if (!evaluated) return null;
|
|
9212
9587
|
const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, recovery, coverage: grammarCoverage });
|
|
9213
|
-
|
|
9214
|
-
warn(init.start, `${label2}: rule map couldn't be inlined`);
|
|
9215
|
-
return null;
|
|
9216
|
-
}
|
|
9217
|
-
return { replacement: compiled.replacement, ruleMap: evaluated.ruleMap, ...compiled.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {}, ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {} };
|
|
9588
|
+
return { replacement: compiled?.replacement ?? null, ruleMap: evaluated.ruleMap, ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {}, ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {} };
|
|
9218
9589
|
};
|
|
9219
9590
|
const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
|
|
9220
9591
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
@@ -9309,6 +9680,17 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9309
9680
|
}
|
|
9310
9681
|
return out;
|
|
9311
9682
|
};
|
|
9683
|
+
const carriedRuleMaps = (items) => {
|
|
9684
|
+
const out = [];
|
|
9685
|
+
const add = (it) => {
|
|
9686
|
+
if (isIR(it)) out.push(evalRuleMapIR(it.ir));
|
|
9687
|
+
};
|
|
9688
|
+
for (const it of items) {
|
|
9689
|
+
if (isSpread(it)) for (const p of importedPieces(it.__spreadLocal) ?? []) add(p);
|
|
9690
|
+
else add(it);
|
|
9691
|
+
}
|
|
9692
|
+
return out;
|
|
9693
|
+
};
|
|
9312
9694
|
const materializeLeafCoverage = (items, localRules, localNs, composing, captureTerminals = false) => {
|
|
9313
9695
|
const sources = [];
|
|
9314
9696
|
const add = (item) => {
|
|
@@ -9442,6 +9824,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9442
9824
|
}
|
|
9443
9825
|
carried.push(...r.carried);
|
|
9444
9826
|
}
|
|
9827
|
+
runFusedGatingDiagnostic(() => carriedRuleMaps(carried));
|
|
9445
9828
|
const pieces = materializeCarried(carried, composing);
|
|
9446
9829
|
try {
|
|
9447
9830
|
return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
|
|
@@ -9490,6 +9873,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9490
9873
|
}
|
|
9491
9874
|
try {
|
|
9492
9875
|
const localNs = nsFor(`composeLeaf${init.start}`);
|
|
9876
|
+
runFusedGatingDiagnostic(() => [...carriedRuleMaps(carried), [...localRules]]);
|
|
9493
9877
|
const plainLocalPiece = compileLinkable([...localRules], localNs, { ...composing ? { trivia: composing } : {}, ...localScanSkip ? { scanSkip: localScanSkip } : {}, recovery });
|
|
9494
9878
|
if (!plainLocalPiece) {
|
|
9495
9879
|
warn(init.start, "composeLeaf(): local rules could not be statically compiled");
|
|
@@ -9578,7 +9962,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9578
9962
|
const refEntry = scope.get(varName);
|
|
9579
9963
|
const refCombi = refEntry?.combi ?? null;
|
|
9580
9964
|
if (refCombi) {
|
|
9581
|
-
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage });
|
|
9965
|
+
const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage, gating: { entryName: varName } });
|
|
9582
9966
|
if (compiled2.inlineExpression === null) {
|
|
9583
9967
|
warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
|
|
9584
9968
|
continue;
|
|
@@ -9593,20 +9977,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9593
9977
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
9594
9978
|
if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
|
|
9595
9979
|
if (compiledRules.scanSkip) localGrammarScanSkip.set(varName, compiledRules.scanSkip);
|
|
9980
|
+
const ns = nsFor(varName);
|
|
9981
|
+
const pieces = exportPrefix || compiledRules.replacement === null ? compileLinkable([...compiledRules.ruleMap], ns, {
|
|
9982
|
+
...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
|
|
9983
|
+
recovery
|
|
9984
|
+
}) : null;
|
|
9985
|
+
if (compiledRules.replacement === null) {
|
|
9986
|
+
if (!pieces || !hasExternalRuleRef([...compiledRules.ruleMap])) {
|
|
9987
|
+
warn(init.start, `${varName}: rule map couldn't be inlined`);
|
|
9988
|
+
continue;
|
|
9989
|
+
}
|
|
9990
|
+
keepMacroImport = true;
|
|
9991
|
+
}
|
|
9992
|
+
const source = compiledRules.replacement ?? code.slice(init.start, init.end);
|
|
9596
9993
|
let replacement = withCoverageDefinitions(
|
|
9597
|
-
|
|
9598
|
-
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(
|
|
9994
|
+
source,
|
|
9995
|
+
compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(source)
|
|
9599
9996
|
);
|
|
9600
|
-
if (exportPrefix) {
|
|
9601
|
-
const
|
|
9602
|
-
|
|
9603
|
-
...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
|
|
9604
|
-
recovery
|
|
9605
|
-
});
|
|
9606
|
-
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
9607
|
-
const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
|
|
9608
|
-
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
9609
|
-
}
|
|
9997
|
+
if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
9998
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
|
|
9999
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
9610
10000
|
}
|
|
9611
10001
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
9612
10002
|
continue;
|
|
@@ -9649,7 +10039,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9649
10039
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
9650
10040
|
continue;
|
|
9651
10041
|
}
|
|
9652
|
-
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage });
|
|
10042
|
+
const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage, gating: { entryName: varName } });
|
|
9653
10043
|
if (compiled.inlineExpression === null) {
|
|
9654
10044
|
warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
|
|
9655
10045
|
continue;
|
|
@@ -9668,6 +10058,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9668
10058
|
}
|
|
9669
10059
|
const compiledRules = compileRulesFactory(init, "{ \u2026 }");
|
|
9670
10060
|
if (!compiledRules) continue;
|
|
10061
|
+
if (compiledRules.replacement === null) {
|
|
10062
|
+
warn(init.start, `{ \u2026 }: rule map couldn't be inlined`);
|
|
10063
|
+
continue;
|
|
10064
|
+
}
|
|
9671
10065
|
const pattern = d.id;
|
|
9672
10066
|
const bindings = [];
|
|
9673
10067
|
let allOk = true;
|
|
@@ -9709,7 +10103,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
9709
10103
|
}
|
|
9710
10104
|
if (replacements.length === 0 && warnings.length === 0) return null;
|
|
9711
10105
|
for (const imp of macroImports) {
|
|
9712
|
-
imp.fullyResolved = !anyUnresolved;
|
|
10106
|
+
imp.fullyResolved = !anyUnresolved && !keepMacroImport;
|
|
9713
10107
|
}
|
|
9714
10108
|
const ms = new MagicString(code);
|
|
9715
10109
|
if (!anyUnresolved) {
|