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/plugin/index.js
CHANGED
|
@@ -34,6 +34,63 @@ function any() {
|
|
|
34
34
|
function empty() {
|
|
35
35
|
return { kind: "empty" };
|
|
36
36
|
}
|
|
37
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
38
|
+
if (seen.has(p)) return true;
|
|
39
|
+
seen.add(p);
|
|
40
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
41
|
+
const d = p._def;
|
|
42
|
+
switch (d.tag) {
|
|
43
|
+
case "literal":
|
|
44
|
+
return d.value.length === 0;
|
|
45
|
+
case "keywords":
|
|
46
|
+
return false;
|
|
47
|
+
case "regex":
|
|
48
|
+
try {
|
|
49
|
+
const m = new RegExp(d.source).exec("");
|
|
50
|
+
return m != null && m[0] === "";
|
|
51
|
+
} catch {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
case "many":
|
|
55
|
+
case "optional":
|
|
56
|
+
case "not":
|
|
57
|
+
return true;
|
|
58
|
+
// zero repetitions / absent / lookahead
|
|
59
|
+
case "oneOrMore":
|
|
60
|
+
return me(d.parser);
|
|
61
|
+
case "sequence":
|
|
62
|
+
return d.parsers.every(me);
|
|
63
|
+
case "choice":
|
|
64
|
+
return d.parsers.some(me);
|
|
65
|
+
case "transform":
|
|
66
|
+
case "label":
|
|
67
|
+
case "trivia":
|
|
68
|
+
case "expect":
|
|
69
|
+
case "withCtx":
|
|
70
|
+
case "node":
|
|
71
|
+
case "grammar":
|
|
72
|
+
case "recover":
|
|
73
|
+
return me(d.parser);
|
|
74
|
+
case "skip":
|
|
75
|
+
return me(d.main);
|
|
76
|
+
case "lazy":
|
|
77
|
+
try {
|
|
78
|
+
return me(d.thunk());
|
|
79
|
+
} catch {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function sequenceFirstSet(parsers) {
|
|
87
|
+
let fs2 = empty();
|
|
88
|
+
for (const p of parsers) {
|
|
89
|
+
fs2 = union(fs2, p._meta.firstSet);
|
|
90
|
+
if (!matchesEmpty(p)) return fs2;
|
|
91
|
+
}
|
|
92
|
+
return fs2;
|
|
93
|
+
}
|
|
37
94
|
function mergeRanges(ranges) {
|
|
38
95
|
if (ranges.length === 0) return [];
|
|
39
96
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -241,12 +298,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
241
298
|
}
|
|
242
299
|
|
|
243
300
|
// src/combinators/literal.ts
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
301
|
+
function asciiFoldEq(a, b) {
|
|
302
|
+
if (a.length !== b.length) return false;
|
|
303
|
+
for (let i = 0; i < a.length; i++) {
|
|
304
|
+
let ca = a.charCodeAt(i);
|
|
305
|
+
let cb = b.charCodeAt(i);
|
|
306
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
307
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
308
|
+
if (ca !== cb) return false;
|
|
248
309
|
}
|
|
249
|
-
return
|
|
310
|
+
return true;
|
|
250
311
|
}
|
|
251
312
|
function literal(value, opts = {}) {
|
|
252
313
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -261,8 +322,9 @@ function literal(value, opts = {}) {
|
|
|
261
322
|
const lower = value.toLowerCase();
|
|
262
323
|
const firstUpper = upper.codePointAt(0);
|
|
263
324
|
const firstLower = lower.codePointAt(0);
|
|
264
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
265
|
-
{ lo:
|
|
325
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
326
|
+
{ lo: firstLower, hi: firstLower },
|
|
327
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
266
328
|
] } : firstSet;
|
|
267
329
|
}
|
|
268
330
|
return {
|
|
@@ -275,7 +337,7 @@ function literal(value, opts = {}) {
|
|
|
275
337
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
276
338
|
}
|
|
277
339
|
const slice = input.slice(pos, end);
|
|
278
|
-
const matched = caseInsensitive ?
|
|
340
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
279
341
|
if (matched) {
|
|
280
342
|
const span = { start: pos, end };
|
|
281
343
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -882,16 +944,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
882
944
|
// src/combinators/sequence.ts
|
|
883
945
|
function sequence(...parsers) {
|
|
884
946
|
const meta = {
|
|
885
|
-
|
|
947
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
948
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
949
|
+
firstSet: sequenceFirstSet(parsers),
|
|
886
950
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
887
951
|
isTrivia: false
|
|
888
952
|
};
|
|
953
|
+
const def = { tag: "sequence", parsers };
|
|
889
954
|
return {
|
|
890
955
|
_tag: "sequence",
|
|
891
956
|
_meta: meta,
|
|
892
|
-
_def:
|
|
957
|
+
_def: def,
|
|
893
958
|
parse(input, pos, ctx) {
|
|
894
|
-
const values = [];
|
|
959
|
+
const values = def.valueUnused ? void 0 : [];
|
|
895
960
|
let cur = pos;
|
|
896
961
|
for (let i = 0; i < parsers.length; i++) {
|
|
897
962
|
if (ctx.trivia && i > 0) {
|
|
@@ -911,17 +976,18 @@ function sequence(...parsers) {
|
|
|
911
976
|
} else {
|
|
912
977
|
rollbackTrivia(ctx, mark);
|
|
913
978
|
}
|
|
914
|
-
values.push(result2.value);
|
|
979
|
+
if (values !== void 0) values.push(result2.value);
|
|
915
980
|
continue;
|
|
916
981
|
}
|
|
917
982
|
const result = parsers[i].parse(input, cur, ctx);
|
|
918
983
|
if (!result.ok) return result;
|
|
919
|
-
values.push(result.value);
|
|
984
|
+
if (values !== void 0) values.push(result.value);
|
|
920
985
|
cur = result.span.end;
|
|
921
986
|
}
|
|
922
987
|
return {
|
|
923
988
|
ok: true,
|
|
924
|
-
|
|
989
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
990
|
+
value: values ?? void 0,
|
|
925
991
|
span: { start: pos, end: cur }
|
|
926
992
|
};
|
|
927
993
|
}
|
|
@@ -962,20 +1028,21 @@ function many(combinator) {
|
|
|
962
1028
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
963
1029
|
isTrivia: false
|
|
964
1030
|
};
|
|
1031
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
965
1032
|
return {
|
|
966
1033
|
_tag: "many",
|
|
967
1034
|
_meta: meta,
|
|
968
|
-
_def:
|
|
1035
|
+
_def: def,
|
|
969
1036
|
parse(input, pos, ctx) {
|
|
970
|
-
const values = [];
|
|
1037
|
+
const values = def.valueUnused ? void 0 : [];
|
|
971
1038
|
let cur = pos;
|
|
972
1039
|
while (cur < input.length) {
|
|
973
1040
|
const item = repItem(combinator, input, cur, ctx);
|
|
974
1041
|
if (item === "stop" || "fail" in item) break;
|
|
975
|
-
values.push(item.value);
|
|
1042
|
+
if (values !== void 0) values.push(item.value);
|
|
976
1043
|
cur = item.end;
|
|
977
1044
|
}
|
|
978
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1045
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
979
1046
|
}
|
|
980
1047
|
};
|
|
981
1048
|
}
|
|
@@ -985,22 +1052,23 @@ function oneOrMore(combinator) {
|
|
|
985
1052
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
986
1053
|
isTrivia: false
|
|
987
1054
|
};
|
|
1055
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
988
1056
|
return {
|
|
989
1057
|
_tag: "oneOrMore",
|
|
990
1058
|
_meta: meta,
|
|
991
|
-
_def:
|
|
1059
|
+
_def: def,
|
|
992
1060
|
parse(input, pos, ctx) {
|
|
993
1061
|
const first = combinator.parse(input, pos, ctx);
|
|
994
1062
|
if (!first.ok) return first;
|
|
995
|
-
const values = [first.value];
|
|
1063
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
996
1064
|
let cur = first.span.end;
|
|
997
1065
|
while (cur < input.length) {
|
|
998
1066
|
const item = repItem(combinator, input, cur, ctx);
|
|
999
1067
|
if (item === "stop" || "fail" in item) break;
|
|
1000
|
-
values.push(item.value);
|
|
1068
|
+
if (values !== void 0) values.push(item.value);
|
|
1001
1069
|
cur = item.end;
|
|
1002
1070
|
}
|
|
1003
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1071
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1004
1072
|
}
|
|
1005
1073
|
};
|
|
1006
1074
|
}
|
|
@@ -1078,6 +1146,90 @@ function sepBy(combinator, separator) {
|
|
|
1078
1146
|
};
|
|
1079
1147
|
}
|
|
1080
1148
|
|
|
1149
|
+
// src/compiler/value-usage.ts
|
|
1150
|
+
function markUnusedValues(root) {
|
|
1151
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1152
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1153
|
+
const mark = (def, consumed) => {
|
|
1154
|
+
if (consumed) def.valueUnused = false;
|
|
1155
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1156
|
+
};
|
|
1157
|
+
const visit = (c, consumed) => {
|
|
1158
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1159
|
+
if (seen.has(c)) return;
|
|
1160
|
+
seen.add(c);
|
|
1161
|
+
const def = c._def;
|
|
1162
|
+
switch (def.tag) {
|
|
1163
|
+
case "many":
|
|
1164
|
+
case "oneOrMore":
|
|
1165
|
+
mark(def, consumed);
|
|
1166
|
+
visit(def.parser, consumed);
|
|
1167
|
+
return;
|
|
1168
|
+
case "optional":
|
|
1169
|
+
visit(def.parser, consumed);
|
|
1170
|
+
return;
|
|
1171
|
+
case "sequence":
|
|
1172
|
+
mark(def, consumed);
|
|
1173
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1174
|
+
return;
|
|
1175
|
+
case "choice":
|
|
1176
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1177
|
+
return;
|
|
1178
|
+
case "node":
|
|
1179
|
+
visit(def.parser, false);
|
|
1180
|
+
return;
|
|
1181
|
+
case "transform":
|
|
1182
|
+
visit(def.parser, true);
|
|
1183
|
+
return;
|
|
1184
|
+
case "skip":
|
|
1185
|
+
visit(def.main, consumed);
|
|
1186
|
+
visit(def.skipped, false);
|
|
1187
|
+
return;
|
|
1188
|
+
case "sepBy":
|
|
1189
|
+
visit(def.parser, true);
|
|
1190
|
+
visit(def.separator, false);
|
|
1191
|
+
return;
|
|
1192
|
+
case "not":
|
|
1193
|
+
visit(def.parser, false);
|
|
1194
|
+
return;
|
|
1195
|
+
case "grammar":
|
|
1196
|
+
visit(def.parser, consumed);
|
|
1197
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1198
|
+
return;
|
|
1199
|
+
case "recover":
|
|
1200
|
+
visit(def.parser, consumed);
|
|
1201
|
+
visit(def.sentinel, false);
|
|
1202
|
+
return;
|
|
1203
|
+
case "label":
|
|
1204
|
+
case "trivia":
|
|
1205
|
+
case "withCtx":
|
|
1206
|
+
case "expect":
|
|
1207
|
+
visit(def.parser, consumed);
|
|
1208
|
+
return;
|
|
1209
|
+
case "lazy":
|
|
1210
|
+
return;
|
|
1211
|
+
default:
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
const resolveRoot = (c) => {
|
|
1216
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1217
|
+
let cur = c;
|
|
1218
|
+
while (!guard2.has(cur)) {
|
|
1219
|
+
const d = cur._def;
|
|
1220
|
+
if (d.tag !== "lazy") return cur;
|
|
1221
|
+
guard2.add(cur);
|
|
1222
|
+
try {
|
|
1223
|
+
cur = d.thunk();
|
|
1224
|
+
} catch {
|
|
1225
|
+
return c;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return cur;
|
|
1229
|
+
};
|
|
1230
|
+
visit(resolveRoot(root), true);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1081
1233
|
// src/combinators/not.ts
|
|
1082
1234
|
function not(combinator) {
|
|
1083
1235
|
const meta = {
|
|
@@ -1414,8 +1566,8 @@ function classToRanges(cls) {
|
|
|
1414
1566
|
return parseClassRanges(body);
|
|
1415
1567
|
}
|
|
1416
1568
|
var foldEq = (cVar, cp) => {
|
|
1417
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1418
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1569
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1570
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1419
1571
|
return `${cVar} === ${cp}`;
|
|
1420
1572
|
};
|
|
1421
1573
|
function literalCodePoints(frag) {
|
|
@@ -1666,6 +1818,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1666
1818
|
return classDisjoint(a, b);
|
|
1667
1819
|
}
|
|
1668
1820
|
function fixedClassSeq(shape) {
|
|
1821
|
+
if (shape.kind === "litFold") {
|
|
1822
|
+
return shape.open.map((cp) => {
|
|
1823
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1824
|
+
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 };
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1669
1827
|
if (shape.kind !== "seq") return null;
|
|
1670
1828
|
const out = [];
|
|
1671
1829
|
for (const p of shape.parts) {
|
|
@@ -2087,12 +2245,34 @@ function parseScanShapeCore(source) {
|
|
|
2087
2245
|
}
|
|
2088
2246
|
function scanShapeFromRegex(source, flags) {
|
|
2089
2247
|
if (/[msu]/.test(flags)) return null;
|
|
2090
|
-
if (/i/.test(flags))
|
|
2091
|
-
const open = literalCodePoints(source);
|
|
2092
|
-
return open ? { kind: "litFold", open } : null;
|
|
2093
|
-
}
|
|
2248
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2094
2249
|
return parseScanShape(source);
|
|
2095
2250
|
}
|
|
2251
|
+
function caseFoldShape(source) {
|
|
2252
|
+
const la = stripTrailingLookahead(source);
|
|
2253
|
+
if (la) {
|
|
2254
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2255
|
+
if (!inner) return null;
|
|
2256
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2257
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2258
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2259
|
+
}
|
|
2260
|
+
return caseFoldLiteralOrAlt(source);
|
|
2261
|
+
}
|
|
2262
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2263
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2264
|
+
const open = literalCodePoints(source);
|
|
2265
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2266
|
+
const arms = splitTopLevelAlternation(source);
|
|
2267
|
+
if (!arms || arms.length < 2) return null;
|
|
2268
|
+
const litArms = [];
|
|
2269
|
+
for (const arm of arms) {
|
|
2270
|
+
const cp = literalCodePoints(arm);
|
|
2271
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2272
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2273
|
+
}
|
|
2274
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2275
|
+
}
|
|
2096
2276
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2097
2277
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2098
2278
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2104,6 +2284,25 @@ function stringHardStop(shape) {
|
|
|
2104
2284
|
);
|
|
2105
2285
|
}
|
|
2106
2286
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2287
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2288
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2289
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2290
|
+
function planAltSwitch(firsts) {
|
|
2291
|
+
const cases = [];
|
|
2292
|
+
let total = 0;
|
|
2293
|
+
for (const fs2 of firsts) {
|
|
2294
|
+
if (!fs2 || fs2.negated) return { kind: "if" };
|
|
2295
|
+
const pts = [];
|
|
2296
|
+
for (const [lo, hi] of fs2.ranges) {
|
|
2297
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2298
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2299
|
+
}
|
|
2300
|
+
total += pts.length;
|
|
2301
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2302
|
+
cases.push(pts);
|
|
2303
|
+
}
|
|
2304
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2305
|
+
}
|
|
2107
2306
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2108
2307
|
if (shape.kind === "lookahead") {
|
|
2109
2308
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2128,18 +2327,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2128
2327
|
const okV = mint("_ok");
|
|
2129
2328
|
const endV2 = mint("_end");
|
|
2130
2329
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2330
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2331
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2332
|
+
for (const a of shape.arms) {
|
|
2333
|
+
const open = a.open;
|
|
2334
|
+
const f = open[0];
|
|
2335
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2336
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2337
|
+
}
|
|
2338
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2339
|
+
for (const [lower, arms] of groups) {
|
|
2340
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2341
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2342
|
+
for (const open of arms) {
|
|
2343
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2344
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2345
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2346
|
+
}
|
|
2347
|
+
lines.push(`${ind2} break`);
|
|
2348
|
+
lines.push(`${ind2} }`);
|
|
2349
|
+
}
|
|
2350
|
+
lines.push(`${ind2}}`);
|
|
2351
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2352
|
+
}
|
|
2131
2353
|
if (shape.disjoint) {
|
|
2132
|
-
const
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2354
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2355
|
+
if (plan.kind === "switch") {
|
|
2356
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2357
|
+
shape.arms.forEach((arm, k) => {
|
|
2358
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2359
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2360
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2361
|
+
lines.push(...m.setup);
|
|
2362
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2363
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2364
|
+
lines.push(`${ind2} break`);
|
|
2365
|
+
lines.push(`${ind2} }`);
|
|
2366
|
+
});
|
|
2141
2367
|
lines.push(`${ind2}}`);
|
|
2142
|
-
}
|
|
2368
|
+
} else {
|
|
2369
|
+
const c0 = codeAt(start, firstChar);
|
|
2370
|
+
shape.arms.forEach((arm, k) => {
|
|
2371
|
+
const fs2 = shape.firsts[k];
|
|
2372
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2373
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2374
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2375
|
+
lines.push(...m.setup);
|
|
2376
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2377
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2378
|
+
lines.push(`${ind2}}`);
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2143
2381
|
} else {
|
|
2144
2382
|
const lbl = mint("_alt");
|
|
2145
2383
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2627,6 +2865,16 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2627
2865
|
}
|
|
2628
2866
|
|
|
2629
2867
|
// src/compiler/codegen.ts
|
|
2868
|
+
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 }";
|
|
2869
|
+
var _loweringSink = null;
|
|
2870
|
+
function beginLoweringCapture() {
|
|
2871
|
+
_loweringSink = /* @__PURE__ */ new Set();
|
|
2872
|
+
}
|
|
2873
|
+
function endLoweringCapture() {
|
|
2874
|
+
const misses = _loweringSink ? [..._loweringSink] : [];
|
|
2875
|
+
_loweringSink = null;
|
|
2876
|
+
return misses;
|
|
2877
|
+
}
|
|
2630
2878
|
function v(ctx, prefix = "_v") {
|
|
2631
2879
|
return `${prefix}${ctx.vars++}`;
|
|
2632
2880
|
}
|
|
@@ -3025,13 +3273,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3025
3273
|
const vv = v(ctx);
|
|
3026
3274
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3027
3275
|
const stmts = [];
|
|
3028
|
-
if (caseInsensitive) {
|
|
3029
|
-
|
|
3276
|
+
if (caseInsensitive && len > 0) {
|
|
3277
|
+
const match = Array.from(
|
|
3278
|
+
{ length: len },
|
|
3279
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3280
|
+
).join(" && ");
|
|
3030
3281
|
stmts.push(
|
|
3031
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3032
|
-
`${ind(ctx)}const ${vv}
|
|
3033
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3034
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3282
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3283
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3035
3284
|
);
|
|
3036
3285
|
} else if (len === 0) {
|
|
3037
3286
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3152,6 +3401,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3152
3401
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3153
3402
|
}
|
|
3154
3403
|
}
|
|
3404
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3155
3405
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3156
3406
|
const key = `${def.source}/${flags}`;
|
|
3157
3407
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3216,6 +3466,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3216
3466
|
}
|
|
3217
3467
|
function emitSeq(def, ctx, pos) {
|
|
3218
3468
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3469
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3219
3470
|
const arrV = v(ctx, "_arr");
|
|
3220
3471
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3221
3472
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3422,7 +3673,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3422
3673
|
const autoNot = def.autoNot[i];
|
|
3423
3674
|
const atStart = failsAtStart(p);
|
|
3424
3675
|
const staticFx = armStaticExpected(ctx, p);
|
|
3425
|
-
const
|
|
3676
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3677
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3426
3678
|
let gateCond = null;
|
|
3427
3679
|
if (gate) {
|
|
3428
3680
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3513,19 +3765,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3513
3765
|
return checks.join(" && ");
|
|
3514
3766
|
}
|
|
3515
3767
|
function emitMany(def, ctx, pos) {
|
|
3768
|
+
const wantValue = !def.valueUnused;
|
|
3516
3769
|
const arrV = v(ctx, "_arr");
|
|
3517
3770
|
const curV = v(ctx, "_cur");
|
|
3518
3771
|
const stmts = [
|
|
3519
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3772
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3520
3773
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3521
3774
|
];
|
|
3522
3775
|
if (def.min === 1) {
|
|
3523
3776
|
const firstR = emit(def.parser, ctx, curV);
|
|
3524
3777
|
stmts.push(...firstR.stmts);
|
|
3525
|
-
stmts.push(
|
|
3526
|
-
|
|
3527
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3528
|
-
);
|
|
3778
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3779
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3529
3780
|
}
|
|
3530
3781
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3531
3782
|
ctx.indent++;
|
|
@@ -3556,13 +3807,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3556
3807
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3557
3808
|
stmts.push(
|
|
3558
3809
|
...iterStmts,
|
|
3559
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3560
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3561
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3810
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3562
3811
|
);
|
|
3812
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3813
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3563
3814
|
ctx.indent--;
|
|
3564
3815
|
stmts.push(`${ind(ctx)}}`);
|
|
3565
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3816
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3566
3817
|
}
|
|
3567
3818
|
function emitOptional(def, ctx, pos) {
|
|
3568
3819
|
const valV = v(ctx, "_opt");
|
|
@@ -3731,15 +3982,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3731
3982
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3732
3983
|
}
|
|
3733
3984
|
const i = ind(ctx);
|
|
3734
|
-
const capturesTrivia = mkType !== null || structural
|
|
3735
|
-
const clonesState = structural
|
|
3985
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
3986
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3736
3987
|
const chV = v(ctx, "_ch");
|
|
3737
3988
|
const rawV = v(ctx, "_raw");
|
|
3738
|
-
const
|
|
3989
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
3990
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
3991
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3739
3992
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3740
3993
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3741
|
-
|
|
3742
|
-
const
|
|
3994
|
+
if (structural) ctx.needsHostReads = true;
|
|
3995
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
3996
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
3997
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3743
3998
|
const stmts = [
|
|
3744
3999
|
allocStmt,
|
|
3745
4000
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3750,7 +4005,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3750
4005
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3751
4006
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3752
4007
|
let stV = "undefined";
|
|
3753
|
-
if (
|
|
4008
|
+
if (structural) {
|
|
4009
|
+
stV = v(ctx, "_nst");
|
|
4010
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4011
|
+
} else if (clonesState) {
|
|
3754
4012
|
stV = v(ctx, "_nst");
|
|
3755
4013
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3756
4014
|
}
|
|
@@ -4172,6 +4430,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4172
4430
|
return deps;
|
|
4173
4431
|
}
|
|
4174
4432
|
function compile(combinator, mapFnSources) {
|
|
4433
|
+
markUnusedValues(combinator);
|
|
4175
4434
|
const ctx = {
|
|
4176
4435
|
vars: 0,
|
|
4177
4436
|
indent: 1,
|
|
@@ -4185,7 +4444,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4185
4444
|
buildFns: [],
|
|
4186
4445
|
buildSrcs: [],
|
|
4187
4446
|
runtimeParsers: [],
|
|
4188
|
-
needsCollator: false,
|
|
4189
4447
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4190
4448
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4191
4449
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4194,18 +4452,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4194
4452
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4195
4453
|
};
|
|
4196
4454
|
const r = emit(combinator, ctx, "_pos");
|
|
4197
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4198
|
-
` : "";
|
|
4199
4455
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4200
4456
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4457
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4201
4458
|
const source = [
|
|
4202
4459
|
...emptyTlDecls,
|
|
4460
|
+
...hostReadsDecls,
|
|
4203
4461
|
...ctx.regexDecls,
|
|
4204
4462
|
...ctx.expectedDecls,
|
|
4205
4463
|
"",
|
|
4206
4464
|
...namedPrelude,
|
|
4207
4465
|
ctx.namedFnDecls.join("\n\n"),
|
|
4208
|
-
|
|
4466
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4209
4467
|
` let pos = _pos`,
|
|
4210
4468
|
...r.stmts,
|
|
4211
4469
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4213,9 +4471,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4213
4471
|
].join("\n");
|
|
4214
4472
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4215
4473
|
...emptyTlDecls,
|
|
4474
|
+
...hostReadsDecls,
|
|
4216
4475
|
...ctx.regexDecls,
|
|
4217
4476
|
...ctx.expectedDecls,
|
|
4218
|
-
collatorDecl,
|
|
4219
4477
|
...namedPrelude,
|
|
4220
4478
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4221
4479
|
`let pos = _pos`,
|
|
@@ -4232,7 +4490,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4232
4490
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4233
4491
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4234
4492
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4235
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4493
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4236
4494
|
return {
|
|
4237
4495
|
source,
|
|
4238
4496
|
inlineExpression,
|
|
@@ -4254,7 +4512,16 @@ function compile(combinator, mapFnSources) {
|
|
|
4254
4512
|
}
|
|
4255
4513
|
};
|
|
4256
4514
|
}
|
|
4515
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4516
|
+
function assertRuleName(name) {
|
|
4517
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4518
|
+
throw new Error(
|
|
4519
|
+
`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).`
|
|
4520
|
+
);
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4257
4523
|
function compileRuleMap(ruleMap) {
|
|
4524
|
+
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
4258
4525
|
const ctx = {
|
|
4259
4526
|
vars: 0,
|
|
4260
4527
|
indent: 1,
|
|
@@ -4268,7 +4535,6 @@ function compileRuleMap(ruleMap) {
|
|
|
4268
4535
|
buildFns: [],
|
|
4269
4536
|
buildSrcs: [],
|
|
4270
4537
|
runtimeParsers: [],
|
|
4271
|
-
needsCollator: false,
|
|
4272
4538
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4273
4539
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4274
4540
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4279,15 +4545,14 @@ function compileRuleMap(ruleMap) {
|
|
|
4279
4545
|
const seenNames = /* @__PURE__ */ new Map();
|
|
4280
4546
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4281
4547
|
for (const [key, rule] of ruleMap) {
|
|
4282
|
-
|
|
4548
|
+
assertRuleName(key);
|
|
4549
|
+
const base = `_r_${key}`;
|
|
4283
4550
|
const n = seenNames.get(base) ?? 0;
|
|
4284
4551
|
seenNames.set(base, n + 1);
|
|
4285
4552
|
ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
|
|
4286
4553
|
}
|
|
4287
4554
|
ctx.ruleNames = ruleNames;
|
|
4288
4555
|
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
4289
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4290
|
-
` : "";
|
|
4291
4556
|
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
4292
4557
|
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
4293
4558
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
@@ -4299,9 +4564,9 @@ function compileRuleMap(ruleMap) {
|
|
|
4299
4564
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4300
4565
|
const hoistedDecls = [
|
|
4301
4566
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
4567
|
+
ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
|
|
4302
4568
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4303
4569
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4304
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4305
4570
|
mfDecl,
|
|
4306
4571
|
buildDecl,
|
|
4307
4572
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4330,6 +4595,7 @@ function compileRuleMap(ruleMap) {
|
|
|
4330
4595
|
}
|
|
4331
4596
|
function compileLinkable(ruleMapArg, ns) {
|
|
4332
4597
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4598
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4333
4599
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4334
4600
|
const d = val._def;
|
|
4335
4601
|
if (d.tag !== "lazy") return true;
|
|
@@ -4353,20 +4619,21 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4353
4619
|
buildFns: [],
|
|
4354
4620
|
buildSrcs: [],
|
|
4355
4621
|
runtimeParsers: [],
|
|
4356
|
-
needsCollator: false,
|
|
4357
4622
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4358
4623
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4359
4624
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4360
4625
|
namedFnDecls: [],
|
|
4361
4626
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4362
4627
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4363
|
-
ns
|
|
4628
|
+
ns,
|
|
4629
|
+
deferFirstSetRefs: true
|
|
4364
4630
|
};
|
|
4365
4631
|
const seen = /* @__PURE__ */ new Map();
|
|
4366
4632
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4367
4633
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4368
4634
|
for (const [key, rule] of ruleMap) {
|
|
4369
|
-
|
|
4635
|
+
assertRuleName(key);
|
|
4636
|
+
const base = `_r_${key}`;
|
|
4370
4637
|
const n = seen.get(base) ?? 0;
|
|
4371
4638
|
seen.set(base, n + 1);
|
|
4372
4639
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4390,7 +4657,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4390
4657
|
if (resolved === void 0) {
|
|
4391
4658
|
const name = p._ruleName;
|
|
4392
4659
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4393
|
-
|
|
4660
|
+
assertRuleName(name);
|
|
4661
|
+
const fn = `_r_${name}`;
|
|
4394
4662
|
ruleNames.set(p, fn);
|
|
4395
4663
|
ctx.namedParsers.set(p, fn);
|
|
4396
4664
|
}
|
|
@@ -4459,20 +4727,34 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4459
4727
|
`}`
|
|
4460
4728
|
].join("\n"));
|
|
4461
4729
|
}
|
|
4730
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4731
|
+
for (const [key, rule] of ruleMap) {
|
|
4732
|
+
let resolved = rule;
|
|
4733
|
+
const d = rule._def;
|
|
4734
|
+
if (d.tag === "lazy") {
|
|
4735
|
+
try {
|
|
4736
|
+
resolved = d.thunk();
|
|
4737
|
+
} catch {
|
|
4738
|
+
resolved = rule;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4742
|
+
}
|
|
4462
4743
|
return {
|
|
4463
4744
|
ns,
|
|
4464
4745
|
keys: perEntry.map((e) => e.key),
|
|
4465
4746
|
prelude,
|
|
4466
4747
|
ruleFns,
|
|
4467
4748
|
wrappers,
|
|
4749
|
+
firstSets,
|
|
4468
4750
|
deps: ruleDependencies(ruleMap),
|
|
4469
4751
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4470
|
-
|
|
4752
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4471
4753
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4472
4754
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4473
4755
|
};
|
|
4474
4756
|
}
|
|
4475
|
-
function buildInlineExpression(ctx, r,
|
|
4757
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4476
4758
|
const bodyLines = [
|
|
4477
4759
|
` let pos = _pos`,
|
|
4478
4760
|
...r.stmts,
|
|
@@ -4486,14 +4768,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4486
4768
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4487
4769
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4488
4770
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4489
|
-
const
|
|
4771
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4772
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4490
4773
|
if (!needsWrapper) return innerFn;
|
|
4491
4774
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4492
4775
|
const hoistedDecls = [
|
|
4493
4776
|
emptyTlDecl,
|
|
4777
|
+
hostReadsDecl,
|
|
4494
4778
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4495
4779
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4496
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4497
4780
|
mfDecl,
|
|
4498
4781
|
buildDecl,
|
|
4499
4782
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4521,20 +4804,36 @@ function fusedBody(pieces) {
|
|
|
4521
4804
|
}
|
|
4522
4805
|
const contributing = new Set(winner.values());
|
|
4523
4806
|
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4524
|
-
const
|
|
4807
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4525
4808
|
const lines = [
|
|
4526
4809
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4527
4810
|
"const _pfFail = {}",
|
|
4528
4811
|
"let _pfEnd",
|
|
4529
4812
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4530
|
-
...
|
|
4813
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4531
4814
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4532
4815
|
...[...contributing].flatMap((p) => p.prelude),
|
|
4533
4816
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4534
4817
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4535
4818
|
];
|
|
4536
4819
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4537
|
-
const
|
|
4820
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4821
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4822
|
+
for (const [k, p] of winner) {
|
|
4823
|
+
const fs2 = p.firstSets?.get(k);
|
|
4824
|
+
if (fs2) finalFS.set(k, fs2);
|
|
4825
|
+
}
|
|
4826
|
+
const body = rawBody.replace(
|
|
4827
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4828
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4829
|
+
// matches every well-formed placeholder.
|
|
4830
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4831
|
+
(_m, name, codevar) => {
|
|
4832
|
+
const fs2 = finalFS.get(name);
|
|
4833
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4834
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
4835
|
+
}
|
|
4836
|
+
);
|
|
4538
4837
|
const env = {};
|
|
4539
4838
|
for (const p of contributing) {
|
|
4540
4839
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -5157,7 +5456,7 @@ var plugin_default = createUnplugin((opts = {}) => ({
|
|
|
5157
5456
|
if (!code.includes("parseman")) return null;
|
|
5158
5457
|
if (!code.includes("macro")) return null;
|
|
5159
5458
|
const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
|
|
5160
|
-
const result = transformMacro(code, id, moduleAliases);
|
|
5459
|
+
const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true);
|
|
5161
5460
|
if (result?.warnings.length) {
|
|
5162
5461
|
for (const w of result.warnings) {
|
|
5163
5462
|
if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
|
|
@@ -5239,7 +5538,7 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5239
5538
|
visit(root);
|
|
5240
5539
|
return found;
|
|
5241
5540
|
}
|
|
5242
|
-
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE])) {
|
|
5541
|
+
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5243
5542
|
let result;
|
|
5244
5543
|
try {
|
|
5245
5544
|
result = parseSync(id, code);
|
|
@@ -5278,6 +5577,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5278
5577
|
const scope = /* @__PURE__ */ new Map();
|
|
5279
5578
|
const replacements = [];
|
|
5280
5579
|
const warnings = [];
|
|
5580
|
+
beginLoweringCapture();
|
|
5281
5581
|
let anyUnresolved = false;
|
|
5282
5582
|
let ruleMapHolderCounter = 0;
|
|
5283
5583
|
const lineOf = (pos) => {
|
|
@@ -5314,7 +5614,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5314
5614
|
const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
5315
5615
|
const serializePieces = (p) => {
|
|
5316
5616
|
const mapLit = (m) => `new Map([${[...m].map(([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(v2)}]`).join(", ")}])`;
|
|
5317
|
-
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude)}, ruleFns: ${mapLit(p.ruleFns)}, wrappers: ${mapLit(p.wrappers)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl},
|
|
5617
|
+
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude)}, ruleFns: ${mapLit(p.ruleFns)}, wrappers: ${mapLit(p.wrappers)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, mfFns: [], buildFns: [] }`;
|
|
5318
5618
|
};
|
|
5319
5619
|
const serializeList = (list) => `[${list.map(serializePieces).join(", ")}]`;
|
|
5320
5620
|
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ (() => { const _g = ${grammarExpr}; Object.defineProperty(_g, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)} }); return _g })()`;
|
|
@@ -5585,6 +5885,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5585
5885
|
for (const { start, end, replacement } of [...replacements].sort((a, b) => b.start - a.start)) {
|
|
5586
5886
|
ms.overwrite(start, end, replacement);
|
|
5587
5887
|
}
|
|
5888
|
+
const unlowered = endLoweringCapture();
|
|
5889
|
+
if (warnUnloweredRegex) {
|
|
5890
|
+
for (const src of unlowered) {
|
|
5891
|
+
warnings.push(`${id}: regex ${src} did not lower to a fast charCodeAt scan (RegExp.exec fallback)`);
|
|
5892
|
+
}
|
|
5893
|
+
}
|
|
5588
5894
|
return {
|
|
5589
5895
|
code: ms.toString(),
|
|
5590
5896
|
map: ms.generateMap({ hires: true }),
|