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.cjs
CHANGED
|
@@ -124,6 +124,63 @@ function any() {
|
|
|
124
124
|
function empty() {
|
|
125
125
|
return { kind: "empty" };
|
|
126
126
|
}
|
|
127
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
128
|
+
if (seen.has(p)) return true;
|
|
129
|
+
seen.add(p);
|
|
130
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
131
|
+
const d = p._def;
|
|
132
|
+
switch (d.tag) {
|
|
133
|
+
case "literal":
|
|
134
|
+
return d.value.length === 0;
|
|
135
|
+
case "keywords":
|
|
136
|
+
return false;
|
|
137
|
+
case "regex":
|
|
138
|
+
try {
|
|
139
|
+
const m = new RegExp(d.source).exec("");
|
|
140
|
+
return m != null && m[0] === "";
|
|
141
|
+
} catch {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
case "many":
|
|
145
|
+
case "optional":
|
|
146
|
+
case "not":
|
|
147
|
+
return true;
|
|
148
|
+
// zero repetitions / absent / lookahead
|
|
149
|
+
case "oneOrMore":
|
|
150
|
+
return me(d.parser);
|
|
151
|
+
case "sequence":
|
|
152
|
+
return d.parsers.every(me);
|
|
153
|
+
case "choice":
|
|
154
|
+
return d.parsers.some(me);
|
|
155
|
+
case "transform":
|
|
156
|
+
case "label":
|
|
157
|
+
case "trivia":
|
|
158
|
+
case "expect":
|
|
159
|
+
case "withCtx":
|
|
160
|
+
case "node":
|
|
161
|
+
case "grammar":
|
|
162
|
+
case "recover":
|
|
163
|
+
return me(d.parser);
|
|
164
|
+
case "skip":
|
|
165
|
+
return me(d.main);
|
|
166
|
+
case "lazy":
|
|
167
|
+
try {
|
|
168
|
+
return me(d.thunk());
|
|
169
|
+
} catch {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function sequenceFirstSet(parsers) {
|
|
177
|
+
let fs = empty();
|
|
178
|
+
for (const p of parsers) {
|
|
179
|
+
fs = union(fs, p._meta.firstSet);
|
|
180
|
+
if (!matchesEmpty(p)) return fs;
|
|
181
|
+
}
|
|
182
|
+
return fs;
|
|
183
|
+
}
|
|
127
184
|
function mergeRanges(ranges) {
|
|
128
185
|
if (ranges.length === 0) return [];
|
|
129
186
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -301,12 +358,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
301
358
|
}
|
|
302
359
|
|
|
303
360
|
// src/combinators/literal.ts
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
361
|
+
function asciiFoldEq(a, b) {
|
|
362
|
+
if (a.length !== b.length) return false;
|
|
363
|
+
for (let i = 0; i < a.length; i++) {
|
|
364
|
+
let ca = a.charCodeAt(i);
|
|
365
|
+
let cb = b.charCodeAt(i);
|
|
366
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
367
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
368
|
+
if (ca !== cb) return false;
|
|
308
369
|
}
|
|
309
|
-
return
|
|
370
|
+
return true;
|
|
310
371
|
}
|
|
311
372
|
function literal(value, opts = {}) {
|
|
312
373
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -321,8 +382,9 @@ function literal(value, opts = {}) {
|
|
|
321
382
|
const lower = value.toLowerCase();
|
|
322
383
|
const firstUpper = upper.codePointAt(0);
|
|
323
384
|
const firstLower = lower.codePointAt(0);
|
|
324
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
325
|
-
{ lo:
|
|
385
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
386
|
+
{ lo: firstLower, hi: firstLower },
|
|
387
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
326
388
|
] } : firstSet;
|
|
327
389
|
}
|
|
328
390
|
return {
|
|
@@ -335,7 +397,7 @@ function literal(value, opts = {}) {
|
|
|
335
397
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
336
398
|
}
|
|
337
399
|
const slice = input.slice(pos, end);
|
|
338
|
-
const matched = caseInsensitive ?
|
|
400
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
339
401
|
if (matched) {
|
|
340
402
|
const span = { start: pos, end };
|
|
341
403
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -945,16 +1007,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
945
1007
|
// src/combinators/sequence.ts
|
|
946
1008
|
function sequence(...parsers) {
|
|
947
1009
|
const meta = {
|
|
948
|
-
|
|
1010
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
1011
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
1012
|
+
firstSet: sequenceFirstSet(parsers),
|
|
949
1013
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
950
1014
|
isTrivia: false
|
|
951
1015
|
};
|
|
1016
|
+
const def = { tag: "sequence", parsers };
|
|
952
1017
|
return {
|
|
953
1018
|
_tag: "sequence",
|
|
954
1019
|
_meta: meta,
|
|
955
|
-
_def:
|
|
1020
|
+
_def: def,
|
|
956
1021
|
parse(input, pos, ctx) {
|
|
957
|
-
const values = [];
|
|
1022
|
+
const values = def.valueUnused ? void 0 : [];
|
|
958
1023
|
let cur = pos;
|
|
959
1024
|
for (let i = 0; i < parsers.length; i++) {
|
|
960
1025
|
if (ctx.trivia && i > 0) {
|
|
@@ -974,17 +1039,18 @@ function sequence(...parsers) {
|
|
|
974
1039
|
} else {
|
|
975
1040
|
rollbackTrivia(ctx, mark);
|
|
976
1041
|
}
|
|
977
|
-
values.push(result2.value);
|
|
1042
|
+
if (values !== void 0) values.push(result2.value);
|
|
978
1043
|
continue;
|
|
979
1044
|
}
|
|
980
1045
|
const result = parsers[i].parse(input, cur, ctx);
|
|
981
1046
|
if (!result.ok) return result;
|
|
982
|
-
values.push(result.value);
|
|
1047
|
+
if (values !== void 0) values.push(result.value);
|
|
983
1048
|
cur = result.span.end;
|
|
984
1049
|
}
|
|
985
1050
|
return {
|
|
986
1051
|
ok: true,
|
|
987
|
-
|
|
1052
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
1053
|
+
value: values ?? void 0,
|
|
988
1054
|
span: { start: pos, end: cur }
|
|
989
1055
|
};
|
|
990
1056
|
}
|
|
@@ -1025,20 +1091,21 @@ function many(combinator) {
|
|
|
1025
1091
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1026
1092
|
isTrivia: false
|
|
1027
1093
|
};
|
|
1094
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
1028
1095
|
return {
|
|
1029
1096
|
_tag: "many",
|
|
1030
1097
|
_meta: meta,
|
|
1031
|
-
_def:
|
|
1098
|
+
_def: def,
|
|
1032
1099
|
parse(input, pos, ctx) {
|
|
1033
|
-
const values = [];
|
|
1100
|
+
const values = def.valueUnused ? void 0 : [];
|
|
1034
1101
|
let cur = pos;
|
|
1035
1102
|
while (cur < input.length) {
|
|
1036
1103
|
const item = repItem(combinator, input, cur, ctx);
|
|
1037
1104
|
if (item === "stop" || "fail" in item) break;
|
|
1038
|
-
values.push(item.value);
|
|
1105
|
+
if (values !== void 0) values.push(item.value);
|
|
1039
1106
|
cur = item.end;
|
|
1040
1107
|
}
|
|
1041
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1108
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1042
1109
|
}
|
|
1043
1110
|
};
|
|
1044
1111
|
}
|
|
@@ -1048,22 +1115,23 @@ function oneOrMore(combinator) {
|
|
|
1048
1115
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1049
1116
|
isTrivia: false
|
|
1050
1117
|
};
|
|
1118
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
1051
1119
|
return {
|
|
1052
1120
|
_tag: "oneOrMore",
|
|
1053
1121
|
_meta: meta,
|
|
1054
|
-
_def:
|
|
1122
|
+
_def: def,
|
|
1055
1123
|
parse(input, pos, ctx) {
|
|
1056
1124
|
const first = combinator.parse(input, pos, ctx);
|
|
1057
1125
|
if (!first.ok) return first;
|
|
1058
|
-
const values = [first.value];
|
|
1126
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
1059
1127
|
let cur = first.span.end;
|
|
1060
1128
|
while (cur < input.length) {
|
|
1061
1129
|
const item = repItem(combinator, input, cur, ctx);
|
|
1062
1130
|
if (item === "stop" || "fail" in item) break;
|
|
1063
|
-
values.push(item.value);
|
|
1131
|
+
if (values !== void 0) values.push(item.value);
|
|
1064
1132
|
cur = item.end;
|
|
1065
1133
|
}
|
|
1066
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1134
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1067
1135
|
}
|
|
1068
1136
|
};
|
|
1069
1137
|
}
|
|
@@ -1171,6 +1239,90 @@ function ref() {
|
|
|
1171
1239
|
return slot;
|
|
1172
1240
|
}
|
|
1173
1241
|
|
|
1242
|
+
// src/compiler/value-usage.ts
|
|
1243
|
+
function markUnusedValues(root) {
|
|
1244
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1245
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1246
|
+
const mark = (def, consumed) => {
|
|
1247
|
+
if (consumed) def.valueUnused = false;
|
|
1248
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1249
|
+
};
|
|
1250
|
+
const visit = (c, consumed) => {
|
|
1251
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1252
|
+
if (seen.has(c)) return;
|
|
1253
|
+
seen.add(c);
|
|
1254
|
+
const def = c._def;
|
|
1255
|
+
switch (def.tag) {
|
|
1256
|
+
case "many":
|
|
1257
|
+
case "oneOrMore":
|
|
1258
|
+
mark(def, consumed);
|
|
1259
|
+
visit(def.parser, consumed);
|
|
1260
|
+
return;
|
|
1261
|
+
case "optional":
|
|
1262
|
+
visit(def.parser, consumed);
|
|
1263
|
+
return;
|
|
1264
|
+
case "sequence":
|
|
1265
|
+
mark(def, consumed);
|
|
1266
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1267
|
+
return;
|
|
1268
|
+
case "choice":
|
|
1269
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1270
|
+
return;
|
|
1271
|
+
case "node":
|
|
1272
|
+
visit(def.parser, false);
|
|
1273
|
+
return;
|
|
1274
|
+
case "transform":
|
|
1275
|
+
visit(def.parser, true);
|
|
1276
|
+
return;
|
|
1277
|
+
case "skip":
|
|
1278
|
+
visit(def.main, consumed);
|
|
1279
|
+
visit(def.skipped, false);
|
|
1280
|
+
return;
|
|
1281
|
+
case "sepBy":
|
|
1282
|
+
visit(def.parser, true);
|
|
1283
|
+
visit(def.separator, false);
|
|
1284
|
+
return;
|
|
1285
|
+
case "not":
|
|
1286
|
+
visit(def.parser, false);
|
|
1287
|
+
return;
|
|
1288
|
+
case "grammar":
|
|
1289
|
+
visit(def.parser, consumed);
|
|
1290
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1291
|
+
return;
|
|
1292
|
+
case "recover":
|
|
1293
|
+
visit(def.parser, consumed);
|
|
1294
|
+
visit(def.sentinel, false);
|
|
1295
|
+
return;
|
|
1296
|
+
case "label":
|
|
1297
|
+
case "trivia":
|
|
1298
|
+
case "withCtx":
|
|
1299
|
+
case "expect":
|
|
1300
|
+
visit(def.parser, consumed);
|
|
1301
|
+
return;
|
|
1302
|
+
case "lazy":
|
|
1303
|
+
return;
|
|
1304
|
+
default:
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
const resolveRoot = (c) => {
|
|
1309
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1310
|
+
let cur = c;
|
|
1311
|
+
while (!guard2.has(cur)) {
|
|
1312
|
+
const d = cur._def;
|
|
1313
|
+
if (d.tag !== "lazy") return cur;
|
|
1314
|
+
guard2.add(cur);
|
|
1315
|
+
try {
|
|
1316
|
+
cur = d.thunk();
|
|
1317
|
+
} catch {
|
|
1318
|
+
return c;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return cur;
|
|
1322
|
+
};
|
|
1323
|
+
visit(resolveRoot(root), true);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1174
1326
|
// src/combinators/parser.ts
|
|
1175
1327
|
function rules(factory) {
|
|
1176
1328
|
const cache = {};
|
|
@@ -1200,6 +1352,9 @@ function rules(factory) {
|
|
|
1200
1352
|
cache[key] = parser2;
|
|
1201
1353
|
}
|
|
1202
1354
|
}
|
|
1355
|
+
for (const key of Object.keys(definitions)) {
|
|
1356
|
+
markUnusedValues(cache[key]);
|
|
1357
|
+
}
|
|
1203
1358
|
return cache;
|
|
1204
1359
|
}
|
|
1205
1360
|
|
|
@@ -1416,7 +1571,12 @@ function parser(opts, root) {
|
|
|
1416
1571
|
function noTrivia(root) {
|
|
1417
1572
|
return parser({ trivia: null }, root);
|
|
1418
1573
|
}
|
|
1574
|
+
var _analyzed = /* @__PURE__ */ new WeakSet();
|
|
1419
1575
|
function parse(combinator, input, opts = {}) {
|
|
1576
|
+
if (!_analyzed.has(combinator)) {
|
|
1577
|
+
_analyzed.add(combinator);
|
|
1578
|
+
markUnusedValues(combinator);
|
|
1579
|
+
}
|
|
1420
1580
|
const trackLines = opts.trackLines ?? false;
|
|
1421
1581
|
const _errors = opts.recover ? [] : void 0;
|
|
1422
1582
|
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
@@ -1571,8 +1731,8 @@ function classToRanges(cls) {
|
|
|
1571
1731
|
return parseClassRanges(body);
|
|
1572
1732
|
}
|
|
1573
1733
|
var foldEq = (cVar, cp) => {
|
|
1574
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1575
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1734
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1735
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1576
1736
|
return `${cVar} === ${cp}`;
|
|
1577
1737
|
};
|
|
1578
1738
|
function literalCodePoints(frag) {
|
|
@@ -1823,6 +1983,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1823
1983
|
return classDisjoint(a, b);
|
|
1824
1984
|
}
|
|
1825
1985
|
function fixedClassSeq(shape) {
|
|
1986
|
+
if (shape.kind === "litFold") {
|
|
1987
|
+
return shape.open.map((cp) => {
|
|
1988
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1989
|
+
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 };
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1826
1992
|
if (shape.kind !== "seq") return null;
|
|
1827
1993
|
const out = [];
|
|
1828
1994
|
for (const p of shape.parts) {
|
|
@@ -2244,12 +2410,34 @@ function parseScanShapeCore(source) {
|
|
|
2244
2410
|
}
|
|
2245
2411
|
function scanShapeFromRegex(source, flags) {
|
|
2246
2412
|
if (/[msu]/.test(flags)) return null;
|
|
2247
|
-
if (/i/.test(flags))
|
|
2248
|
-
const open = literalCodePoints(source);
|
|
2249
|
-
return open ? { kind: "litFold", open } : null;
|
|
2250
|
-
}
|
|
2413
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2251
2414
|
return parseScanShape(source);
|
|
2252
2415
|
}
|
|
2416
|
+
function caseFoldShape(source) {
|
|
2417
|
+
const la = stripTrailingLookahead(source);
|
|
2418
|
+
if (la) {
|
|
2419
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2420
|
+
if (!inner) return null;
|
|
2421
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2422
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2423
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2424
|
+
}
|
|
2425
|
+
return caseFoldLiteralOrAlt(source);
|
|
2426
|
+
}
|
|
2427
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2428
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2429
|
+
const open = literalCodePoints(source);
|
|
2430
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2431
|
+
const arms = splitTopLevelAlternation(source);
|
|
2432
|
+
if (!arms || arms.length < 2) return null;
|
|
2433
|
+
const litArms = [];
|
|
2434
|
+
for (const arm of arms) {
|
|
2435
|
+
const cp = literalCodePoints(arm);
|
|
2436
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2437
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2438
|
+
}
|
|
2439
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2440
|
+
}
|
|
2253
2441
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2254
2442
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2255
2443
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2261,6 +2449,25 @@ function stringHardStop(shape) {
|
|
|
2261
2449
|
);
|
|
2262
2450
|
}
|
|
2263
2451
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2452
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2453
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2454
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2455
|
+
function planAltSwitch(firsts) {
|
|
2456
|
+
const cases = [];
|
|
2457
|
+
let total = 0;
|
|
2458
|
+
for (const fs of firsts) {
|
|
2459
|
+
if (!fs || fs.negated) return { kind: "if" };
|
|
2460
|
+
const pts = [];
|
|
2461
|
+
for (const [lo, hi] of fs.ranges) {
|
|
2462
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2463
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2464
|
+
}
|
|
2465
|
+
total += pts.length;
|
|
2466
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2467
|
+
cases.push(pts);
|
|
2468
|
+
}
|
|
2469
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2470
|
+
}
|
|
2264
2471
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2265
2472
|
if (shape.kind === "lookahead") {
|
|
2266
2473
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2285,18 +2492,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2285
2492
|
const okV = mint("_ok");
|
|
2286
2493
|
const endV2 = mint("_end");
|
|
2287
2494
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2495
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2496
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2497
|
+
for (const a of shape.arms) {
|
|
2498
|
+
const open = a.open;
|
|
2499
|
+
const f = open[0];
|
|
2500
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2501
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2502
|
+
}
|
|
2503
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2504
|
+
for (const [lower, arms] of groups) {
|
|
2505
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2506
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2507
|
+
for (const open of arms) {
|
|
2508
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2509
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2510
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2511
|
+
}
|
|
2512
|
+
lines.push(`${ind2} break`);
|
|
2513
|
+
lines.push(`${ind2} }`);
|
|
2514
|
+
}
|
|
2515
|
+
lines.push(`${ind2}}`);
|
|
2516
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2517
|
+
}
|
|
2288
2518
|
if (shape.disjoint) {
|
|
2289
|
-
const
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2519
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2520
|
+
if (plan.kind === "switch") {
|
|
2521
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2522
|
+
shape.arms.forEach((arm, k) => {
|
|
2523
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2524
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2525
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2526
|
+
lines.push(...m.setup);
|
|
2527
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2528
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2529
|
+
lines.push(`${ind2} break`);
|
|
2530
|
+
lines.push(`${ind2} }`);
|
|
2531
|
+
});
|
|
2298
2532
|
lines.push(`${ind2}}`);
|
|
2299
|
-
}
|
|
2533
|
+
} else {
|
|
2534
|
+
const c0 = codeAt(start, firstChar);
|
|
2535
|
+
shape.arms.forEach((arm, k) => {
|
|
2536
|
+
const fs = shape.firsts[k];
|
|
2537
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2538
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2539
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2540
|
+
lines.push(...m.setup);
|
|
2541
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2542
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2543
|
+
lines.push(`${ind2}}`);
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2300
2546
|
} else {
|
|
2301
2547
|
const lbl = mint("_alt");
|
|
2302
2548
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2784,6 +3030,8 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2784
3030
|
}
|
|
2785
3031
|
|
|
2786
3032
|
// src/compiler/codegen.ts
|
|
3033
|
+
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 }";
|
|
3034
|
+
var _loweringSink = null;
|
|
2787
3035
|
function v(ctx, prefix = "_v") {
|
|
2788
3036
|
return `${prefix}${ctx.vars++}`;
|
|
2789
3037
|
}
|
|
@@ -3182,13 +3430,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3182
3430
|
const vv = v(ctx);
|
|
3183
3431
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3184
3432
|
const stmts = [];
|
|
3185
|
-
if (caseInsensitive) {
|
|
3186
|
-
|
|
3433
|
+
if (caseInsensitive && len > 0) {
|
|
3434
|
+
const match = Array.from(
|
|
3435
|
+
{ length: len },
|
|
3436
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3437
|
+
).join(" && ");
|
|
3187
3438
|
stmts.push(
|
|
3188
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3189
|
-
`${ind(ctx)}const ${vv}
|
|
3190
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3191
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3439
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3440
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3192
3441
|
);
|
|
3193
3442
|
} else if (len === 0) {
|
|
3194
3443
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3309,6 +3558,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3309
3558
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3310
3559
|
}
|
|
3311
3560
|
}
|
|
3561
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3312
3562
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3313
3563
|
const key = `${def.source}/${flags}`;
|
|
3314
3564
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3373,6 +3623,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3373
3623
|
}
|
|
3374
3624
|
function emitSeq(def, ctx, pos) {
|
|
3375
3625
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3626
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3376
3627
|
const arrV = v(ctx, "_arr");
|
|
3377
3628
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3378
3629
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3579,7 +3830,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3579
3830
|
const autoNot = def.autoNot[i];
|
|
3580
3831
|
const atStart = failsAtStart(p);
|
|
3581
3832
|
const staticFx = armStaticExpected(ctx, p);
|
|
3582
|
-
const
|
|
3833
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3834
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3583
3835
|
let gateCond = null;
|
|
3584
3836
|
if (gate) {
|
|
3585
3837
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3670,19 +3922,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3670
3922
|
return checks.join(" && ");
|
|
3671
3923
|
}
|
|
3672
3924
|
function emitMany(def, ctx, pos) {
|
|
3925
|
+
const wantValue = !def.valueUnused;
|
|
3673
3926
|
const arrV = v(ctx, "_arr");
|
|
3674
3927
|
const curV = v(ctx, "_cur");
|
|
3675
3928
|
const stmts = [
|
|
3676
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3929
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3677
3930
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3678
3931
|
];
|
|
3679
3932
|
if (def.min === 1) {
|
|
3680
3933
|
const firstR = emit(def.parser, ctx, curV);
|
|
3681
3934
|
stmts.push(...firstR.stmts);
|
|
3682
|
-
stmts.push(
|
|
3683
|
-
|
|
3684
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3685
|
-
);
|
|
3935
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3936
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3686
3937
|
}
|
|
3687
3938
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3688
3939
|
ctx.indent++;
|
|
@@ -3713,13 +3964,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3713
3964
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3714
3965
|
stmts.push(
|
|
3715
3966
|
...iterStmts,
|
|
3716
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3717
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3718
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3967
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3719
3968
|
);
|
|
3969
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3970
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3720
3971
|
ctx.indent--;
|
|
3721
3972
|
stmts.push(`${ind(ctx)}}`);
|
|
3722
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3973
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3723
3974
|
}
|
|
3724
3975
|
function emitOptional(def, ctx, pos) {
|
|
3725
3976
|
const valV = v(ctx, "_opt");
|
|
@@ -3888,15 +4139,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3888
4139
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3889
4140
|
}
|
|
3890
4141
|
const i = ind(ctx);
|
|
3891
|
-
const capturesTrivia = mkType !== null || structural
|
|
3892
|
-
const clonesState = structural
|
|
4142
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4143
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3893
4144
|
const chV = v(ctx, "_ch");
|
|
3894
4145
|
const rawV = v(ctx, "_raw");
|
|
3895
|
-
const
|
|
4146
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4147
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4148
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3896
4149
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3897
4150
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3898
|
-
|
|
3899
|
-
const
|
|
4151
|
+
if (structural) ctx.needsHostReads = true;
|
|
4152
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4153
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4154
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3900
4155
|
const stmts = [
|
|
3901
4156
|
allocStmt,
|
|
3902
4157
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3907,7 +4162,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3907
4162
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3908
4163
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3909
4164
|
let stV = "undefined";
|
|
3910
|
-
if (
|
|
4165
|
+
if (structural) {
|
|
4166
|
+
stV = v(ctx, "_nst");
|
|
4167
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4168
|
+
} else if (clonesState) {
|
|
3911
4169
|
stV = v(ctx, "_nst");
|
|
3912
4170
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3913
4171
|
}
|
|
@@ -4329,6 +4587,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4329
4587
|
return deps;
|
|
4330
4588
|
}
|
|
4331
4589
|
function compile(combinator, mapFnSources) {
|
|
4590
|
+
markUnusedValues(combinator);
|
|
4332
4591
|
const ctx = {
|
|
4333
4592
|
vars: 0,
|
|
4334
4593
|
indent: 1,
|
|
@@ -4342,7 +4601,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4342
4601
|
buildFns: [],
|
|
4343
4602
|
buildSrcs: [],
|
|
4344
4603
|
runtimeParsers: [],
|
|
4345
|
-
needsCollator: false,
|
|
4346
4604
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4347
4605
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4348
4606
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4351,18 +4609,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4351
4609
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4352
4610
|
};
|
|
4353
4611
|
const r = emit(combinator, ctx, "_pos");
|
|
4354
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4355
|
-
` : "";
|
|
4356
4612
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4357
4613
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4614
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4358
4615
|
const source = [
|
|
4359
4616
|
...emptyTlDecls,
|
|
4617
|
+
...hostReadsDecls,
|
|
4360
4618
|
...ctx.regexDecls,
|
|
4361
4619
|
...ctx.expectedDecls,
|
|
4362
4620
|
"",
|
|
4363
4621
|
...namedPrelude,
|
|
4364
4622
|
ctx.namedFnDecls.join("\n\n"),
|
|
4365
|
-
|
|
4623
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4366
4624
|
` let pos = _pos`,
|
|
4367
4625
|
...r.stmts,
|
|
4368
4626
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4370,9 +4628,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4370
4628
|
].join("\n");
|
|
4371
4629
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4372
4630
|
...emptyTlDecls,
|
|
4631
|
+
...hostReadsDecls,
|
|
4373
4632
|
...ctx.regexDecls,
|
|
4374
4633
|
...ctx.expectedDecls,
|
|
4375
|
-
collatorDecl,
|
|
4376
4634
|
...namedPrelude,
|
|
4377
4635
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4378
4636
|
`let pos = _pos`,
|
|
@@ -4389,7 +4647,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4389
4647
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4390
4648
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4391
4649
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4392
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4650
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4393
4651
|
return {
|
|
4394
4652
|
source,
|
|
4395
4653
|
inlineExpression,
|
|
@@ -4411,8 +4669,17 @@ function compile(combinator, mapFnSources) {
|
|
|
4411
4669
|
}
|
|
4412
4670
|
};
|
|
4413
4671
|
}
|
|
4672
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4673
|
+
function assertRuleName(name) {
|
|
4674
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4675
|
+
throw new Error(
|
|
4676
|
+
`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).`
|
|
4677
|
+
);
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4414
4680
|
function compileLinkable(ruleMapArg, ns) {
|
|
4415
4681
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4682
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4416
4683
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4417
4684
|
const d = val._def;
|
|
4418
4685
|
if (d.tag !== "lazy") return true;
|
|
@@ -4436,20 +4703,21 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4436
4703
|
buildFns: [],
|
|
4437
4704
|
buildSrcs: [],
|
|
4438
4705
|
runtimeParsers: [],
|
|
4439
|
-
needsCollator: false,
|
|
4440
4706
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4441
4707
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4442
4708
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4443
4709
|
namedFnDecls: [],
|
|
4444
4710
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4445
4711
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4446
|
-
ns
|
|
4712
|
+
ns,
|
|
4713
|
+
deferFirstSetRefs: true
|
|
4447
4714
|
};
|
|
4448
4715
|
const seen = /* @__PURE__ */ new Map();
|
|
4449
4716
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4450
4717
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4451
4718
|
for (const [key, rule] of ruleMap) {
|
|
4452
|
-
|
|
4719
|
+
assertRuleName(key);
|
|
4720
|
+
const base = `_r_${key}`;
|
|
4453
4721
|
const n = seen.get(base) ?? 0;
|
|
4454
4722
|
seen.set(base, n + 1);
|
|
4455
4723
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4473,7 +4741,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4473
4741
|
if (resolved === void 0) {
|
|
4474
4742
|
const name = p._ruleName;
|
|
4475
4743
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4476
|
-
|
|
4744
|
+
assertRuleName(name);
|
|
4745
|
+
const fn = `_r_${name}`;
|
|
4477
4746
|
ruleNames.set(p, fn);
|
|
4478
4747
|
ctx.namedParsers.set(p, fn);
|
|
4479
4748
|
}
|
|
@@ -4542,20 +4811,34 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4542
4811
|
`}`
|
|
4543
4812
|
].join("\n"));
|
|
4544
4813
|
}
|
|
4814
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4815
|
+
for (const [key, rule] of ruleMap) {
|
|
4816
|
+
let resolved = rule;
|
|
4817
|
+
const d = rule._def;
|
|
4818
|
+
if (d.tag === "lazy") {
|
|
4819
|
+
try {
|
|
4820
|
+
resolved = d.thunk();
|
|
4821
|
+
} catch {
|
|
4822
|
+
resolved = rule;
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4826
|
+
}
|
|
4545
4827
|
return {
|
|
4546
4828
|
ns,
|
|
4547
4829
|
keys: perEntry.map((e) => e.key),
|
|
4548
4830
|
prelude,
|
|
4549
4831
|
ruleFns,
|
|
4550
4832
|
wrappers,
|
|
4833
|
+
firstSets,
|
|
4551
4834
|
deps: ruleDependencies(ruleMap),
|
|
4552
4835
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4553
|
-
|
|
4836
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4554
4837
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4555
4838
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4556
4839
|
};
|
|
4557
4840
|
}
|
|
4558
|
-
function buildInlineExpression(ctx, r,
|
|
4841
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4559
4842
|
const bodyLines = [
|
|
4560
4843
|
` let pos = _pos`,
|
|
4561
4844
|
...r.stmts,
|
|
@@ -4569,14 +4852,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4569
4852
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4570
4853
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4571
4854
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4572
|
-
const
|
|
4855
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4856
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4573
4857
|
if (!needsWrapper) return innerFn;
|
|
4574
4858
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4575
4859
|
const hoistedDecls = [
|
|
4576
4860
|
emptyTlDecl,
|
|
4861
|
+
hostReadsDecl,
|
|
4577
4862
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4578
4863
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4579
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4580
4864
|
mfDecl,
|
|
4581
4865
|
buildDecl,
|
|
4582
4866
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4635,20 +4919,36 @@ function fusedBody(pieces) {
|
|
|
4635
4919
|
}
|
|
4636
4920
|
const contributing = new Set(winner.values());
|
|
4637
4921
|
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4638
|
-
const
|
|
4922
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4639
4923
|
const lines = [
|
|
4640
4924
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4641
4925
|
"const _pfFail = {}",
|
|
4642
4926
|
"let _pfEnd",
|
|
4643
4927
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4644
|
-
...
|
|
4928
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4645
4929
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4646
4930
|
...[...contributing].flatMap((p) => p.prelude),
|
|
4647
4931
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4648
4932
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4649
4933
|
];
|
|
4650
4934
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4651
|
-
const
|
|
4935
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4936
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4937
|
+
for (const [k, p] of winner) {
|
|
4938
|
+
const fs = p.firstSets?.get(k);
|
|
4939
|
+
if (fs) finalFS.set(k, fs);
|
|
4940
|
+
}
|
|
4941
|
+
const body = rawBody.replace(
|
|
4942
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4943
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4944
|
+
// matches every well-formed placeholder.
|
|
4945
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4946
|
+
(_m, name, codevar) => {
|
|
4947
|
+
const fs = finalFS.get(name);
|
|
4948
|
+
if (!fs || fs.kind === "any" || fs.kind === "empty") return "true";
|
|
4949
|
+
return `(${firstSetCond(codevar, fs)})`;
|
|
4950
|
+
}
|
|
4951
|
+
);
|
|
4652
4952
|
const env = {};
|
|
4653
4953
|
for (const p of contributing) {
|
|
4654
4954
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|