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.cjs
CHANGED
|
@@ -69,6 +69,63 @@ function any() {
|
|
|
69
69
|
function empty() {
|
|
70
70
|
return { kind: "empty" };
|
|
71
71
|
}
|
|
72
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
73
|
+
if (seen.has(p)) return true;
|
|
74
|
+
seen.add(p);
|
|
75
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
76
|
+
const d = p._def;
|
|
77
|
+
switch (d.tag) {
|
|
78
|
+
case "literal":
|
|
79
|
+
return d.value.length === 0;
|
|
80
|
+
case "keywords":
|
|
81
|
+
return false;
|
|
82
|
+
case "regex":
|
|
83
|
+
try {
|
|
84
|
+
const m = new RegExp(d.source).exec("");
|
|
85
|
+
return m != null && m[0] === "";
|
|
86
|
+
} catch {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
case "many":
|
|
90
|
+
case "optional":
|
|
91
|
+
case "not":
|
|
92
|
+
return true;
|
|
93
|
+
// zero repetitions / absent / lookahead
|
|
94
|
+
case "oneOrMore":
|
|
95
|
+
return me(d.parser);
|
|
96
|
+
case "sequence":
|
|
97
|
+
return d.parsers.every(me);
|
|
98
|
+
case "choice":
|
|
99
|
+
return d.parsers.some(me);
|
|
100
|
+
case "transform":
|
|
101
|
+
case "label":
|
|
102
|
+
case "trivia":
|
|
103
|
+
case "expect":
|
|
104
|
+
case "withCtx":
|
|
105
|
+
case "node":
|
|
106
|
+
case "grammar":
|
|
107
|
+
case "recover":
|
|
108
|
+
return me(d.parser);
|
|
109
|
+
case "skip":
|
|
110
|
+
return me(d.main);
|
|
111
|
+
case "lazy":
|
|
112
|
+
try {
|
|
113
|
+
return me(d.thunk());
|
|
114
|
+
} catch {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function sequenceFirstSet(parsers) {
|
|
122
|
+
let fs2 = empty();
|
|
123
|
+
for (const p of parsers) {
|
|
124
|
+
fs2 = union(fs2, p._meta.firstSet);
|
|
125
|
+
if (!matchesEmpty(p)) return fs2;
|
|
126
|
+
}
|
|
127
|
+
return fs2;
|
|
128
|
+
}
|
|
72
129
|
function mergeRanges(ranges) {
|
|
73
130
|
if (ranges.length === 0) return [];
|
|
74
131
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -276,12 +333,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
276
333
|
}
|
|
277
334
|
|
|
278
335
|
// src/combinators/literal.ts
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
336
|
+
function asciiFoldEq(a, b) {
|
|
337
|
+
if (a.length !== b.length) return false;
|
|
338
|
+
for (let i = 0; i < a.length; i++) {
|
|
339
|
+
let ca = a.charCodeAt(i);
|
|
340
|
+
let cb = b.charCodeAt(i);
|
|
341
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
342
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
343
|
+
if (ca !== cb) return false;
|
|
283
344
|
}
|
|
284
|
-
return
|
|
345
|
+
return true;
|
|
285
346
|
}
|
|
286
347
|
function literal(value, opts = {}) {
|
|
287
348
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -296,8 +357,9 @@ function literal(value, opts = {}) {
|
|
|
296
357
|
const lower = value.toLowerCase();
|
|
297
358
|
const firstUpper = upper.codePointAt(0);
|
|
298
359
|
const firstLower = lower.codePointAt(0);
|
|
299
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
300
|
-
{ lo:
|
|
360
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
361
|
+
{ lo: firstLower, hi: firstLower },
|
|
362
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
301
363
|
] } : firstSet;
|
|
302
364
|
}
|
|
303
365
|
return {
|
|
@@ -310,7 +372,7 @@ function literal(value, opts = {}) {
|
|
|
310
372
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
311
373
|
}
|
|
312
374
|
const slice = input.slice(pos, end);
|
|
313
|
-
const matched = caseInsensitive ?
|
|
375
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
314
376
|
if (matched) {
|
|
315
377
|
const span = { start: pos, end };
|
|
316
378
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -917,16 +979,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
917
979
|
// src/combinators/sequence.ts
|
|
918
980
|
function sequence(...parsers) {
|
|
919
981
|
const meta = {
|
|
920
|
-
|
|
982
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
983
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
984
|
+
firstSet: sequenceFirstSet(parsers),
|
|
921
985
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
922
986
|
isTrivia: false
|
|
923
987
|
};
|
|
988
|
+
const def = { tag: "sequence", parsers };
|
|
924
989
|
return {
|
|
925
990
|
_tag: "sequence",
|
|
926
991
|
_meta: meta,
|
|
927
|
-
_def:
|
|
992
|
+
_def: def,
|
|
928
993
|
parse(input, pos, ctx) {
|
|
929
|
-
const values = [];
|
|
994
|
+
const values = def.valueUnused ? void 0 : [];
|
|
930
995
|
let cur = pos;
|
|
931
996
|
for (let i = 0; i < parsers.length; i++) {
|
|
932
997
|
if (ctx.trivia && i > 0) {
|
|
@@ -946,17 +1011,18 @@ function sequence(...parsers) {
|
|
|
946
1011
|
} else {
|
|
947
1012
|
rollbackTrivia(ctx, mark);
|
|
948
1013
|
}
|
|
949
|
-
values.push(result2.value);
|
|
1014
|
+
if (values !== void 0) values.push(result2.value);
|
|
950
1015
|
continue;
|
|
951
1016
|
}
|
|
952
1017
|
const result = parsers[i].parse(input, cur, ctx);
|
|
953
1018
|
if (!result.ok) return result;
|
|
954
|
-
values.push(result.value);
|
|
1019
|
+
if (values !== void 0) values.push(result.value);
|
|
955
1020
|
cur = result.span.end;
|
|
956
1021
|
}
|
|
957
1022
|
return {
|
|
958
1023
|
ok: true,
|
|
959
|
-
|
|
1024
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
1025
|
+
value: values ?? void 0,
|
|
960
1026
|
span: { start: pos, end: cur }
|
|
961
1027
|
};
|
|
962
1028
|
}
|
|
@@ -997,20 +1063,21 @@ function many(combinator) {
|
|
|
997
1063
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
998
1064
|
isTrivia: false
|
|
999
1065
|
};
|
|
1066
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
1000
1067
|
return {
|
|
1001
1068
|
_tag: "many",
|
|
1002
1069
|
_meta: meta,
|
|
1003
|
-
_def:
|
|
1070
|
+
_def: def,
|
|
1004
1071
|
parse(input, pos, ctx) {
|
|
1005
|
-
const values = [];
|
|
1072
|
+
const values = def.valueUnused ? void 0 : [];
|
|
1006
1073
|
let cur = pos;
|
|
1007
1074
|
while (cur < input.length) {
|
|
1008
1075
|
const item = repItem(combinator, input, cur, ctx);
|
|
1009
1076
|
if (item === "stop" || "fail" in item) break;
|
|
1010
|
-
values.push(item.value);
|
|
1077
|
+
if (values !== void 0) values.push(item.value);
|
|
1011
1078
|
cur = item.end;
|
|
1012
1079
|
}
|
|
1013
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1080
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1014
1081
|
}
|
|
1015
1082
|
};
|
|
1016
1083
|
}
|
|
@@ -1020,22 +1087,23 @@ function oneOrMore(combinator) {
|
|
|
1020
1087
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1021
1088
|
isTrivia: false
|
|
1022
1089
|
};
|
|
1090
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
1023
1091
|
return {
|
|
1024
1092
|
_tag: "oneOrMore",
|
|
1025
1093
|
_meta: meta,
|
|
1026
|
-
_def:
|
|
1094
|
+
_def: def,
|
|
1027
1095
|
parse(input, pos, ctx) {
|
|
1028
1096
|
const first = combinator.parse(input, pos, ctx);
|
|
1029
1097
|
if (!first.ok) return first;
|
|
1030
|
-
const values = [first.value];
|
|
1098
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
1031
1099
|
let cur = first.span.end;
|
|
1032
1100
|
while (cur < input.length) {
|
|
1033
1101
|
const item = repItem(combinator, input, cur, ctx);
|
|
1034
1102
|
if (item === "stop" || "fail" in item) break;
|
|
1035
|
-
values.push(item.value);
|
|
1103
|
+
if (values !== void 0) values.push(item.value);
|
|
1036
1104
|
cur = item.end;
|
|
1037
1105
|
}
|
|
1038
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1106
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1039
1107
|
}
|
|
1040
1108
|
};
|
|
1041
1109
|
}
|
|
@@ -1113,6 +1181,90 @@ function sepBy(combinator, separator) {
|
|
|
1113
1181
|
};
|
|
1114
1182
|
}
|
|
1115
1183
|
|
|
1184
|
+
// src/compiler/value-usage.ts
|
|
1185
|
+
function markUnusedValues(root) {
|
|
1186
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1187
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1188
|
+
const mark = (def, consumed) => {
|
|
1189
|
+
if (consumed) def.valueUnused = false;
|
|
1190
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1191
|
+
};
|
|
1192
|
+
const visit = (c, consumed) => {
|
|
1193
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1194
|
+
if (seen.has(c)) return;
|
|
1195
|
+
seen.add(c);
|
|
1196
|
+
const def = c._def;
|
|
1197
|
+
switch (def.tag) {
|
|
1198
|
+
case "many":
|
|
1199
|
+
case "oneOrMore":
|
|
1200
|
+
mark(def, consumed);
|
|
1201
|
+
visit(def.parser, consumed);
|
|
1202
|
+
return;
|
|
1203
|
+
case "optional":
|
|
1204
|
+
visit(def.parser, consumed);
|
|
1205
|
+
return;
|
|
1206
|
+
case "sequence":
|
|
1207
|
+
mark(def, consumed);
|
|
1208
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1209
|
+
return;
|
|
1210
|
+
case "choice":
|
|
1211
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1212
|
+
return;
|
|
1213
|
+
case "node":
|
|
1214
|
+
visit(def.parser, false);
|
|
1215
|
+
return;
|
|
1216
|
+
case "transform":
|
|
1217
|
+
visit(def.parser, true);
|
|
1218
|
+
return;
|
|
1219
|
+
case "skip":
|
|
1220
|
+
visit(def.main, consumed);
|
|
1221
|
+
visit(def.skipped, false);
|
|
1222
|
+
return;
|
|
1223
|
+
case "sepBy":
|
|
1224
|
+
visit(def.parser, true);
|
|
1225
|
+
visit(def.separator, false);
|
|
1226
|
+
return;
|
|
1227
|
+
case "not":
|
|
1228
|
+
visit(def.parser, false);
|
|
1229
|
+
return;
|
|
1230
|
+
case "grammar":
|
|
1231
|
+
visit(def.parser, consumed);
|
|
1232
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1233
|
+
return;
|
|
1234
|
+
case "recover":
|
|
1235
|
+
visit(def.parser, consumed);
|
|
1236
|
+
visit(def.sentinel, false);
|
|
1237
|
+
return;
|
|
1238
|
+
case "label":
|
|
1239
|
+
case "trivia":
|
|
1240
|
+
case "withCtx":
|
|
1241
|
+
case "expect":
|
|
1242
|
+
visit(def.parser, consumed);
|
|
1243
|
+
return;
|
|
1244
|
+
case "lazy":
|
|
1245
|
+
return;
|
|
1246
|
+
default:
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
const resolveRoot = (c) => {
|
|
1251
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1252
|
+
let cur = c;
|
|
1253
|
+
while (!guard2.has(cur)) {
|
|
1254
|
+
const d = cur._def;
|
|
1255
|
+
if (d.tag !== "lazy") return cur;
|
|
1256
|
+
guard2.add(cur);
|
|
1257
|
+
try {
|
|
1258
|
+
cur = d.thunk();
|
|
1259
|
+
} catch {
|
|
1260
|
+
return c;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return cur;
|
|
1264
|
+
};
|
|
1265
|
+
visit(resolveRoot(root), true);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1116
1268
|
// src/combinators/not.ts
|
|
1117
1269
|
function not(combinator) {
|
|
1118
1270
|
const meta = {
|
|
@@ -1449,8 +1601,8 @@ function classToRanges(cls) {
|
|
|
1449
1601
|
return parseClassRanges(body);
|
|
1450
1602
|
}
|
|
1451
1603
|
var foldEq = (cVar, cp) => {
|
|
1452
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1453
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1604
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1605
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1454
1606
|
return `${cVar} === ${cp}`;
|
|
1455
1607
|
};
|
|
1456
1608
|
function literalCodePoints(frag) {
|
|
@@ -1701,6 +1853,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1701
1853
|
return classDisjoint(a, b);
|
|
1702
1854
|
}
|
|
1703
1855
|
function fixedClassSeq(shape) {
|
|
1856
|
+
if (shape.kind === "litFold") {
|
|
1857
|
+
return shape.open.map((cp) => {
|
|
1858
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1859
|
+
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 };
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1704
1862
|
if (shape.kind !== "seq") return null;
|
|
1705
1863
|
const out = [];
|
|
1706
1864
|
for (const p of shape.parts) {
|
|
@@ -2122,12 +2280,34 @@ function parseScanShapeCore(source) {
|
|
|
2122
2280
|
}
|
|
2123
2281
|
function scanShapeFromRegex(source, flags) {
|
|
2124
2282
|
if (/[msu]/.test(flags)) return null;
|
|
2125
|
-
if (/i/.test(flags))
|
|
2126
|
-
const open = literalCodePoints(source);
|
|
2127
|
-
return open ? { kind: "litFold", open } : null;
|
|
2128
|
-
}
|
|
2283
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2129
2284
|
return parseScanShape(source);
|
|
2130
2285
|
}
|
|
2286
|
+
function caseFoldShape(source) {
|
|
2287
|
+
const la = stripTrailingLookahead(source);
|
|
2288
|
+
if (la) {
|
|
2289
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2290
|
+
if (!inner) return null;
|
|
2291
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2292
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2293
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2294
|
+
}
|
|
2295
|
+
return caseFoldLiteralOrAlt(source);
|
|
2296
|
+
}
|
|
2297
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2298
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2299
|
+
const open = literalCodePoints(source);
|
|
2300
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2301
|
+
const arms = splitTopLevelAlternation(source);
|
|
2302
|
+
if (!arms || arms.length < 2) return null;
|
|
2303
|
+
const litArms = [];
|
|
2304
|
+
for (const arm of arms) {
|
|
2305
|
+
const cp = literalCodePoints(arm);
|
|
2306
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2307
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2308
|
+
}
|
|
2309
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2310
|
+
}
|
|
2131
2311
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2132
2312
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2133
2313
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2139,6 +2319,25 @@ function stringHardStop(shape) {
|
|
|
2139
2319
|
);
|
|
2140
2320
|
}
|
|
2141
2321
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2322
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2323
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2324
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2325
|
+
function planAltSwitch(firsts) {
|
|
2326
|
+
const cases = [];
|
|
2327
|
+
let total = 0;
|
|
2328
|
+
for (const fs2 of firsts) {
|
|
2329
|
+
if (!fs2 || fs2.negated) return { kind: "if" };
|
|
2330
|
+
const pts = [];
|
|
2331
|
+
for (const [lo, hi] of fs2.ranges) {
|
|
2332
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2333
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2334
|
+
}
|
|
2335
|
+
total += pts.length;
|
|
2336
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2337
|
+
cases.push(pts);
|
|
2338
|
+
}
|
|
2339
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2340
|
+
}
|
|
2142
2341
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2143
2342
|
if (shape.kind === "lookahead") {
|
|
2144
2343
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2163,18 +2362,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2163
2362
|
const okV = mint("_ok");
|
|
2164
2363
|
const endV2 = mint("_end");
|
|
2165
2364
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2365
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2366
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2367
|
+
for (const a of shape.arms) {
|
|
2368
|
+
const open = a.open;
|
|
2369
|
+
const f = open[0];
|
|
2370
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2371
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2372
|
+
}
|
|
2373
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2374
|
+
for (const [lower, arms] of groups) {
|
|
2375
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2376
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2377
|
+
for (const open of arms) {
|
|
2378
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2379
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2380
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2381
|
+
}
|
|
2382
|
+
lines.push(`${ind2} break`);
|
|
2383
|
+
lines.push(`${ind2} }`);
|
|
2384
|
+
}
|
|
2385
|
+
lines.push(`${ind2}}`);
|
|
2386
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2387
|
+
}
|
|
2166
2388
|
if (shape.disjoint) {
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2389
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2390
|
+
if (plan.kind === "switch") {
|
|
2391
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2392
|
+
shape.arms.forEach((arm, k) => {
|
|
2393
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2394
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2395
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2396
|
+
lines.push(...m.setup);
|
|
2397
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2398
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2399
|
+
lines.push(`${ind2} break`);
|
|
2400
|
+
lines.push(`${ind2} }`);
|
|
2401
|
+
});
|
|
2176
2402
|
lines.push(`${ind2}}`);
|
|
2177
|
-
}
|
|
2403
|
+
} else {
|
|
2404
|
+
const c0 = codeAt(start, firstChar);
|
|
2405
|
+
shape.arms.forEach((arm, k) => {
|
|
2406
|
+
const fs2 = shape.firsts[k];
|
|
2407
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2408
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2409
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2410
|
+
lines.push(...m.setup);
|
|
2411
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2412
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2413
|
+
lines.push(`${ind2}}`);
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2178
2416
|
} else {
|
|
2179
2417
|
const lbl = mint("_alt");
|
|
2180
2418
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2662,6 +2900,16 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2662
2900
|
}
|
|
2663
2901
|
|
|
2664
2902
|
// src/compiler/codegen.ts
|
|
2903
|
+
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 }";
|
|
2904
|
+
var _loweringSink = null;
|
|
2905
|
+
function beginLoweringCapture() {
|
|
2906
|
+
_loweringSink = /* @__PURE__ */ new Set();
|
|
2907
|
+
}
|
|
2908
|
+
function endLoweringCapture() {
|
|
2909
|
+
const misses = _loweringSink ? [..._loweringSink] : [];
|
|
2910
|
+
_loweringSink = null;
|
|
2911
|
+
return misses;
|
|
2912
|
+
}
|
|
2665
2913
|
function v(ctx, prefix = "_v") {
|
|
2666
2914
|
return `${prefix}${ctx.vars++}`;
|
|
2667
2915
|
}
|
|
@@ -3060,13 +3308,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3060
3308
|
const vv = v(ctx);
|
|
3061
3309
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3062
3310
|
const stmts = [];
|
|
3063
|
-
if (caseInsensitive) {
|
|
3064
|
-
|
|
3311
|
+
if (caseInsensitive && len > 0) {
|
|
3312
|
+
const match = Array.from(
|
|
3313
|
+
{ length: len },
|
|
3314
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3315
|
+
).join(" && ");
|
|
3065
3316
|
stmts.push(
|
|
3066
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3067
|
-
`${ind(ctx)}const ${vv}
|
|
3068
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3069
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3317
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3318
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3070
3319
|
);
|
|
3071
3320
|
} else if (len === 0) {
|
|
3072
3321
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3187,6 +3436,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3187
3436
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3188
3437
|
}
|
|
3189
3438
|
}
|
|
3439
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3190
3440
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3191
3441
|
const key = `${def.source}/${flags}`;
|
|
3192
3442
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3251,6 +3501,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3251
3501
|
}
|
|
3252
3502
|
function emitSeq(def, ctx, pos) {
|
|
3253
3503
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3504
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3254
3505
|
const arrV = v(ctx, "_arr");
|
|
3255
3506
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3256
3507
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3457,7 +3708,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3457
3708
|
const autoNot = def.autoNot[i];
|
|
3458
3709
|
const atStart = failsAtStart(p);
|
|
3459
3710
|
const staticFx = armStaticExpected(ctx, p);
|
|
3460
|
-
const
|
|
3711
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3712
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3461
3713
|
let gateCond = null;
|
|
3462
3714
|
if (gate) {
|
|
3463
3715
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3548,19 +3800,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3548
3800
|
return checks.join(" && ");
|
|
3549
3801
|
}
|
|
3550
3802
|
function emitMany(def, ctx, pos) {
|
|
3803
|
+
const wantValue = !def.valueUnused;
|
|
3551
3804
|
const arrV = v(ctx, "_arr");
|
|
3552
3805
|
const curV = v(ctx, "_cur");
|
|
3553
3806
|
const stmts = [
|
|
3554
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3807
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3555
3808
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3556
3809
|
];
|
|
3557
3810
|
if (def.min === 1) {
|
|
3558
3811
|
const firstR = emit(def.parser, ctx, curV);
|
|
3559
3812
|
stmts.push(...firstR.stmts);
|
|
3560
|
-
stmts.push(
|
|
3561
|
-
|
|
3562
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3563
|
-
);
|
|
3813
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3814
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3564
3815
|
}
|
|
3565
3816
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3566
3817
|
ctx.indent++;
|
|
@@ -3591,13 +3842,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3591
3842
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3592
3843
|
stmts.push(
|
|
3593
3844
|
...iterStmts,
|
|
3594
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3595
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3596
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3845
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3597
3846
|
);
|
|
3847
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3848
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3598
3849
|
ctx.indent--;
|
|
3599
3850
|
stmts.push(`${ind(ctx)}}`);
|
|
3600
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3851
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3601
3852
|
}
|
|
3602
3853
|
function emitOptional(def, ctx, pos) {
|
|
3603
3854
|
const valV = v(ctx, "_opt");
|
|
@@ -3766,15 +4017,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3766
4017
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3767
4018
|
}
|
|
3768
4019
|
const i = ind(ctx);
|
|
3769
|
-
const capturesTrivia = mkType !== null || structural
|
|
3770
|
-
const clonesState = structural
|
|
4020
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4021
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3771
4022
|
const chV = v(ctx, "_ch");
|
|
3772
4023
|
const rawV = v(ctx, "_raw");
|
|
3773
|
-
const
|
|
4024
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4025
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4026
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3774
4027
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3775
4028
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3776
|
-
|
|
3777
|
-
const
|
|
4029
|
+
if (structural) ctx.needsHostReads = true;
|
|
4030
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4031
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4032
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3778
4033
|
const stmts = [
|
|
3779
4034
|
allocStmt,
|
|
3780
4035
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3785,7 +4040,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3785
4040
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3786
4041
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3787
4042
|
let stV = "undefined";
|
|
3788
|
-
if (
|
|
4043
|
+
if (structural) {
|
|
4044
|
+
stV = v(ctx, "_nst");
|
|
4045
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4046
|
+
} else if (clonesState) {
|
|
3789
4047
|
stV = v(ctx, "_nst");
|
|
3790
4048
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3791
4049
|
}
|
|
@@ -4207,6 +4465,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4207
4465
|
return deps;
|
|
4208
4466
|
}
|
|
4209
4467
|
function compile(combinator, mapFnSources) {
|
|
4468
|
+
markUnusedValues(combinator);
|
|
4210
4469
|
const ctx = {
|
|
4211
4470
|
vars: 0,
|
|
4212
4471
|
indent: 1,
|
|
@@ -4220,7 +4479,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4220
4479
|
buildFns: [],
|
|
4221
4480
|
buildSrcs: [],
|
|
4222
4481
|
runtimeParsers: [],
|
|
4223
|
-
needsCollator: false,
|
|
4224
4482
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4225
4483
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4226
4484
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4229,18 +4487,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4229
4487
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4230
4488
|
};
|
|
4231
4489
|
const r = emit(combinator, ctx, "_pos");
|
|
4232
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4233
|
-
` : "";
|
|
4234
4490
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4235
4491
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4492
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4236
4493
|
const source = [
|
|
4237
4494
|
...emptyTlDecls,
|
|
4495
|
+
...hostReadsDecls,
|
|
4238
4496
|
...ctx.regexDecls,
|
|
4239
4497
|
...ctx.expectedDecls,
|
|
4240
4498
|
"",
|
|
4241
4499
|
...namedPrelude,
|
|
4242
4500
|
ctx.namedFnDecls.join("\n\n"),
|
|
4243
|
-
|
|
4501
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4244
4502
|
` let pos = _pos`,
|
|
4245
4503
|
...r.stmts,
|
|
4246
4504
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4248,9 +4506,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4248
4506
|
].join("\n");
|
|
4249
4507
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4250
4508
|
...emptyTlDecls,
|
|
4509
|
+
...hostReadsDecls,
|
|
4251
4510
|
...ctx.regexDecls,
|
|
4252
4511
|
...ctx.expectedDecls,
|
|
4253
|
-
collatorDecl,
|
|
4254
4512
|
...namedPrelude,
|
|
4255
4513
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4256
4514
|
`let pos = _pos`,
|
|
@@ -4267,7 +4525,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4267
4525
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4268
4526
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4269
4527
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4270
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4528
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4271
4529
|
return {
|
|
4272
4530
|
source,
|
|
4273
4531
|
inlineExpression,
|
|
@@ -4289,7 +4547,16 @@ function compile(combinator, mapFnSources) {
|
|
|
4289
4547
|
}
|
|
4290
4548
|
};
|
|
4291
4549
|
}
|
|
4550
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4551
|
+
function assertRuleName(name) {
|
|
4552
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4553
|
+
throw new Error(
|
|
4554
|
+
`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).`
|
|
4555
|
+
);
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4292
4558
|
function compileRuleMap(ruleMap) {
|
|
4559
|
+
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
4293
4560
|
const ctx = {
|
|
4294
4561
|
vars: 0,
|
|
4295
4562
|
indent: 1,
|
|
@@ -4303,7 +4570,6 @@ function compileRuleMap(ruleMap) {
|
|
|
4303
4570
|
buildFns: [],
|
|
4304
4571
|
buildSrcs: [],
|
|
4305
4572
|
runtimeParsers: [],
|
|
4306
|
-
needsCollator: false,
|
|
4307
4573
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4308
4574
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4309
4575
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4314,15 +4580,14 @@ function compileRuleMap(ruleMap) {
|
|
|
4314
4580
|
const seenNames = /* @__PURE__ */ new Map();
|
|
4315
4581
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4316
4582
|
for (const [key, rule] of ruleMap) {
|
|
4317
|
-
|
|
4583
|
+
assertRuleName(key);
|
|
4584
|
+
const base = `_r_${key}`;
|
|
4318
4585
|
const n = seenNames.get(base) ?? 0;
|
|
4319
4586
|
seenNames.set(base, n + 1);
|
|
4320
4587
|
ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
|
|
4321
4588
|
}
|
|
4322
4589
|
ctx.ruleNames = ruleNames;
|
|
4323
4590
|
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
4324
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4325
|
-
` : "";
|
|
4326
4591
|
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
4327
4592
|
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
4328
4593
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
@@ -4334,9 +4599,9 @@ function compileRuleMap(ruleMap) {
|
|
|
4334
4599
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4335
4600
|
const hoistedDecls = [
|
|
4336
4601
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
4602
|
+
ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
|
|
4337
4603
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4338
4604
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4339
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4340
4605
|
mfDecl,
|
|
4341
4606
|
buildDecl,
|
|
4342
4607
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4365,6 +4630,7 @@ function compileRuleMap(ruleMap) {
|
|
|
4365
4630
|
}
|
|
4366
4631
|
function compileLinkable(ruleMapArg, ns) {
|
|
4367
4632
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4633
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4368
4634
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4369
4635
|
const d = val._def;
|
|
4370
4636
|
if (d.tag !== "lazy") return true;
|
|
@@ -4388,20 +4654,21 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4388
4654
|
buildFns: [],
|
|
4389
4655
|
buildSrcs: [],
|
|
4390
4656
|
runtimeParsers: [],
|
|
4391
|
-
needsCollator: false,
|
|
4392
4657
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4393
4658
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4394
4659
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4395
4660
|
namedFnDecls: [],
|
|
4396
4661
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4397
4662
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4398
|
-
ns
|
|
4663
|
+
ns,
|
|
4664
|
+
deferFirstSetRefs: true
|
|
4399
4665
|
};
|
|
4400
4666
|
const seen = /* @__PURE__ */ new Map();
|
|
4401
4667
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4402
4668
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4403
4669
|
for (const [key, rule] of ruleMap) {
|
|
4404
|
-
|
|
4670
|
+
assertRuleName(key);
|
|
4671
|
+
const base = `_r_${key}`;
|
|
4405
4672
|
const n = seen.get(base) ?? 0;
|
|
4406
4673
|
seen.set(base, n + 1);
|
|
4407
4674
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4425,7 +4692,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4425
4692
|
if (resolved === void 0) {
|
|
4426
4693
|
const name = p._ruleName;
|
|
4427
4694
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4428
|
-
|
|
4695
|
+
assertRuleName(name);
|
|
4696
|
+
const fn = `_r_${name}`;
|
|
4429
4697
|
ruleNames.set(p, fn);
|
|
4430
4698
|
ctx.namedParsers.set(p, fn);
|
|
4431
4699
|
}
|
|
@@ -4494,20 +4762,34 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4494
4762
|
`}`
|
|
4495
4763
|
].join("\n"));
|
|
4496
4764
|
}
|
|
4765
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4766
|
+
for (const [key, rule] of ruleMap) {
|
|
4767
|
+
let resolved = rule;
|
|
4768
|
+
const d = rule._def;
|
|
4769
|
+
if (d.tag === "lazy") {
|
|
4770
|
+
try {
|
|
4771
|
+
resolved = d.thunk();
|
|
4772
|
+
} catch {
|
|
4773
|
+
resolved = rule;
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4777
|
+
}
|
|
4497
4778
|
return {
|
|
4498
4779
|
ns,
|
|
4499
4780
|
keys: perEntry.map((e) => e.key),
|
|
4500
4781
|
prelude,
|
|
4501
4782
|
ruleFns,
|
|
4502
4783
|
wrappers,
|
|
4784
|
+
firstSets,
|
|
4503
4785
|
deps: ruleDependencies(ruleMap),
|
|
4504
4786
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4505
|
-
|
|
4787
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4506
4788
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4507
4789
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4508
4790
|
};
|
|
4509
4791
|
}
|
|
4510
|
-
function buildInlineExpression(ctx, r,
|
|
4792
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4511
4793
|
const bodyLines = [
|
|
4512
4794
|
` let pos = _pos`,
|
|
4513
4795
|
...r.stmts,
|
|
@@ -4521,14 +4803,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4521
4803
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4522
4804
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4523
4805
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4524
|
-
const
|
|
4806
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4807
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4525
4808
|
if (!needsWrapper) return innerFn;
|
|
4526
4809
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4527
4810
|
const hoistedDecls = [
|
|
4528
4811
|
emptyTlDecl,
|
|
4812
|
+
hostReadsDecl,
|
|
4529
4813
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4530
4814
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4531
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4532
4815
|
mfDecl,
|
|
4533
4816
|
buildDecl,
|
|
4534
4817
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4556,20 +4839,36 @@ function fusedBody(pieces) {
|
|
|
4556
4839
|
}
|
|
4557
4840
|
const contributing = new Set(winner.values());
|
|
4558
4841
|
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4559
|
-
const
|
|
4842
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4560
4843
|
const lines = [
|
|
4561
4844
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4562
4845
|
"const _pfFail = {}",
|
|
4563
4846
|
"let _pfEnd",
|
|
4564
4847
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4565
|
-
...
|
|
4848
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4566
4849
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4567
4850
|
...[...contributing].flatMap((p) => p.prelude),
|
|
4568
4851
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4569
4852
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4570
4853
|
];
|
|
4571
4854
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4572
|
-
const
|
|
4855
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4856
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4857
|
+
for (const [k, p] of winner) {
|
|
4858
|
+
const fs2 = p.firstSets?.get(k);
|
|
4859
|
+
if (fs2) finalFS.set(k, fs2);
|
|
4860
|
+
}
|
|
4861
|
+
const body = rawBody.replace(
|
|
4862
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4863
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4864
|
+
// matches every well-formed placeholder.
|
|
4865
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4866
|
+
(_m, name, codevar) => {
|
|
4867
|
+
const fs2 = finalFS.get(name);
|
|
4868
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4869
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
4870
|
+
}
|
|
4871
|
+
);
|
|
4573
4872
|
const env = {};
|
|
4574
4873
|
for (const p of contributing) {
|
|
4575
4874
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -5192,7 +5491,7 @@ var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
|
|
|
5192
5491
|
if (!code.includes("parseman")) return null;
|
|
5193
5492
|
if (!code.includes("macro")) return null;
|
|
5194
5493
|
const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
|
|
5195
|
-
const result = transformMacro(code, id, moduleAliases);
|
|
5494
|
+
const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true);
|
|
5196
5495
|
if (result?.warnings.length) {
|
|
5197
5496
|
for (const w of result.warnings) {
|
|
5198
5497
|
if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
|
|
@@ -5274,7 +5573,7 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5274
5573
|
visit(root);
|
|
5275
5574
|
return found;
|
|
5276
5575
|
}
|
|
5277
|
-
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE])) {
|
|
5576
|
+
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5278
5577
|
let result;
|
|
5279
5578
|
try {
|
|
5280
5579
|
result = (0, import_oxc_parser.parseSync)(id, code);
|
|
@@ -5313,6 +5612,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5313
5612
|
const scope = /* @__PURE__ */ new Map();
|
|
5314
5613
|
const replacements = [];
|
|
5315
5614
|
const warnings = [];
|
|
5615
|
+
beginLoweringCapture();
|
|
5316
5616
|
let anyUnresolved = false;
|
|
5317
5617
|
let ruleMapHolderCounter = 0;
|
|
5318
5618
|
const lineOf = (pos) => {
|
|
@@ -5349,7 +5649,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5349
5649
|
const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
5350
5650
|
const serializePieces = (p) => {
|
|
5351
5651
|
const mapLit = (m) => `new Map([${[...m].map(([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(v2)}]`).join(", ")}])`;
|
|
5352
|
-
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},
|
|
5652
|
+
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: [] }`;
|
|
5353
5653
|
};
|
|
5354
5654
|
const serializeList = (list) => `[${list.map(serializePieces).join(", ")}]`;
|
|
5355
5655
|
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ (() => { const _g = ${grammarExpr}; Object.defineProperty(_g, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)} }); return _g })()`;
|
|
@@ -5620,6 +5920,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5620
5920
|
for (const { start, end, replacement } of [...replacements].sort((a, b) => b.start - a.start)) {
|
|
5621
5921
|
ms.overwrite(start, end, replacement);
|
|
5622
5922
|
}
|
|
5923
|
+
const unlowered = endLoweringCapture();
|
|
5924
|
+
if (warnUnloweredRegex) {
|
|
5925
|
+
for (const src of unlowered) {
|
|
5926
|
+
warnings.push(`${id}: regex ${src} did not lower to a fast charCodeAt scan (RegExp.exec fallback)`);
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5623
5929
|
return {
|
|
5624
5930
|
code: ms.toString(),
|
|
5625
5931
|
map: ms.generateMap({ hires: true }),
|