parseman 0.14.1 → 0.16.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 +41 -2
- package/dist/combinators/first-set.d.ts +18 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +24 -32
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/scannable-run.d.ts +2 -0
- package/dist/compiler/scannable-run.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts +24 -0
- package/dist/compiler/value-usage.d.ts.map +1 -0
- package/dist/index.cjs +377 -77
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +377 -77
- package/dist/index.js.map +4 -4
- package/dist/plugin/index.cjs +391 -85
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts +9 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +391 -85
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -27,6 +27,63 @@ function any() {
|
|
|
27
27
|
function empty() {
|
|
28
28
|
return { kind: "empty" };
|
|
29
29
|
}
|
|
30
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
31
|
+
if (seen.has(p)) return true;
|
|
32
|
+
seen.add(p);
|
|
33
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
34
|
+
const d = p._def;
|
|
35
|
+
switch (d.tag) {
|
|
36
|
+
case "literal":
|
|
37
|
+
return d.value.length === 0;
|
|
38
|
+
case "keywords":
|
|
39
|
+
return false;
|
|
40
|
+
case "regex":
|
|
41
|
+
try {
|
|
42
|
+
const m = new RegExp(d.source).exec("");
|
|
43
|
+
return m != null && m[0] === "";
|
|
44
|
+
} catch {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
case "many":
|
|
48
|
+
case "optional":
|
|
49
|
+
case "not":
|
|
50
|
+
return true;
|
|
51
|
+
// zero repetitions / absent / lookahead
|
|
52
|
+
case "oneOrMore":
|
|
53
|
+
return me(d.parser);
|
|
54
|
+
case "sequence":
|
|
55
|
+
return d.parsers.every(me);
|
|
56
|
+
case "choice":
|
|
57
|
+
return d.parsers.some(me);
|
|
58
|
+
case "transform":
|
|
59
|
+
case "label":
|
|
60
|
+
case "trivia":
|
|
61
|
+
case "expect":
|
|
62
|
+
case "withCtx":
|
|
63
|
+
case "node":
|
|
64
|
+
case "grammar":
|
|
65
|
+
case "recover":
|
|
66
|
+
return me(d.parser);
|
|
67
|
+
case "skip":
|
|
68
|
+
return me(d.main);
|
|
69
|
+
case "lazy":
|
|
70
|
+
try {
|
|
71
|
+
return me(d.thunk());
|
|
72
|
+
} catch {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
default:
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function sequenceFirstSet(parsers) {
|
|
80
|
+
let fs = empty();
|
|
81
|
+
for (const p of parsers) {
|
|
82
|
+
fs = union(fs, p._meta.firstSet);
|
|
83
|
+
if (!matchesEmpty(p)) return fs;
|
|
84
|
+
}
|
|
85
|
+
return fs;
|
|
86
|
+
}
|
|
30
87
|
function mergeRanges(ranges) {
|
|
31
88
|
if (ranges.length === 0) return [];
|
|
32
89
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -204,12 +261,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
204
261
|
}
|
|
205
262
|
|
|
206
263
|
// src/combinators/literal.ts
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
264
|
+
function asciiFoldEq(a, b) {
|
|
265
|
+
if (a.length !== b.length) return false;
|
|
266
|
+
for (let i = 0; i < a.length; i++) {
|
|
267
|
+
let ca = a.charCodeAt(i);
|
|
268
|
+
let cb = b.charCodeAt(i);
|
|
269
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
270
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
271
|
+
if (ca !== cb) return false;
|
|
211
272
|
}
|
|
212
|
-
return
|
|
273
|
+
return true;
|
|
213
274
|
}
|
|
214
275
|
function literal(value, opts = {}) {
|
|
215
276
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -224,8 +285,9 @@ function literal(value, opts = {}) {
|
|
|
224
285
|
const lower = value.toLowerCase();
|
|
225
286
|
const firstUpper = upper.codePointAt(0);
|
|
226
287
|
const firstLower = lower.codePointAt(0);
|
|
227
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
228
|
-
{ lo:
|
|
288
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
289
|
+
{ lo: firstLower, hi: firstLower },
|
|
290
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
229
291
|
] } : firstSet;
|
|
230
292
|
}
|
|
231
293
|
return {
|
|
@@ -238,7 +300,7 @@ function literal(value, opts = {}) {
|
|
|
238
300
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
239
301
|
}
|
|
240
302
|
const slice = input.slice(pos, end);
|
|
241
|
-
const matched = caseInsensitive ?
|
|
303
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
242
304
|
if (matched) {
|
|
243
305
|
const span = { start: pos, end };
|
|
244
306
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -848,16 +910,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
848
910
|
// src/combinators/sequence.ts
|
|
849
911
|
function sequence(...parsers) {
|
|
850
912
|
const meta = {
|
|
851
|
-
|
|
913
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
914
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
915
|
+
firstSet: sequenceFirstSet(parsers),
|
|
852
916
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
853
917
|
isTrivia: false
|
|
854
918
|
};
|
|
919
|
+
const def = { tag: "sequence", parsers };
|
|
855
920
|
return {
|
|
856
921
|
_tag: "sequence",
|
|
857
922
|
_meta: meta,
|
|
858
|
-
_def:
|
|
923
|
+
_def: def,
|
|
859
924
|
parse(input, pos, ctx) {
|
|
860
|
-
const values = [];
|
|
925
|
+
const values = def.valueUnused ? void 0 : [];
|
|
861
926
|
let cur = pos;
|
|
862
927
|
for (let i = 0; i < parsers.length; i++) {
|
|
863
928
|
if (ctx.trivia && i > 0) {
|
|
@@ -877,17 +942,18 @@ function sequence(...parsers) {
|
|
|
877
942
|
} else {
|
|
878
943
|
rollbackTrivia(ctx, mark);
|
|
879
944
|
}
|
|
880
|
-
values.push(result2.value);
|
|
945
|
+
if (values !== void 0) values.push(result2.value);
|
|
881
946
|
continue;
|
|
882
947
|
}
|
|
883
948
|
const result = parsers[i].parse(input, cur, ctx);
|
|
884
949
|
if (!result.ok) return result;
|
|
885
|
-
values.push(result.value);
|
|
950
|
+
if (values !== void 0) values.push(result.value);
|
|
886
951
|
cur = result.span.end;
|
|
887
952
|
}
|
|
888
953
|
return {
|
|
889
954
|
ok: true,
|
|
890
|
-
|
|
955
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
956
|
+
value: values ?? void 0,
|
|
891
957
|
span: { start: pos, end: cur }
|
|
892
958
|
};
|
|
893
959
|
}
|
|
@@ -928,20 +994,21 @@ function many(combinator) {
|
|
|
928
994
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
929
995
|
isTrivia: false
|
|
930
996
|
};
|
|
997
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
931
998
|
return {
|
|
932
999
|
_tag: "many",
|
|
933
1000
|
_meta: meta,
|
|
934
|
-
_def:
|
|
1001
|
+
_def: def,
|
|
935
1002
|
parse(input, pos, ctx) {
|
|
936
|
-
const values = [];
|
|
1003
|
+
const values = def.valueUnused ? void 0 : [];
|
|
937
1004
|
let cur = pos;
|
|
938
1005
|
while (cur < input.length) {
|
|
939
1006
|
const item = repItem(combinator, input, cur, ctx);
|
|
940
1007
|
if (item === "stop" || "fail" in item) break;
|
|
941
|
-
values.push(item.value);
|
|
1008
|
+
if (values !== void 0) values.push(item.value);
|
|
942
1009
|
cur = item.end;
|
|
943
1010
|
}
|
|
944
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1011
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
945
1012
|
}
|
|
946
1013
|
};
|
|
947
1014
|
}
|
|
@@ -951,22 +1018,23 @@ function oneOrMore(combinator) {
|
|
|
951
1018
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
952
1019
|
isTrivia: false
|
|
953
1020
|
};
|
|
1021
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
954
1022
|
return {
|
|
955
1023
|
_tag: "oneOrMore",
|
|
956
1024
|
_meta: meta,
|
|
957
|
-
_def:
|
|
1025
|
+
_def: def,
|
|
958
1026
|
parse(input, pos, ctx) {
|
|
959
1027
|
const first = combinator.parse(input, pos, ctx);
|
|
960
1028
|
if (!first.ok) return first;
|
|
961
|
-
const values = [first.value];
|
|
1029
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
962
1030
|
let cur = first.span.end;
|
|
963
1031
|
while (cur < input.length) {
|
|
964
1032
|
const item = repItem(combinator, input, cur, ctx);
|
|
965
1033
|
if (item === "stop" || "fail" in item) break;
|
|
966
|
-
values.push(item.value);
|
|
1034
|
+
if (values !== void 0) values.push(item.value);
|
|
967
1035
|
cur = item.end;
|
|
968
1036
|
}
|
|
969
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1037
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
970
1038
|
}
|
|
971
1039
|
};
|
|
972
1040
|
}
|
|
@@ -1074,6 +1142,90 @@ function ref() {
|
|
|
1074
1142
|
return slot;
|
|
1075
1143
|
}
|
|
1076
1144
|
|
|
1145
|
+
// src/compiler/value-usage.ts
|
|
1146
|
+
function markUnusedValues(root) {
|
|
1147
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1148
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1149
|
+
const mark = (def, consumed) => {
|
|
1150
|
+
if (consumed) def.valueUnused = false;
|
|
1151
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1152
|
+
};
|
|
1153
|
+
const visit = (c, consumed) => {
|
|
1154
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1155
|
+
if (seen.has(c)) return;
|
|
1156
|
+
seen.add(c);
|
|
1157
|
+
const def = c._def;
|
|
1158
|
+
switch (def.tag) {
|
|
1159
|
+
case "many":
|
|
1160
|
+
case "oneOrMore":
|
|
1161
|
+
mark(def, consumed);
|
|
1162
|
+
visit(def.parser, consumed);
|
|
1163
|
+
return;
|
|
1164
|
+
case "optional":
|
|
1165
|
+
visit(def.parser, consumed);
|
|
1166
|
+
return;
|
|
1167
|
+
case "sequence":
|
|
1168
|
+
mark(def, consumed);
|
|
1169
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1170
|
+
return;
|
|
1171
|
+
case "choice":
|
|
1172
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1173
|
+
return;
|
|
1174
|
+
case "node":
|
|
1175
|
+
visit(def.parser, false);
|
|
1176
|
+
return;
|
|
1177
|
+
case "transform":
|
|
1178
|
+
visit(def.parser, true);
|
|
1179
|
+
return;
|
|
1180
|
+
case "skip":
|
|
1181
|
+
visit(def.main, consumed);
|
|
1182
|
+
visit(def.skipped, false);
|
|
1183
|
+
return;
|
|
1184
|
+
case "sepBy":
|
|
1185
|
+
visit(def.parser, true);
|
|
1186
|
+
visit(def.separator, false);
|
|
1187
|
+
return;
|
|
1188
|
+
case "not":
|
|
1189
|
+
visit(def.parser, false);
|
|
1190
|
+
return;
|
|
1191
|
+
case "grammar":
|
|
1192
|
+
visit(def.parser, consumed);
|
|
1193
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1194
|
+
return;
|
|
1195
|
+
case "recover":
|
|
1196
|
+
visit(def.parser, consumed);
|
|
1197
|
+
visit(def.sentinel, false);
|
|
1198
|
+
return;
|
|
1199
|
+
case "label":
|
|
1200
|
+
case "trivia":
|
|
1201
|
+
case "withCtx":
|
|
1202
|
+
case "expect":
|
|
1203
|
+
visit(def.parser, consumed);
|
|
1204
|
+
return;
|
|
1205
|
+
case "lazy":
|
|
1206
|
+
return;
|
|
1207
|
+
default:
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
const resolveRoot = (c) => {
|
|
1212
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1213
|
+
let cur = c;
|
|
1214
|
+
while (!guard2.has(cur)) {
|
|
1215
|
+
const d = cur._def;
|
|
1216
|
+
if (d.tag !== "lazy") return cur;
|
|
1217
|
+
guard2.add(cur);
|
|
1218
|
+
try {
|
|
1219
|
+
cur = d.thunk();
|
|
1220
|
+
} catch {
|
|
1221
|
+
return c;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return cur;
|
|
1225
|
+
};
|
|
1226
|
+
visit(resolveRoot(root), true);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1077
1229
|
// src/combinators/parser.ts
|
|
1078
1230
|
function rules(factory) {
|
|
1079
1231
|
const cache = {};
|
|
@@ -1103,6 +1255,9 @@ function rules(factory) {
|
|
|
1103
1255
|
cache[key] = parser2;
|
|
1104
1256
|
}
|
|
1105
1257
|
}
|
|
1258
|
+
for (const key of Object.keys(definitions)) {
|
|
1259
|
+
markUnusedValues(cache[key]);
|
|
1260
|
+
}
|
|
1106
1261
|
return cache;
|
|
1107
1262
|
}
|
|
1108
1263
|
|
|
@@ -1319,7 +1474,12 @@ function parser(opts, root) {
|
|
|
1319
1474
|
function noTrivia(root) {
|
|
1320
1475
|
return parser({ trivia: null }, root);
|
|
1321
1476
|
}
|
|
1477
|
+
var _analyzed = /* @__PURE__ */ new WeakSet();
|
|
1322
1478
|
function parse(combinator, input, opts = {}) {
|
|
1479
|
+
if (!_analyzed.has(combinator)) {
|
|
1480
|
+
_analyzed.add(combinator);
|
|
1481
|
+
markUnusedValues(combinator);
|
|
1482
|
+
}
|
|
1323
1483
|
const trackLines = opts.trackLines ?? false;
|
|
1324
1484
|
const _errors = opts.recover ? [] : void 0;
|
|
1325
1485
|
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
@@ -1474,8 +1634,8 @@ function classToRanges(cls) {
|
|
|
1474
1634
|
return parseClassRanges(body);
|
|
1475
1635
|
}
|
|
1476
1636
|
var foldEq = (cVar, cp) => {
|
|
1477
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1478
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1637
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1638
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1479
1639
|
return `${cVar} === ${cp}`;
|
|
1480
1640
|
};
|
|
1481
1641
|
function literalCodePoints(frag) {
|
|
@@ -1726,6 +1886,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1726
1886
|
return classDisjoint(a, b);
|
|
1727
1887
|
}
|
|
1728
1888
|
function fixedClassSeq(shape) {
|
|
1889
|
+
if (shape.kind === "litFold") {
|
|
1890
|
+
return shape.open.map((cp) => {
|
|
1891
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1892
|
+
return other === cp ? { ranges: [[cp, cp]], negated: false } : { ranges: [[Math.min(cp, other), Math.min(cp, other)], [Math.max(cp, other), Math.max(cp, other)]], negated: false };
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1729
1895
|
if (shape.kind !== "seq") return null;
|
|
1730
1896
|
const out = [];
|
|
1731
1897
|
for (const p of shape.parts) {
|
|
@@ -2147,12 +2313,34 @@ function parseScanShapeCore(source) {
|
|
|
2147
2313
|
}
|
|
2148
2314
|
function scanShapeFromRegex(source, flags) {
|
|
2149
2315
|
if (/[msu]/.test(flags)) return null;
|
|
2150
|
-
if (/i/.test(flags))
|
|
2151
|
-
const open = literalCodePoints(source);
|
|
2152
|
-
return open ? { kind: "litFold", open } : null;
|
|
2153
|
-
}
|
|
2316
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2154
2317
|
return parseScanShape(source);
|
|
2155
2318
|
}
|
|
2319
|
+
function caseFoldShape(source) {
|
|
2320
|
+
const la = stripTrailingLookahead(source);
|
|
2321
|
+
if (la) {
|
|
2322
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2323
|
+
if (!inner) return null;
|
|
2324
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2325
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2326
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2327
|
+
}
|
|
2328
|
+
return caseFoldLiteralOrAlt(source);
|
|
2329
|
+
}
|
|
2330
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2331
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2332
|
+
const open = literalCodePoints(source);
|
|
2333
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2334
|
+
const arms = splitTopLevelAlternation(source);
|
|
2335
|
+
if (!arms || arms.length < 2) return null;
|
|
2336
|
+
const litArms = [];
|
|
2337
|
+
for (const arm of arms) {
|
|
2338
|
+
const cp = literalCodePoints(arm);
|
|
2339
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2340
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2341
|
+
}
|
|
2342
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2343
|
+
}
|
|
2156
2344
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2157
2345
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2158
2346
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2164,6 +2352,25 @@ function stringHardStop(shape) {
|
|
|
2164
2352
|
);
|
|
2165
2353
|
}
|
|
2166
2354
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2355
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2356
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2357
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2358
|
+
function planAltSwitch(firsts) {
|
|
2359
|
+
const cases = [];
|
|
2360
|
+
let total = 0;
|
|
2361
|
+
for (const fs of firsts) {
|
|
2362
|
+
if (!fs || fs.negated) return { kind: "if" };
|
|
2363
|
+
const pts = [];
|
|
2364
|
+
for (const [lo, hi] of fs.ranges) {
|
|
2365
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2366
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2367
|
+
}
|
|
2368
|
+
total += pts.length;
|
|
2369
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2370
|
+
cases.push(pts);
|
|
2371
|
+
}
|
|
2372
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2373
|
+
}
|
|
2167
2374
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2168
2375
|
if (shape.kind === "lookahead") {
|
|
2169
2376
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2188,18 +2395,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2188
2395
|
const okV = mint("_ok");
|
|
2189
2396
|
const endV2 = mint("_end");
|
|
2190
2397
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2398
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2399
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2400
|
+
for (const a of shape.arms) {
|
|
2401
|
+
const open = a.open;
|
|
2402
|
+
const f = open[0];
|
|
2403
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2404
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2405
|
+
}
|
|
2406
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2407
|
+
for (const [lower, arms] of groups) {
|
|
2408
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2409
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2410
|
+
for (const open of arms) {
|
|
2411
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2412
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2413
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2414
|
+
}
|
|
2415
|
+
lines.push(`${ind2} break`);
|
|
2416
|
+
lines.push(`${ind2} }`);
|
|
2417
|
+
}
|
|
2418
|
+
lines.push(`${ind2}}`);
|
|
2419
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2420
|
+
}
|
|
2191
2421
|
if (shape.disjoint) {
|
|
2192
|
-
const
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2422
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2423
|
+
if (plan.kind === "switch") {
|
|
2424
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2425
|
+
shape.arms.forEach((arm, k) => {
|
|
2426
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2427
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2428
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2429
|
+
lines.push(...m.setup);
|
|
2430
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2431
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2432
|
+
lines.push(`${ind2} break`);
|
|
2433
|
+
lines.push(`${ind2} }`);
|
|
2434
|
+
});
|
|
2201
2435
|
lines.push(`${ind2}}`);
|
|
2202
|
-
}
|
|
2436
|
+
} else {
|
|
2437
|
+
const c0 = codeAt(start, firstChar);
|
|
2438
|
+
shape.arms.forEach((arm, k) => {
|
|
2439
|
+
const fs = shape.firsts[k];
|
|
2440
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2441
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2442
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2443
|
+
lines.push(...m.setup);
|
|
2444
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2445
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2446
|
+
lines.push(`${ind2}}`);
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2203
2449
|
} else {
|
|
2204
2450
|
const lbl = mint("_alt");
|
|
2205
2451
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2687,6 +2933,8 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2687
2933
|
}
|
|
2688
2934
|
|
|
2689
2935
|
// src/compiler/codegen.ts
|
|
2936
|
+
var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) return false; let s; try { s = Function.prototype.toString.call(b) } catch (e) { return true } if (/\\barguments\\b/.test(s)) return true; const m = /^[^(]*\\(([\\s\\S]*?)\\)/.exec(s); if (m && /\\.\\.\\.|=/.test(m[1])) return true; return b.length > n }";
|
|
2937
|
+
var _loweringSink = null;
|
|
2690
2938
|
function v(ctx, prefix = "_v") {
|
|
2691
2939
|
return `${prefix}${ctx.vars++}`;
|
|
2692
2940
|
}
|
|
@@ -3085,13 +3333,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3085
3333
|
const vv = v(ctx);
|
|
3086
3334
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3087
3335
|
const stmts = [];
|
|
3088
|
-
if (caseInsensitive) {
|
|
3089
|
-
|
|
3336
|
+
if (caseInsensitive && len > 0) {
|
|
3337
|
+
const match = Array.from(
|
|
3338
|
+
{ length: len },
|
|
3339
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3340
|
+
).join(" && ");
|
|
3090
3341
|
stmts.push(
|
|
3091
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3092
|
-
`${ind(ctx)}const ${vv}
|
|
3093
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3094
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3342
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3343
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3095
3344
|
);
|
|
3096
3345
|
} else if (len === 0) {
|
|
3097
3346
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3212,6 +3461,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3212
3461
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3213
3462
|
}
|
|
3214
3463
|
}
|
|
3464
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3215
3465
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3216
3466
|
const key = `${def.source}/${flags}`;
|
|
3217
3467
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3276,6 +3526,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3276
3526
|
}
|
|
3277
3527
|
function emitSeq(def, ctx, pos) {
|
|
3278
3528
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3529
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3279
3530
|
const arrV = v(ctx, "_arr");
|
|
3280
3531
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3281
3532
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3482,7 +3733,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3482
3733
|
const autoNot = def.autoNot[i];
|
|
3483
3734
|
const atStart = failsAtStart(p);
|
|
3484
3735
|
const staticFx = armStaticExpected(ctx, p);
|
|
3485
|
-
const
|
|
3736
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3737
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3486
3738
|
let gateCond = null;
|
|
3487
3739
|
if (gate) {
|
|
3488
3740
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3573,19 +3825,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3573
3825
|
return checks.join(" && ");
|
|
3574
3826
|
}
|
|
3575
3827
|
function emitMany(def, ctx, pos) {
|
|
3828
|
+
const wantValue = !def.valueUnused;
|
|
3576
3829
|
const arrV = v(ctx, "_arr");
|
|
3577
3830
|
const curV = v(ctx, "_cur");
|
|
3578
3831
|
const stmts = [
|
|
3579
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3832
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3580
3833
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3581
3834
|
];
|
|
3582
3835
|
if (def.min === 1) {
|
|
3583
3836
|
const firstR = emit(def.parser, ctx, curV);
|
|
3584
3837
|
stmts.push(...firstR.stmts);
|
|
3585
|
-
stmts.push(
|
|
3586
|
-
|
|
3587
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3588
|
-
);
|
|
3838
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3839
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3589
3840
|
}
|
|
3590
3841
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3591
3842
|
ctx.indent++;
|
|
@@ -3616,13 +3867,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3616
3867
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3617
3868
|
stmts.push(
|
|
3618
3869
|
...iterStmts,
|
|
3619
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3620
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3621
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3870
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3622
3871
|
);
|
|
3872
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3873
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3623
3874
|
ctx.indent--;
|
|
3624
3875
|
stmts.push(`${ind(ctx)}}`);
|
|
3625
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3876
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3626
3877
|
}
|
|
3627
3878
|
function emitOptional(def, ctx, pos) {
|
|
3628
3879
|
const valV = v(ctx, "_opt");
|
|
@@ -3791,15 +4042,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3791
4042
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3792
4043
|
}
|
|
3793
4044
|
const i = ind(ctx);
|
|
3794
|
-
const capturesTrivia = mkType !== null || structural
|
|
3795
|
-
const clonesState = structural
|
|
4045
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4046
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3796
4047
|
const chV = v(ctx, "_ch");
|
|
3797
4048
|
const rawV = v(ctx, "_raw");
|
|
3798
|
-
const
|
|
4049
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4050
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4051
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3799
4052
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3800
4053
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3801
|
-
|
|
3802
|
-
const
|
|
4054
|
+
if (structural) ctx.needsHostReads = true;
|
|
4055
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4056
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4057
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3803
4058
|
const stmts = [
|
|
3804
4059
|
allocStmt,
|
|
3805
4060
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3810,7 +4065,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3810
4065
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3811
4066
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3812
4067
|
let stV = "undefined";
|
|
3813
|
-
if (
|
|
4068
|
+
if (structural) {
|
|
4069
|
+
stV = v(ctx, "_nst");
|
|
4070
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4071
|
+
} else if (clonesState) {
|
|
3814
4072
|
stV = v(ctx, "_nst");
|
|
3815
4073
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3816
4074
|
}
|
|
@@ -4232,6 +4490,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4232
4490
|
return deps;
|
|
4233
4491
|
}
|
|
4234
4492
|
function compile(combinator, mapFnSources) {
|
|
4493
|
+
markUnusedValues(combinator);
|
|
4235
4494
|
const ctx = {
|
|
4236
4495
|
vars: 0,
|
|
4237
4496
|
indent: 1,
|
|
@@ -4245,7 +4504,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4245
4504
|
buildFns: [],
|
|
4246
4505
|
buildSrcs: [],
|
|
4247
4506
|
runtimeParsers: [],
|
|
4248
|
-
needsCollator: false,
|
|
4249
4507
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4250
4508
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4251
4509
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4254,18 +4512,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4254
4512
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4255
4513
|
};
|
|
4256
4514
|
const r = emit(combinator, ctx, "_pos");
|
|
4257
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4258
|
-
` : "";
|
|
4259
4515
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4260
4516
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4517
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4261
4518
|
const source = [
|
|
4262
4519
|
...emptyTlDecls,
|
|
4520
|
+
...hostReadsDecls,
|
|
4263
4521
|
...ctx.regexDecls,
|
|
4264
4522
|
...ctx.expectedDecls,
|
|
4265
4523
|
"",
|
|
4266
4524
|
...namedPrelude,
|
|
4267
4525
|
ctx.namedFnDecls.join("\n\n"),
|
|
4268
|
-
|
|
4526
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4269
4527
|
` let pos = _pos`,
|
|
4270
4528
|
...r.stmts,
|
|
4271
4529
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4273,9 +4531,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4273
4531
|
].join("\n");
|
|
4274
4532
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4275
4533
|
...emptyTlDecls,
|
|
4534
|
+
...hostReadsDecls,
|
|
4276
4535
|
...ctx.regexDecls,
|
|
4277
4536
|
...ctx.expectedDecls,
|
|
4278
|
-
collatorDecl,
|
|
4279
4537
|
...namedPrelude,
|
|
4280
4538
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4281
4539
|
`let pos = _pos`,
|
|
@@ -4292,7 +4550,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4292
4550
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4293
4551
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4294
4552
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4295
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4553
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4296
4554
|
return {
|
|
4297
4555
|
source,
|
|
4298
4556
|
inlineExpression,
|
|
@@ -4314,8 +4572,17 @@ function compile(combinator, mapFnSources) {
|
|
|
4314
4572
|
}
|
|
4315
4573
|
};
|
|
4316
4574
|
}
|
|
4575
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4576
|
+
function assertRuleName(name) {
|
|
4577
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4578
|
+
throw new Error(
|
|
4579
|
+
`parseman: grammar rule name ${JSON.stringify(name)} is not a valid JS identifier. Rule names compile to _r_<Name> functions and dispatch guards \u2014 use letters, digits, _ or $ (and don't start with a digit).`
|
|
4580
|
+
);
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4317
4583
|
function compileLinkable(ruleMapArg, ns) {
|
|
4318
4584
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4585
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4319
4586
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4320
4587
|
const d = val._def;
|
|
4321
4588
|
if (d.tag !== "lazy") return true;
|
|
@@ -4339,20 +4606,21 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4339
4606
|
buildFns: [],
|
|
4340
4607
|
buildSrcs: [],
|
|
4341
4608
|
runtimeParsers: [],
|
|
4342
|
-
needsCollator: false,
|
|
4343
4609
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4344
4610
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4345
4611
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4346
4612
|
namedFnDecls: [],
|
|
4347
4613
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4348
4614
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4349
|
-
ns
|
|
4615
|
+
ns,
|
|
4616
|
+
deferFirstSetRefs: true
|
|
4350
4617
|
};
|
|
4351
4618
|
const seen = /* @__PURE__ */ new Map();
|
|
4352
4619
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4353
4620
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4354
4621
|
for (const [key, rule] of ruleMap) {
|
|
4355
|
-
|
|
4622
|
+
assertRuleName(key);
|
|
4623
|
+
const base = `_r_${key}`;
|
|
4356
4624
|
const n = seen.get(base) ?? 0;
|
|
4357
4625
|
seen.set(base, n + 1);
|
|
4358
4626
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4376,7 +4644,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4376
4644
|
if (resolved === void 0) {
|
|
4377
4645
|
const name = p._ruleName;
|
|
4378
4646
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4379
|
-
|
|
4647
|
+
assertRuleName(name);
|
|
4648
|
+
const fn = `_r_${name}`;
|
|
4380
4649
|
ruleNames.set(p, fn);
|
|
4381
4650
|
ctx.namedParsers.set(p, fn);
|
|
4382
4651
|
}
|
|
@@ -4445,20 +4714,34 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4445
4714
|
`}`
|
|
4446
4715
|
].join("\n"));
|
|
4447
4716
|
}
|
|
4717
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4718
|
+
for (const [key, rule] of ruleMap) {
|
|
4719
|
+
let resolved = rule;
|
|
4720
|
+
const d = rule._def;
|
|
4721
|
+
if (d.tag === "lazy") {
|
|
4722
|
+
try {
|
|
4723
|
+
resolved = d.thunk();
|
|
4724
|
+
} catch {
|
|
4725
|
+
resolved = rule;
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4729
|
+
}
|
|
4448
4730
|
return {
|
|
4449
4731
|
ns,
|
|
4450
4732
|
keys: perEntry.map((e) => e.key),
|
|
4451
4733
|
prelude,
|
|
4452
4734
|
ruleFns,
|
|
4453
4735
|
wrappers,
|
|
4736
|
+
firstSets,
|
|
4454
4737
|
deps: ruleDependencies(ruleMap),
|
|
4455
4738
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4456
|
-
|
|
4739
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4457
4740
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4458
4741
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4459
4742
|
};
|
|
4460
4743
|
}
|
|
4461
|
-
function buildInlineExpression(ctx, r,
|
|
4744
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4462
4745
|
const bodyLines = [
|
|
4463
4746
|
` let pos = _pos`,
|
|
4464
4747
|
...r.stmts,
|
|
@@ -4472,14 +4755,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4472
4755
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4473
4756
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4474
4757
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4475
|
-
const
|
|
4758
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4759
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4476
4760
|
if (!needsWrapper) return innerFn;
|
|
4477
4761
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4478
4762
|
const hoistedDecls = [
|
|
4479
4763
|
emptyTlDecl,
|
|
4764
|
+
hostReadsDecl,
|
|
4480
4765
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4481
4766
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4482
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4483
4767
|
mfDecl,
|
|
4484
4768
|
buildDecl,
|
|
4485
4769
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4538,20 +4822,36 @@ function fusedBody(pieces) {
|
|
|
4538
4822
|
}
|
|
4539
4823
|
const contributing = new Set(winner.values());
|
|
4540
4824
|
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4541
|
-
const
|
|
4825
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4542
4826
|
const lines = [
|
|
4543
4827
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4544
4828
|
"const _pfFail = {}",
|
|
4545
4829
|
"let _pfEnd",
|
|
4546
4830
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4547
|
-
...
|
|
4831
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4548
4832
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4549
4833
|
...[...contributing].flatMap((p) => p.prelude),
|
|
4550
4834
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4551
4835
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4552
4836
|
];
|
|
4553
4837
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4554
|
-
const
|
|
4838
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4839
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4840
|
+
for (const [k, p] of winner) {
|
|
4841
|
+
const fs = p.firstSets?.get(k);
|
|
4842
|
+
if (fs) finalFS.set(k, fs);
|
|
4843
|
+
}
|
|
4844
|
+
const body = rawBody.replace(
|
|
4845
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4846
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4847
|
+
// matches every well-formed placeholder.
|
|
4848
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4849
|
+
(_m, name, codevar) => {
|
|
4850
|
+
const fs = finalFS.get(name);
|
|
4851
|
+
if (!fs || fs.kind === "any" || fs.kind === "empty") return "true";
|
|
4852
|
+
return `(${firstSetCond(codevar, fs)})`;
|
|
4853
|
+
}
|
|
4854
|
+
);
|
|
4555
4855
|
const env = {};
|
|
4556
4856
|
for (const p of contributing) {
|
|
4557
4857
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|