parseman 0.15.0 → 0.17.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/dist/combinators/first-set.d.ts +14 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +16 -2
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts +28 -0
- package/dist/compiler/ir-serialize.d.ts.map +1 -0
- package/dist/compiler/linker.d.ts +9 -0
- 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/index.cjs +479 -161
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +479 -161
- package/dist/index.js.map +4 -4
- package/dist/plugin/index.cjs +813 -161
- 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 +813 -161
- package/dist/plugin/index.js.map +4 -4
- package/package.json +2 -2
package/dist/plugin/index.js
CHANGED
|
@@ -91,6 +91,54 @@ function sequenceFirstSet(parsers) {
|
|
|
91
91
|
}
|
|
92
92
|
return fs2;
|
|
93
93
|
}
|
|
94
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
95
|
+
if (seen.has(p)) return any();
|
|
96
|
+
seen.add(p);
|
|
97
|
+
const fs2 = (c) => firstSetOf(c, seen);
|
|
98
|
+
const d = p._def;
|
|
99
|
+
switch (d.tag) {
|
|
100
|
+
case "literal":
|
|
101
|
+
case "regex":
|
|
102
|
+
case "keywords":
|
|
103
|
+
return p._meta.firstSet;
|
|
104
|
+
// terminals: no refs, cached set is exact
|
|
105
|
+
case "lazy":
|
|
106
|
+
try {
|
|
107
|
+
return fs2(d.thunk());
|
|
108
|
+
} catch {
|
|
109
|
+
return any();
|
|
110
|
+
}
|
|
111
|
+
case "choice": {
|
|
112
|
+
let out = empty();
|
|
113
|
+
for (const arm of d.parsers) out = union(out, fs2(arm));
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
case "sequence": {
|
|
117
|
+
let out = empty();
|
|
118
|
+
for (const term of d.parsers) {
|
|
119
|
+
out = union(out, fs2(term));
|
|
120
|
+
if (!matchesEmpty(term)) return out;
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
case "oneOrMore":
|
|
125
|
+
case "many":
|
|
126
|
+
case "optional":
|
|
127
|
+
case "transform":
|
|
128
|
+
case "label":
|
|
129
|
+
case "trivia":
|
|
130
|
+
case "node":
|
|
131
|
+
case "grammar":
|
|
132
|
+
case "expect":
|
|
133
|
+
return fs2(d.parser);
|
|
134
|
+
case "sepBy":
|
|
135
|
+
return fs2(d.parser);
|
|
136
|
+
case "skip":
|
|
137
|
+
return fs2(d.main);
|
|
138
|
+
default:
|
|
139
|
+
return p._meta.firstSet;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
94
142
|
function mergeRanges(ranges) {
|
|
95
143
|
if (ranges.length === 0) return [];
|
|
96
144
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -298,12 +346,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
298
346
|
}
|
|
299
347
|
|
|
300
348
|
// src/combinators/literal.ts
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
349
|
+
function asciiFoldEq(a, b) {
|
|
350
|
+
if (a.length !== b.length) return false;
|
|
351
|
+
for (let i = 0; i < a.length; i++) {
|
|
352
|
+
let ca = a.charCodeAt(i);
|
|
353
|
+
let cb = b.charCodeAt(i);
|
|
354
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
355
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
356
|
+
if (ca !== cb) return false;
|
|
305
357
|
}
|
|
306
|
-
return
|
|
358
|
+
return true;
|
|
307
359
|
}
|
|
308
360
|
function literal(value, opts = {}) {
|
|
309
361
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -318,8 +370,9 @@ function literal(value, opts = {}) {
|
|
|
318
370
|
const lower = value.toLowerCase();
|
|
319
371
|
const firstUpper = upper.codePointAt(0);
|
|
320
372
|
const firstLower = lower.codePointAt(0);
|
|
321
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
322
|
-
{ lo:
|
|
373
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
374
|
+
{ lo: firstLower, hi: firstLower },
|
|
375
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
323
376
|
] } : firstSet;
|
|
324
377
|
}
|
|
325
378
|
return {
|
|
@@ -332,7 +385,7 @@ function literal(value, opts = {}) {
|
|
|
332
385
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
333
386
|
}
|
|
334
387
|
const slice = input.slice(pos, end);
|
|
335
|
-
const matched = caseInsensitive ?
|
|
388
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
336
389
|
if (matched) {
|
|
337
390
|
const span = { start: pos, end };
|
|
338
391
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -1225,6 +1278,41 @@ function markUnusedValues(root) {
|
|
|
1225
1278
|
visit(resolveRoot(root), true);
|
|
1226
1279
|
}
|
|
1227
1280
|
|
|
1281
|
+
// src/combinators/parser.ts
|
|
1282
|
+
function rules(factory) {
|
|
1283
|
+
const cache = {};
|
|
1284
|
+
const proxy = new Proxy(cache, {
|
|
1285
|
+
get(target, key) {
|
|
1286
|
+
if (typeof key !== "string") return void 0;
|
|
1287
|
+
const record = target;
|
|
1288
|
+
if (!(key in record)) {
|
|
1289
|
+
const r = ref();
|
|
1290
|
+
r._ruleName = key;
|
|
1291
|
+
record[key] = r;
|
|
1292
|
+
}
|
|
1293
|
+
return record[key];
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
const definitions = factory(proxy);
|
|
1297
|
+
for (const key of Object.keys(definitions)) {
|
|
1298
|
+
const placeholder = cache[key];
|
|
1299
|
+
const parser2 = definitions[key];
|
|
1300
|
+
if (placeholder !== void 0 && typeof placeholder.define === "function") {
|
|
1301
|
+
;
|
|
1302
|
+
placeholder.define(parser2);
|
|
1303
|
+
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
1304
|
+
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
1305
|
+
} else {
|
|
1306
|
+
;
|
|
1307
|
+
cache[key] = parser2;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
for (const key of Object.keys(definitions)) {
|
|
1311
|
+
markUnusedValues(cache[key]);
|
|
1312
|
+
}
|
|
1313
|
+
return cache;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1228
1316
|
// src/combinators/not.ts
|
|
1229
1317
|
function not(combinator) {
|
|
1230
1318
|
const meta = {
|
|
@@ -1331,6 +1419,20 @@ function transform(combinator, fn) {
|
|
|
1331
1419
|
}
|
|
1332
1420
|
};
|
|
1333
1421
|
}
|
|
1422
|
+
function skip(main, skipped) {
|
|
1423
|
+
return {
|
|
1424
|
+
_tag: "skip",
|
|
1425
|
+
_meta: main._meta,
|
|
1426
|
+
_def: { tag: "skip", main, skipped },
|
|
1427
|
+
parse(input, pos, ctx) {
|
|
1428
|
+
const result = main.parse(input, pos, ctx);
|
|
1429
|
+
if (!result.ok) return result;
|
|
1430
|
+
const s = skipped.parse(input, result.span.end, ctx);
|
|
1431
|
+
if (!s.ok) return result;
|
|
1432
|
+
return { ...result, span: { start: result.span.start, end: s.span.end } };
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1334
1436
|
function trivia(combinator) {
|
|
1335
1437
|
const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
|
|
1336
1438
|
return {
|
|
@@ -1561,8 +1663,8 @@ function classToRanges(cls) {
|
|
|
1561
1663
|
return parseClassRanges(body);
|
|
1562
1664
|
}
|
|
1563
1665
|
var foldEq = (cVar, cp) => {
|
|
1564
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1565
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1666
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1667
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1566
1668
|
return `${cVar} === ${cp}`;
|
|
1567
1669
|
};
|
|
1568
1670
|
function literalCodePoints(frag) {
|
|
@@ -1813,6 +1915,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1813
1915
|
return classDisjoint(a, b);
|
|
1814
1916
|
}
|
|
1815
1917
|
function fixedClassSeq(shape) {
|
|
1918
|
+
if (shape.kind === "litFold") {
|
|
1919
|
+
return shape.open.map((cp) => {
|
|
1920
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1921
|
+
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 };
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1816
1924
|
if (shape.kind !== "seq") return null;
|
|
1817
1925
|
const out = [];
|
|
1818
1926
|
for (const p of shape.parts) {
|
|
@@ -2234,12 +2342,34 @@ function parseScanShapeCore(source) {
|
|
|
2234
2342
|
}
|
|
2235
2343
|
function scanShapeFromRegex(source, flags) {
|
|
2236
2344
|
if (/[msu]/.test(flags)) return null;
|
|
2237
|
-
if (/i/.test(flags))
|
|
2238
|
-
const open = literalCodePoints(source);
|
|
2239
|
-
return open ? { kind: "litFold", open } : null;
|
|
2240
|
-
}
|
|
2345
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2241
2346
|
return parseScanShape(source);
|
|
2242
2347
|
}
|
|
2348
|
+
function caseFoldShape(source) {
|
|
2349
|
+
const la = stripTrailingLookahead(source);
|
|
2350
|
+
if (la) {
|
|
2351
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2352
|
+
if (!inner) return null;
|
|
2353
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2354
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2355
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2356
|
+
}
|
|
2357
|
+
return caseFoldLiteralOrAlt(source);
|
|
2358
|
+
}
|
|
2359
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2360
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2361
|
+
const open = literalCodePoints(source);
|
|
2362
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2363
|
+
const arms = splitTopLevelAlternation(source);
|
|
2364
|
+
if (!arms || arms.length < 2) return null;
|
|
2365
|
+
const litArms = [];
|
|
2366
|
+
for (const arm of arms) {
|
|
2367
|
+
const cp = literalCodePoints(arm);
|
|
2368
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2369
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2370
|
+
}
|
|
2371
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2372
|
+
}
|
|
2243
2373
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2244
2374
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2245
2375
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2251,6 +2381,25 @@ function stringHardStop(shape) {
|
|
|
2251
2381
|
);
|
|
2252
2382
|
}
|
|
2253
2383
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2384
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2385
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2386
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2387
|
+
function planAltSwitch(firsts) {
|
|
2388
|
+
const cases = [];
|
|
2389
|
+
let total = 0;
|
|
2390
|
+
for (const fs2 of firsts) {
|
|
2391
|
+
if (!fs2 || fs2.negated) return { kind: "if" };
|
|
2392
|
+
const pts = [];
|
|
2393
|
+
for (const [lo, hi] of fs2.ranges) {
|
|
2394
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2395
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2396
|
+
}
|
|
2397
|
+
total += pts.length;
|
|
2398
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2399
|
+
cases.push(pts);
|
|
2400
|
+
}
|
|
2401
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2402
|
+
}
|
|
2254
2403
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2255
2404
|
if (shape.kind === "lookahead") {
|
|
2256
2405
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2275,18 +2424,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2275
2424
|
const okV = mint("_ok");
|
|
2276
2425
|
const endV2 = mint("_end");
|
|
2277
2426
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2427
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2428
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2429
|
+
for (const a of shape.arms) {
|
|
2430
|
+
const open = a.open;
|
|
2431
|
+
const f = open[0];
|
|
2432
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2433
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2434
|
+
}
|
|
2435
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2436
|
+
for (const [lower, arms] of groups) {
|
|
2437
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2438
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2439
|
+
for (const open of arms) {
|
|
2440
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2441
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2442
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2443
|
+
}
|
|
2444
|
+
lines.push(`${ind2} break`);
|
|
2445
|
+
lines.push(`${ind2} }`);
|
|
2446
|
+
}
|
|
2447
|
+
lines.push(`${ind2}}`);
|
|
2448
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2449
|
+
}
|
|
2278
2450
|
if (shape.disjoint) {
|
|
2279
|
-
const
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2451
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2452
|
+
if (plan.kind === "switch") {
|
|
2453
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2454
|
+
shape.arms.forEach((arm, k) => {
|
|
2455
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2456
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2457
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2458
|
+
lines.push(...m.setup);
|
|
2459
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2460
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2461
|
+
lines.push(`${ind2} break`);
|
|
2462
|
+
lines.push(`${ind2} }`);
|
|
2463
|
+
});
|
|
2288
2464
|
lines.push(`${ind2}}`);
|
|
2289
|
-
}
|
|
2465
|
+
} else {
|
|
2466
|
+
const c0 = codeAt(start, firstChar);
|
|
2467
|
+
shape.arms.forEach((arm, k) => {
|
|
2468
|
+
const fs2 = shape.firsts[k];
|
|
2469
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2470
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2471
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2472
|
+
lines.push(...m.setup);
|
|
2473
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2474
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2475
|
+
lines.push(`${ind2}}`);
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2290
2478
|
} else {
|
|
2291
2479
|
const lbl = mint("_alt");
|
|
2292
2480
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2774,6 +2962,16 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2774
2962
|
}
|
|
2775
2963
|
|
|
2776
2964
|
// src/compiler/codegen.ts
|
|
2965
|
+
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 }";
|
|
2966
|
+
var _loweringSink = null;
|
|
2967
|
+
function beginLoweringCapture() {
|
|
2968
|
+
_loweringSink = /* @__PURE__ */ new Set();
|
|
2969
|
+
}
|
|
2970
|
+
function endLoweringCapture() {
|
|
2971
|
+
const misses = _loweringSink ? [..._loweringSink] : [];
|
|
2972
|
+
_loweringSink = null;
|
|
2973
|
+
return misses;
|
|
2974
|
+
}
|
|
2777
2975
|
function v(ctx, prefix = "_v") {
|
|
2778
2976
|
return `${prefix}${ctx.vars++}`;
|
|
2779
2977
|
}
|
|
@@ -2863,16 +3061,12 @@ function namedFnPrelude() {
|
|
|
2863
3061
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
2864
3062
|
ctx.namedFnDecls.push([
|
|
2865
3063
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
2866
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
2867
3064
|
` _pfail: {`,
|
|
2868
3065
|
...reindentStmts(bodyStmts, 2),
|
|
2869
|
-
`
|
|
2870
|
-
`
|
|
2871
|
-
` _pfok = true`,
|
|
3066
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3067
|
+
` return ${valueVar}`,
|
|
2872
3068
|
` }`,
|
|
2873
|
-
`
|
|
2874
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
2875
|
-
` return _pfv`,
|
|
3069
|
+
` return ${NAMED_FN_FAIL}`,
|
|
2876
3070
|
`}`
|
|
2877
3071
|
].join("\n"));
|
|
2878
3072
|
}
|
|
@@ -3172,13 +3366,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3172
3366
|
const vv = v(ctx);
|
|
3173
3367
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3174
3368
|
const stmts = [];
|
|
3175
|
-
if (caseInsensitive) {
|
|
3176
|
-
|
|
3369
|
+
if (caseInsensitive && len > 0) {
|
|
3370
|
+
const match = Array.from(
|
|
3371
|
+
{ length: len },
|
|
3372
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3373
|
+
).join(" && ");
|
|
3177
3374
|
stmts.push(
|
|
3178
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3179
|
-
`${ind(ctx)}const ${vv}
|
|
3180
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3181
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3375
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3376
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3182
3377
|
);
|
|
3183
3378
|
} else if (len === 0) {
|
|
3184
3379
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3299,6 +3494,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3299
3494
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3300
3495
|
}
|
|
3301
3496
|
}
|
|
3497
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3302
3498
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3303
3499
|
const key = `${def.source}/${flags}`;
|
|
3304
3500
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3571,11 +3767,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3571
3767
|
const atStart = failsAtStart(p);
|
|
3572
3768
|
const staticFx = armStaticExpected(ctx, p);
|
|
3573
3769
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3574
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3770
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3771
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3772
|
+
const deep = firstSetOf(p);
|
|
3773
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3774
|
+
})();
|
|
3575
3775
|
let gateCond = null;
|
|
3576
3776
|
if (gate) {
|
|
3577
3777
|
const gateIdx = ctx.mapFns.length;
|
|
3578
3778
|
ctx.mapFns.push(gate);
|
|
3779
|
+
ctx.mapFnSrcs.push(null);
|
|
3579
3780
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3580
3781
|
}
|
|
3581
3782
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3634,13 +3835,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3634
3835
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3635
3836
|
return emitFirstMatch(def, ctx, pos);
|
|
3636
3837
|
}
|
|
3838
|
+
function pushMapFn(ctx, fn, src) {
|
|
3839
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3840
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3841
|
+
if (hit !== -1) return hit;
|
|
3842
|
+
}
|
|
3843
|
+
const idx = ctx.mapFns.length;
|
|
3844
|
+
ctx.mapFns.push(fn);
|
|
3845
|
+
ctx.mapFnSrcs.push(src);
|
|
3846
|
+
return idx;
|
|
3847
|
+
}
|
|
3637
3848
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3638
3849
|
const def = p._def;
|
|
3639
3850
|
if (def.tag === "transform") {
|
|
3640
3851
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3641
|
-
const fnIdx = ctx.
|
|
3642
|
-
ctx.mapFns.push(def.fn);
|
|
3643
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3852
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3644
3853
|
const vv = v(ctx);
|
|
3645
3854
|
return {
|
|
3646
3855
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3823,7 +4032,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3823
4032
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3824
4033
|
];
|
|
3825
4034
|
ctx.indent++;
|
|
3826
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4035
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3827
4036
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3828
4037
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3829
4038
|
if (def.skip.length > 0) {
|
|
@@ -3879,15 +4088,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3879
4088
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3880
4089
|
}
|
|
3881
4090
|
const i = ind(ctx);
|
|
3882
|
-
const capturesTrivia = mkType !== null || structural
|
|
3883
|
-
const clonesState = structural
|
|
4091
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4092
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3884
4093
|
const chV = v(ctx, "_ch");
|
|
3885
4094
|
const rawV = v(ctx, "_raw");
|
|
3886
|
-
const
|
|
4095
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4096
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4097
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3887
4098
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3888
4099
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3889
|
-
|
|
3890
|
-
const
|
|
4100
|
+
if (structural) ctx.needsHostReads = true;
|
|
4101
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4102
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4103
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3891
4104
|
const stmts = [
|
|
3892
4105
|
allocStmt,
|
|
3893
4106
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3898,7 +4111,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3898
4111
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3899
4112
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3900
4113
|
let stV = "undefined";
|
|
3901
|
-
if (
|
|
4114
|
+
if (structural) {
|
|
4115
|
+
stV = v(ctx, "_nst");
|
|
4116
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4117
|
+
} else if (clonesState) {
|
|
3902
4118
|
stV = v(ctx, "_nst");
|
|
3903
4119
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3904
4120
|
}
|
|
@@ -4007,6 +4223,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4007
4223
|
return { stmts, valueVar: valVar, endVar };
|
|
4008
4224
|
}
|
|
4009
4225
|
function emit(p, ctx, pos) {
|
|
4226
|
+
const usage = ctx.lazyUsage;
|
|
4227
|
+
if (usage && !ctx.noHoist && !ctx.capAsTrivia && !ctx.ruleNames?.has(p) && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && // Only hoist subtrees big enough that de-duplicating them beats the call
|
|
4228
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4229
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4230
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4231
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4232
|
+
const existing = ctx.namedParsers.get(p);
|
|
4233
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4234
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4235
|
+
ctx.namedParsers.set(p, fnName);
|
|
4236
|
+
const savedIndent = ctx.indent;
|
|
4237
|
+
const savedFailLabel = ctx.failLabel;
|
|
4238
|
+
const savedRecord = ctx.recordFail;
|
|
4239
|
+
ctx.indent = 1;
|
|
4240
|
+
ctx.failLabel = "_pfail";
|
|
4241
|
+
ctx.recordFail = true;
|
|
4242
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4243
|
+
ctx.indent = savedIndent;
|
|
4244
|
+
ctx.failLabel = savedFailLabel;
|
|
4245
|
+
ctx.recordFail = savedRecord;
|
|
4246
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4247
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4248
|
+
}
|
|
4249
|
+
return emitDispatch(p, ctx, pos);
|
|
4250
|
+
}
|
|
4251
|
+
function emitDispatch(p, ctx, pos) {
|
|
4010
4252
|
const def = p._def;
|
|
4011
4253
|
switch (def.tag) {
|
|
4012
4254
|
case "literal":
|
|
@@ -4052,9 +4294,7 @@ function emit(p, ctx, pos) {
|
|
|
4052
4294
|
};
|
|
4053
4295
|
}
|
|
4054
4296
|
}
|
|
4055
|
-
const fnIdx = ctx.
|
|
4056
|
-
ctx.mapFns.push(def.fn);
|
|
4057
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4297
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4058
4298
|
const mv = v(ctx, "_mapped");
|
|
4059
4299
|
return {
|
|
4060
4300
|
stmts: [
|
|
@@ -4119,6 +4359,7 @@ function emit(p, ctx, pos) {
|
|
|
4119
4359
|
case "guard": {
|
|
4120
4360
|
const fnIdx = ctx.mapFns.length;
|
|
4121
4361
|
ctx.mapFns.push(def.predicate);
|
|
4362
|
+
ctx.mapFnSrcs.push(null);
|
|
4122
4363
|
const vv = v(ctx);
|
|
4123
4364
|
return {
|
|
4124
4365
|
stmts: [
|
|
@@ -4133,6 +4374,7 @@ function emit(p, ctx, pos) {
|
|
|
4133
4374
|
const evIdx = ctx.mapFns.length;
|
|
4134
4375
|
const extra = def.extra;
|
|
4135
4376
|
ctx.mapFns.push((() => extra));
|
|
4377
|
+
ctx.mapFnSrcs.push(null);
|
|
4136
4378
|
const innerParser = def.parser;
|
|
4137
4379
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4138
4380
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4252,9 +4494,58 @@ function childrenOf(def) {
|
|
|
4252
4494
|
return [];
|
|
4253
4495
|
}
|
|
4254
4496
|
}
|
|
4497
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4498
|
+
"sequence",
|
|
4499
|
+
"choice",
|
|
4500
|
+
"many",
|
|
4501
|
+
"oneOrMore",
|
|
4502
|
+
"optional",
|
|
4503
|
+
"sepBy",
|
|
4504
|
+
"transform",
|
|
4505
|
+
"node",
|
|
4506
|
+
"not"
|
|
4507
|
+
]);
|
|
4508
|
+
function isHoistableTag(tag) {
|
|
4509
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4510
|
+
}
|
|
4511
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4255
4512
|
function analyzeLazyUsage(root) {
|
|
4256
4513
|
return analyzeLazyUsageMulti([root]);
|
|
4257
4514
|
}
|
|
4515
|
+
function subtreeSizes(roots) {
|
|
4516
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4517
|
+
const sz = (p) => {
|
|
4518
|
+
const cached = sizes.get(p);
|
|
4519
|
+
if (cached !== void 0) return cached;
|
|
4520
|
+
const def = p._def;
|
|
4521
|
+
let s = 1;
|
|
4522
|
+
if (def.tag !== "lazy") {
|
|
4523
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4524
|
+
}
|
|
4525
|
+
sizes.set(p, s);
|
|
4526
|
+
return s;
|
|
4527
|
+
};
|
|
4528
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4529
|
+
const visit = (p) => {
|
|
4530
|
+
const def = p._def;
|
|
4531
|
+
if (def.tag === "lazy") {
|
|
4532
|
+
if (visitedLazy.has(p)) return;
|
|
4533
|
+
visitedLazy.add(p);
|
|
4534
|
+
let resolved;
|
|
4535
|
+
try {
|
|
4536
|
+
resolved = def.thunk();
|
|
4537
|
+
} catch {
|
|
4538
|
+
return;
|
|
4539
|
+
}
|
|
4540
|
+
visit(resolved);
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
sz(p);
|
|
4544
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4545
|
+
};
|
|
4546
|
+
for (const root of roots) visit(root);
|
|
4547
|
+
return sizes;
|
|
4548
|
+
}
|
|
4258
4549
|
function analyzeLazyUsageMulti(roots) {
|
|
4259
4550
|
const counts = /* @__PURE__ */ new Map();
|
|
4260
4551
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4281,10 +4572,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4281
4572
|
active.delete(p);
|
|
4282
4573
|
return;
|
|
4283
4574
|
}
|
|
4575
|
+
if (isHoistableTag(def.tag)) {
|
|
4576
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4577
|
+
}
|
|
4284
4578
|
for (const child of childrenOf(def)) walk2(child);
|
|
4285
4579
|
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4580
|
+
const rootsArr = [...roots];
|
|
4581
|
+
for (const root of rootsArr) walk2(root);
|
|
4582
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4288
4583
|
}
|
|
4289
4584
|
function ruleDependencies(ruleMap) {
|
|
4290
4585
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4334,7 +4629,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4334
4629
|
buildFns: [],
|
|
4335
4630
|
buildSrcs: [],
|
|
4336
4631
|
runtimeParsers: [],
|
|
4337
|
-
needsCollator: false,
|
|
4338
4632
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4339
4633
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4340
4634
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4343,18 +4637,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4343
4637
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4344
4638
|
};
|
|
4345
4639
|
const r = emit(combinator, ctx, "_pos");
|
|
4346
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4347
|
-
` : "";
|
|
4348
4640
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4349
4641
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4642
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4350
4643
|
const source = [
|
|
4351
4644
|
...emptyTlDecls,
|
|
4645
|
+
...hostReadsDecls,
|
|
4352
4646
|
...ctx.regexDecls,
|
|
4353
4647
|
...ctx.expectedDecls,
|
|
4354
4648
|
"",
|
|
4355
4649
|
...namedPrelude,
|
|
4356
4650
|
ctx.namedFnDecls.join("\n\n"),
|
|
4357
|
-
|
|
4651
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4358
4652
|
` let pos = _pos`,
|
|
4359
4653
|
...r.stmts,
|
|
4360
4654
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4362,9 +4656,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4362
4656
|
].join("\n");
|
|
4363
4657
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4364
4658
|
...emptyTlDecls,
|
|
4659
|
+
...hostReadsDecls,
|
|
4365
4660
|
...ctx.regexDecls,
|
|
4366
4661
|
...ctx.expectedDecls,
|
|
4367
|
-
collatorDecl,
|
|
4368
4662
|
...namedPrelude,
|
|
4369
4663
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4370
4664
|
`let pos = _pos`,
|
|
@@ -4381,7 +4675,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4381
4675
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4382
4676
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4383
4677
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4384
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4678
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4385
4679
|
return {
|
|
4386
4680
|
source,
|
|
4387
4681
|
inlineExpression,
|
|
@@ -4426,7 +4720,6 @@ function compileRuleMap(ruleMap) {
|
|
|
4426
4720
|
buildFns: [],
|
|
4427
4721
|
buildSrcs: [],
|
|
4428
4722
|
runtimeParsers: [],
|
|
4429
|
-
needsCollator: false,
|
|
4430
4723
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4431
4724
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4432
4725
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4445,8 +4738,6 @@ function compileRuleMap(ruleMap) {
|
|
|
4445
4738
|
}
|
|
4446
4739
|
ctx.ruleNames = ruleNames;
|
|
4447
4740
|
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
4448
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4449
|
-
` : "";
|
|
4450
4741
|
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
4451
4742
|
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
4452
4743
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
@@ -4458,9 +4749,9 @@ function compileRuleMap(ruleMap) {
|
|
|
4458
4749
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4459
4750
|
const hoistedDecls = [
|
|
4460
4751
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
4752
|
+
ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
|
|
4461
4753
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4462
4754
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4463
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4464
4755
|
mfDecl,
|
|
4465
4756
|
buildDecl,
|
|
4466
4757
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4513,7 +4804,6 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4513
4804
|
buildFns: [],
|
|
4514
4805
|
buildSrcs: [],
|
|
4515
4806
|
runtimeParsers: [],
|
|
4516
|
-
needsCollator: false,
|
|
4517
4807
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4518
4808
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4519
4809
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4644,12 +4934,12 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4644
4934
|
firstSets,
|
|
4645
4935
|
deps: ruleDependencies(ruleMap),
|
|
4646
4936
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4647
|
-
|
|
4937
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4648
4938
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4649
4939
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4650
4940
|
};
|
|
4651
4941
|
}
|
|
4652
|
-
function buildInlineExpression(ctx, r,
|
|
4942
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4653
4943
|
const bodyLines = [
|
|
4654
4944
|
` let pos = _pos`,
|
|
4655
4945
|
...r.stmts,
|
|
@@ -4663,14 +4953,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4663
4953
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4664
4954
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4665
4955
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4666
|
-
const
|
|
4956
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4957
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4667
4958
|
if (!needsWrapper) return innerFn;
|
|
4668
4959
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4669
4960
|
const hoistedDecls = [
|
|
4670
4961
|
emptyTlDecl,
|
|
4962
|
+
hostReadsDecl,
|
|
4671
4963
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4672
4964
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4673
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4674
4965
|
mfDecl,
|
|
4675
4966
|
buildDecl,
|
|
4676
4967
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4687,64 +4978,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4687
4978
|
].join("\n");
|
|
4688
4979
|
}
|
|
4689
4980
|
|
|
4690
|
-
// src/compiler/linker.ts
|
|
4691
|
-
function fusedBody(pieces) {
|
|
4692
|
-
const winner = /* @__PURE__ */ new Map();
|
|
4693
|
-
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4694
|
-
for (const [k, p] of winner) {
|
|
4695
|
-
for (const d of p.deps.get(k) ?? []) {
|
|
4696
|
-
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4697
|
-
}
|
|
4698
|
-
}
|
|
4699
|
-
const contributing = new Set(winner.values());
|
|
4700
|
-
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4701
|
-
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4702
|
-
const lines = [
|
|
4703
|
-
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4704
|
-
"const _pfFail = {}",
|
|
4705
|
-
"let _pfEnd",
|
|
4706
|
-
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4707
|
-
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4708
|
-
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4709
|
-
...[...contributing].flatMap((p) => p.prelude),
|
|
4710
|
-
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4711
|
-
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4712
|
-
];
|
|
4713
|
-
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4714
|
-
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4715
|
-
const finalFS = /* @__PURE__ */ new Map();
|
|
4716
|
-
for (const [k, p] of winner) {
|
|
4717
|
-
const fs2 = p.firstSets?.get(k);
|
|
4718
|
-
if (fs2) finalFS.set(k, fs2);
|
|
4719
|
-
}
|
|
4720
|
-
const body = rawBody.replace(
|
|
4721
|
-
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4722
|
-
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4723
|
-
// matches every well-formed placeholder.
|
|
4724
|
-
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4725
|
-
(_m, name, codevar) => {
|
|
4726
|
-
const fs2 = finalFS.get(name);
|
|
4727
|
-
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4728
|
-
return `(${firstSetCond(codevar, fs2)})`;
|
|
4729
|
-
}
|
|
4730
|
-
);
|
|
4731
|
-
const env = {};
|
|
4732
|
-
for (const p of contributing) {
|
|
4733
|
-
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4734
|
-
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4735
|
-
}
|
|
4736
|
-
return { body, env };
|
|
4737
|
-
}
|
|
4738
|
-
function emitFusedSource(pieces) {
|
|
4739
|
-
const { body, env } = fusedBody(pieces);
|
|
4740
|
-
if (Object.keys(env).length > 0) {
|
|
4741
|
-
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4742
|
-
}
|
|
4743
|
-
return `/* @__PURE__ */ (() => {
|
|
4744
|
-
${body}
|
|
4745
|
-
})()`;
|
|
4746
|
-
}
|
|
4747
|
-
|
|
4748
4981
|
// src/combinators/scanTo.ts
|
|
4749
4982
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4750
4983
|
const meta = {
|
|
@@ -4839,6 +5072,354 @@ function escapeClassChar(ch) {
|
|
|
4839
5072
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4840
5073
|
}
|
|
4841
5074
|
|
|
5075
|
+
// src/compiler/ir-serialize.ts
|
|
5076
|
+
function childrenOf2(def) {
|
|
5077
|
+
switch (def.tag) {
|
|
5078
|
+
case "sequence":
|
|
5079
|
+
case "choice":
|
|
5080
|
+
return def.parsers;
|
|
5081
|
+
case "many":
|
|
5082
|
+
case "oneOrMore":
|
|
5083
|
+
case "optional":
|
|
5084
|
+
case "transform":
|
|
5085
|
+
case "trivia":
|
|
5086
|
+
case "label":
|
|
5087
|
+
case "not":
|
|
5088
|
+
case "node":
|
|
5089
|
+
case "expect":
|
|
5090
|
+
return [def.parser];
|
|
5091
|
+
case "grammar":
|
|
5092
|
+
return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
|
|
5093
|
+
case "sepBy":
|
|
5094
|
+
return [def.parser, def.separator];
|
|
5095
|
+
case "skip":
|
|
5096
|
+
return [def.main, def.skipped];
|
|
5097
|
+
case "scanTo":
|
|
5098
|
+
return [def.sentinel, ...def.skip];
|
|
5099
|
+
case "lazy":
|
|
5100
|
+
case "literal":
|
|
5101
|
+
case "regex":
|
|
5102
|
+
case "keywords":
|
|
5103
|
+
case "guard":
|
|
5104
|
+
case "withCtx":
|
|
5105
|
+
case "recover":
|
|
5106
|
+
case "unknown":
|
|
5107
|
+
return [];
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
var ruleNameOf = (c) => c._ruleName;
|
|
5111
|
+
function lazyTarget(c) {
|
|
5112
|
+
if (c._def.tag !== "lazy") return null;
|
|
5113
|
+
try {
|
|
5114
|
+
return c._def.thunk();
|
|
5115
|
+
} catch {
|
|
5116
|
+
return null;
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
var Unserializable = class extends Error {
|
|
5120
|
+
};
|
|
5121
|
+
function serializeRuleMap(ruleMap) {
|
|
5122
|
+
try {
|
|
5123
|
+
return new Serializer(ruleMap).run();
|
|
5124
|
+
} catch (e) {
|
|
5125
|
+
if (e instanceof Unserializable) {
|
|
5126
|
+
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
5127
|
+
return null;
|
|
5128
|
+
}
|
|
5129
|
+
throw e;
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
function evalRuleMapIR(ir) {
|
|
5133
|
+
const _tf = (child, src) => {
|
|
5134
|
+
let fn2;
|
|
5135
|
+
try {
|
|
5136
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5137
|
+
} catch {
|
|
5138
|
+
fn2 = () => {
|
|
5139
|
+
throw new Error("IR transform fn not materialized");
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
5142
|
+
const t = transform(child, fn2);
|
|
5143
|
+
t._def.fnSrc = src;
|
|
5144
|
+
return t;
|
|
5145
|
+
};
|
|
5146
|
+
const _nd = (type, child, src, opts) => {
|
|
5147
|
+
const n = node(type, child, void 0, opts);
|
|
5148
|
+
n._def.buildSrc = src;
|
|
5149
|
+
return n;
|
|
5150
|
+
};
|
|
5151
|
+
const fn = new Function(
|
|
5152
|
+
"rules",
|
|
5153
|
+
"ref",
|
|
5154
|
+
"regex",
|
|
5155
|
+
"literal",
|
|
5156
|
+
"keywords",
|
|
5157
|
+
"sequence",
|
|
5158
|
+
"choice",
|
|
5159
|
+
"many",
|
|
5160
|
+
"oneOrMore",
|
|
5161
|
+
"optional",
|
|
5162
|
+
"sepBy",
|
|
5163
|
+
"not",
|
|
5164
|
+
"node",
|
|
5165
|
+
"parser",
|
|
5166
|
+
"scanTo",
|
|
5167
|
+
"transform",
|
|
5168
|
+
"skip",
|
|
5169
|
+
"trivia",
|
|
5170
|
+
"label",
|
|
5171
|
+
"expect",
|
|
5172
|
+
"_tf",
|
|
5173
|
+
"_nd",
|
|
5174
|
+
`return (${ir})`
|
|
5175
|
+
);
|
|
5176
|
+
const map = fn(
|
|
5177
|
+
rules,
|
|
5178
|
+
ref,
|
|
5179
|
+
regex,
|
|
5180
|
+
literal,
|
|
5181
|
+
keywords,
|
|
5182
|
+
sequence,
|
|
5183
|
+
choice,
|
|
5184
|
+
many,
|
|
5185
|
+
oneOrMore,
|
|
5186
|
+
optional,
|
|
5187
|
+
sepBy,
|
|
5188
|
+
not,
|
|
5189
|
+
node,
|
|
5190
|
+
parser,
|
|
5191
|
+
scanTo,
|
|
5192
|
+
transform,
|
|
5193
|
+
skip,
|
|
5194
|
+
trivia,
|
|
5195
|
+
label,
|
|
5196
|
+
expect,
|
|
5197
|
+
_tf,
|
|
5198
|
+
_nd
|
|
5199
|
+
);
|
|
5200
|
+
return Object.entries(map);
|
|
5201
|
+
}
|
|
5202
|
+
var Serializer = class {
|
|
5203
|
+
counts = /* @__PURE__ */ new Map();
|
|
5204
|
+
selfRef = /* @__PURE__ */ new Set();
|
|
5205
|
+
// combinators reached by an unnamed lazy pointing back into their own subtree
|
|
5206
|
+
constName = /* @__PURE__ */ new Map();
|
|
5207
|
+
emitted = /* @__PURE__ */ new Set();
|
|
5208
|
+
decls = [];
|
|
5209
|
+
ruleValues = /* @__PURE__ */ new Set();
|
|
5210
|
+
ruleMap;
|
|
5211
|
+
constructor(ruleMap) {
|
|
5212
|
+
this.ruleMap = ruleMap;
|
|
5213
|
+
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
5214
|
+
}
|
|
5215
|
+
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
5216
|
+
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
5217
|
+
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
5218
|
+
body(c) {
|
|
5219
|
+
return c._def.tag === "lazy" && ruleNameOf(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
5220
|
+
}
|
|
5221
|
+
run() {
|
|
5222
|
+
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
5223
|
+
let n = 0;
|
|
5224
|
+
for (const [c, count] of this.counts) {
|
|
5225
|
+
if (this.ruleValues.has(c)) continue;
|
|
5226
|
+
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
5227
|
+
}
|
|
5228
|
+
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
5229
|
+
if (this.decls.length === 0) return `rules((g) => ({
|
|
5230
|
+
${body}
|
|
5231
|
+
}))`;
|
|
5232
|
+
return `rules((g) => {
|
|
5233
|
+
${this.decls.map((d) => " " + d).join("\n")}
|
|
5234
|
+
return ({
|
|
5235
|
+
${body}
|
|
5236
|
+
})
|
|
5237
|
+
})`;
|
|
5238
|
+
}
|
|
5239
|
+
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
5240
|
+
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
5241
|
+
analyze(c, active) {
|
|
5242
|
+
const def = c._def;
|
|
5243
|
+
if (def.tag === "lazy") {
|
|
5244
|
+
if (ruleNameOf(c) !== void 0) return;
|
|
5245
|
+
const target = lazyTarget(c);
|
|
5246
|
+
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
5247
|
+
if (active.has(target)) {
|
|
5248
|
+
this.selfRef.add(target);
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
5251
|
+
this.analyze(target, active);
|
|
5252
|
+
return;
|
|
5253
|
+
}
|
|
5254
|
+
if (def.tag === "guard" || def.tag === "withCtx" || def.tag === "recover" || def.tag === "unknown") {
|
|
5255
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5256
|
+
}
|
|
5257
|
+
this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
|
|
5258
|
+
if ((this.counts.get(c) ?? 0) > 1) return;
|
|
5259
|
+
active.add(c);
|
|
5260
|
+
for (const child of childrenOf2(def)) this.analyze(child, active);
|
|
5261
|
+
active.delete(c);
|
|
5262
|
+
}
|
|
5263
|
+
/** Reference `c`: a const var if it has one (emitting its decl first), else inline.
|
|
5264
|
+
* `selfOf` is the const currently being wrapped in a ref()/define closure — an
|
|
5265
|
+
* unnamed lazy back into it emits the local ref var instead of recursing. */
|
|
5266
|
+
ref(c, selfOf) {
|
|
5267
|
+
const cn = this.constName.get(c);
|
|
5268
|
+
if (cn) {
|
|
5269
|
+
this.emitDecl(c);
|
|
5270
|
+
return cn;
|
|
5271
|
+
}
|
|
5272
|
+
return this.expr(c, selfOf);
|
|
5273
|
+
}
|
|
5274
|
+
emitDecl(c) {
|
|
5275
|
+
const name = this.constName.get(c);
|
|
5276
|
+
if (this.emitted.has(name)) return;
|
|
5277
|
+
this.emitted.add(name);
|
|
5278
|
+
this.decls.push(`const ${name} = ${this.wrap(c)}`);
|
|
5279
|
+
}
|
|
5280
|
+
selfN = 0;
|
|
5281
|
+
/** Emit `c`'s own constructor call, wrapping it in a `ref()`+`.define()` closure
|
|
5282
|
+
* if it is self-referential (an unnamed lazy in its subtree points back to it) —
|
|
5283
|
+
* the shape `balanced()` and other recursive library combinators build. */
|
|
5284
|
+
wrap(c) {
|
|
5285
|
+
if (!this.selfRef.has(c)) return this.expr(c, null);
|
|
5286
|
+
const rv = `_rr${this.selfN++}`;
|
|
5287
|
+
const bodyExpr = this.expr(c, { comb: c, var: rv });
|
|
5288
|
+
return `(() => { const ${rv} = ref(); const _b = ${bodyExpr}; ${rv}.define(_b); return _b })()`;
|
|
5289
|
+
}
|
|
5290
|
+
/** Emit the constructor-call source for `c` (never a const shortcut for `c` itself). */
|
|
5291
|
+
expr(c, selfOf) {
|
|
5292
|
+
const def = c._def;
|
|
5293
|
+
const kid = (x) => this.ref(x, selfOf);
|
|
5294
|
+
switch (def.tag) {
|
|
5295
|
+
case "lazy": {
|
|
5296
|
+
if (ruleNameOf(c) !== void 0) return `g[${JSON.stringify(ruleNameOf(c))}]`;
|
|
5297
|
+
const target = lazyTarget(c);
|
|
5298
|
+
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
5299
|
+
return this.ref(target, selfOf);
|
|
5300
|
+
}
|
|
5301
|
+
case "literal":
|
|
5302
|
+
return `literal(${JSON.stringify(def.value)}${def.caseInsensitive ? ", { caseInsensitive: true }" : ""})`;
|
|
5303
|
+
case "regex":
|
|
5304
|
+
return `regex(${JSON.stringify(def.source)}, ${JSON.stringify(def.flags)})`;
|
|
5305
|
+
case "keywords":
|
|
5306
|
+
return `keywords(${JSON.stringify(def.words)}, { caseInsensitive: ${def.caseInsensitive}${def.boundary !== void 0 ? `, boundary: ${JSON.stringify(def.boundary)}` : ""} })`;
|
|
5307
|
+
case "sequence":
|
|
5308
|
+
return `sequence(${def.parsers.map(kid).join(", ")})`;
|
|
5309
|
+
case "choice": {
|
|
5310
|
+
if (def.gates.some((g) => g !== null)) throw new Unserializable("choice with a gate() has no source");
|
|
5311
|
+
return `choice(${def.parsers.map(kid).join(", ")})`;
|
|
5312
|
+
}
|
|
5313
|
+
case "many":
|
|
5314
|
+
return `many(${kid(def.parser)})`;
|
|
5315
|
+
case "oneOrMore":
|
|
5316
|
+
return `oneOrMore(${kid(def.parser)})`;
|
|
5317
|
+
case "optional":
|
|
5318
|
+
return `optional(${kid(def.parser)})`;
|
|
5319
|
+
case "sepBy":
|
|
5320
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
5321
|
+
case "not":
|
|
5322
|
+
return `not(${kid(def.parser)})`;
|
|
5323
|
+
case "trivia":
|
|
5324
|
+
return `trivia(${kid(def.parser)})`;
|
|
5325
|
+
case "label":
|
|
5326
|
+
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
5327
|
+
case "expect":
|
|
5328
|
+
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
5329
|
+
case "skip":
|
|
5330
|
+
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
5331
|
+
case "scanTo":
|
|
5332
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
5333
|
+
case "transform": {
|
|
5334
|
+
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
5335
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
5336
|
+
}
|
|
5337
|
+
case "node": {
|
|
5338
|
+
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
5339
|
+
const opts = def.collapse ? `, { collapse: true }` : "";
|
|
5340
|
+
if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
|
|
5341
|
+
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
5342
|
+
}
|
|
5343
|
+
case "grammar": {
|
|
5344
|
+
const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
|
|
5345
|
+
return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
|
|
5346
|
+
}
|
|
5347
|
+
case "guard":
|
|
5348
|
+
case "withCtx":
|
|
5349
|
+
case "recover":
|
|
5350
|
+
case "unknown":
|
|
5351
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
};
|
|
5355
|
+
|
|
5356
|
+
// src/compiler/linker.ts
|
|
5357
|
+
function fusedBody(pieces) {
|
|
5358
|
+
const winner = /* @__PURE__ */ new Map();
|
|
5359
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
5360
|
+
for (const [k, p] of winner) {
|
|
5361
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
5362
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
const contributing = new Set(winner.values());
|
|
5366
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
5367
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
5368
|
+
const lines = [
|
|
5369
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
5370
|
+
"const _pfFail = {}",
|
|
5371
|
+
"let _pfEnd",
|
|
5372
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
5373
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
5374
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
5375
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
5376
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
5377
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
5378
|
+
];
|
|
5379
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
5380
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
5381
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
5382
|
+
for (const [k, p] of winner) {
|
|
5383
|
+
const fs2 = p.firstSets?.get(k);
|
|
5384
|
+
if (fs2) finalFS.set(k, fs2);
|
|
5385
|
+
}
|
|
5386
|
+
const body = rawBody.replace(
|
|
5387
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
5388
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
5389
|
+
// matches every well-formed placeholder.
|
|
5390
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
5391
|
+
(_m, name, codevar) => {
|
|
5392
|
+
const fs2 = finalFS.get(name);
|
|
5393
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
5394
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
5395
|
+
}
|
|
5396
|
+
);
|
|
5397
|
+
const env = {};
|
|
5398
|
+
for (const p of contributing) {
|
|
5399
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
5400
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
5401
|
+
}
|
|
5402
|
+
return { body, env };
|
|
5403
|
+
}
|
|
5404
|
+
function emitFusedSource(pieces) {
|
|
5405
|
+
const { body, env } = fusedBody(pieces);
|
|
5406
|
+
if (Object.keys(env).length > 0) {
|
|
5407
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
5408
|
+
}
|
|
5409
|
+
return `/* @__PURE__ */ (() => {
|
|
5410
|
+
${body}
|
|
5411
|
+
})()`;
|
|
5412
|
+
}
|
|
5413
|
+
function isIRPiece(p) {
|
|
5414
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5415
|
+
}
|
|
5416
|
+
function materializePiece(p) {
|
|
5417
|
+
if (!isIRPiece(p)) return p;
|
|
5418
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5419
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5420
|
+
return pieces;
|
|
5421
|
+
}
|
|
5422
|
+
|
|
4842
5423
|
// src/cst/offset-model.ts
|
|
4843
5424
|
var { isFinite } = Number;
|
|
4844
5425
|
|
|
@@ -5350,7 +5931,7 @@ var plugin_default = createUnplugin((opts = {}) => ({
|
|
|
5350
5931
|
if (!code.includes("parseman")) return null;
|
|
5351
5932
|
if (!code.includes("macro")) return null;
|
|
5352
5933
|
const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
|
|
5353
|
-
const result = transformMacro(code, id, moduleAliases);
|
|
5934
|
+
const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true);
|
|
5354
5935
|
if (result?.warnings.length) {
|
|
5355
5936
|
for (const w of result.warnings) {
|
|
5356
5937
|
if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
|
|
@@ -5412,6 +5993,14 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5412
5993
|
const obj = callee?.object?.name;
|
|
5413
5994
|
const prop = callee?.property?.name;
|
|
5414
5995
|
const args = node2.arguments;
|
|
5996
|
+
if (obj === "Object" && prop === "assign" && args && args[1]?.type === "ObjectExpression") {
|
|
5997
|
+
for (const p of args[1].properties ?? []) {
|
|
5998
|
+
if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
|
|
5999
|
+
found = { start: p.value.start, end: p.value.end };
|
|
6000
|
+
return;
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
5415
6004
|
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5416
6005
|
const descriptor = args[2];
|
|
5417
6006
|
for (const p of descriptor?.properties ?? []) {
|
|
@@ -5432,7 +6021,21 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5432
6021
|
visit(root);
|
|
5433
6022
|
return found;
|
|
5434
6023
|
}
|
|
5435
|
-
function
|
|
6024
|
+
function extractImportBindings(body) {
|
|
6025
|
+
const out = /* @__PURE__ */ new Map();
|
|
6026
|
+
for (const stmt of body) {
|
|
6027
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
6028
|
+
const s = stmt;
|
|
6029
|
+
for (const spec of s.specifiers) {
|
|
6030
|
+
if (spec.type === "ImportSpecifier") {
|
|
6031
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
6032
|
+
out.set(spec.local.name, { source: s.source.value, imported });
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
return out;
|
|
6037
|
+
}
|
|
6038
|
+
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5436
6039
|
let result;
|
|
5437
6040
|
try {
|
|
5438
6041
|
result = parseSync(id, code);
|
|
@@ -5471,6 +6074,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5471
6074
|
const scope = /* @__PURE__ */ new Map();
|
|
5472
6075
|
const replacements = [];
|
|
5473
6076
|
const warnings = [];
|
|
6077
|
+
beginLoweringCapture();
|
|
5474
6078
|
let anyUnresolved = false;
|
|
5475
6079
|
let ruleMapHolderCounter = 0;
|
|
5476
6080
|
const lineOf = (pos) => {
|
|
@@ -5505,14 +6109,57 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5505
6109
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5506
6110
|
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5507
6111
|
const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
6112
|
+
const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
|
|
5508
6113
|
const serializePieces = (p) => {
|
|
5509
|
-
const mapLit = (m) => `new Map([${[...m].map(
|
|
5510
|
-
|
|
6114
|
+
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
6115
|
+
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
6116
|
+
).join(", ")}])`;
|
|
6117
|
+
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, mfFns: [], buildFns: [] }`;
|
|
5511
6118
|
};
|
|
5512
|
-
const
|
|
5513
|
-
const
|
|
6119
|
+
const isSpread = (it) => "__spreadLocal" in it;
|
|
6120
|
+
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
6121
|
+
const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
|
|
6122
|
+
const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
|
|
6123
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
|
|
5514
6124
|
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5515
6125
|
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
6126
|
+
const resolveModulePieces = (file, exportName, seen) => {
|
|
6127
|
+
const mod = parseModuleCached(file);
|
|
6128
|
+
if (!mod) return null;
|
|
6129
|
+
const localFor = exportLocalName(mod.body, exportName);
|
|
6130
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
6131
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
6132
|
+
if (!literalRange) return null;
|
|
6133
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
6134
|
+
const imports = extractImportBindings(mod.body);
|
|
6135
|
+
const stubNames = [];
|
|
6136
|
+
const stubVals = [];
|
|
6137
|
+
const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
|
|
6138
|
+
const done = /* @__PURE__ */ new Set();
|
|
6139
|
+
for (let m; m = spreadRe.exec(literal2); ) {
|
|
6140
|
+
const local = m[1];
|
|
6141
|
+
if (done.has(local)) continue;
|
|
6142
|
+
done.add(local);
|
|
6143
|
+
const b = imports.get(local);
|
|
6144
|
+
let subPieces = [];
|
|
6145
|
+
if (b) {
|
|
6146
|
+
let subFile = null;
|
|
6147
|
+
try {
|
|
6148
|
+
subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
|
|
6149
|
+
} catch {
|
|
6150
|
+
}
|
|
6151
|
+
if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
|
|
6152
|
+
}
|
|
6153
|
+
stubNames.push(local);
|
|
6154
|
+
stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
|
|
6155
|
+
}
|
|
6156
|
+
try {
|
|
6157
|
+
const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
|
|
6158
|
+
return list.map(materializePiece);
|
|
6159
|
+
} catch {
|
|
6160
|
+
return null;
|
|
6161
|
+
}
|
|
6162
|
+
};
|
|
5516
6163
|
const importedPieces = (localName) => {
|
|
5517
6164
|
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5518
6165
|
let result2 = null;
|
|
@@ -5523,41 +6170,34 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5523
6170
|
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5524
6171
|
} catch {
|
|
5525
6172
|
}
|
|
5526
|
-
|
|
5527
|
-
if (mod) {
|
|
5528
|
-
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5529
|
-
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5530
|
-
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5531
|
-
if (literalRange) {
|
|
5532
|
-
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5533
|
-
try {
|
|
5534
|
-
result2 = new Function(`return (${literal2})`)();
|
|
5535
|
-
} catch {
|
|
5536
|
-
result2 = null;
|
|
5537
|
-
}
|
|
5538
|
-
}
|
|
5539
|
-
}
|
|
6173
|
+
result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
|
|
5540
6174
|
}
|
|
5541
6175
|
importedPiecesCache.set(localName, result2);
|
|
5542
6176
|
return result2;
|
|
5543
6177
|
};
|
|
6178
|
+
const localArg = (rm, label2) => {
|
|
6179
|
+
const entries = [...rm];
|
|
6180
|
+
const ns = nsFor(label2);
|
|
6181
|
+
const p = compileLinkable(entries, ns);
|
|
6182
|
+
if (!p) return null;
|
|
6183
|
+
const ir = serializeRuleMap(entries);
|
|
6184
|
+
return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
|
|
6185
|
+
};
|
|
5544
6186
|
const argPieces = (arg, label2) => {
|
|
5545
6187
|
if (isRulesCall(arg)) {
|
|
5546
6188
|
const factory = arg.arguments[0];
|
|
5547
6189
|
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5548
|
-
|
|
5549
|
-
return p ? [p] : null;
|
|
6190
|
+
return rm ? localArg(rm, label2) : null;
|
|
5550
6191
|
}
|
|
5551
6192
|
if (arg.type === "Identifier") {
|
|
5552
6193
|
const name = arg.name;
|
|
5553
6194
|
const rm = localRuleMaps.get(name);
|
|
5554
|
-
if (rm)
|
|
5555
|
-
const p = compileLinkable([...rm], nsFor(label2));
|
|
5556
|
-
return p ? [p] : null;
|
|
5557
|
-
}
|
|
6195
|
+
if (rm) return localArg(rm, label2);
|
|
5558
6196
|
const composed = localComposedPieces.get(name);
|
|
5559
|
-
if (composed) return composed;
|
|
5560
|
-
|
|
6197
|
+
if (composed) return { pieces: composed };
|
|
6198
|
+
const pieces = importedPieces(name);
|
|
6199
|
+
if (!pieces) return null;
|
|
6200
|
+
return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
|
|
5561
6201
|
}
|
|
5562
6202
|
return null;
|
|
5563
6203
|
};
|
|
@@ -5570,16 +6210,20 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5570
6210
|
}
|
|
5571
6211
|
const elements = arr.elements;
|
|
5572
6212
|
const pieces = [];
|
|
6213
|
+
const carried = [];
|
|
5573
6214
|
for (let i = 0; i < elements.length; i++) {
|
|
5574
|
-
const
|
|
5575
|
-
if (!
|
|
6215
|
+
const r = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
6216
|
+
if (!r) {
|
|
5576
6217
|
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5577
6218
|
return null;
|
|
5578
6219
|
}
|
|
5579
|
-
pieces.push(...
|
|
6220
|
+
pieces.push(...r.pieces);
|
|
6221
|
+
if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
|
|
6222
|
+
else if (r.ir) carried.push(r.ir);
|
|
6223
|
+
else carried.push(...r.pieces);
|
|
5580
6224
|
}
|
|
5581
6225
|
try {
|
|
5582
|
-
return { replacement: emitFusedSource(pieces), pieces };
|
|
6226
|
+
return { replacement: emitFusedSource(pieces), pieces, carried };
|
|
5583
6227
|
} catch (e) {
|
|
5584
6228
|
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5585
6229
|
return null;
|
|
@@ -5666,9 +6310,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5666
6310
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5667
6311
|
let replacement = compiledRules.replacement;
|
|
5668
6312
|
if (exportPrefix) {
|
|
5669
|
-
const
|
|
6313
|
+
const ns = nsFor(varName);
|
|
6314
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], ns);
|
|
5670
6315
|
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5671
|
-
|
|
6316
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
6317
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
5672
6318
|
}
|
|
5673
6319
|
}
|
|
5674
6320
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
@@ -5678,7 +6324,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5678
6324
|
const fused = compileComposeCall(init);
|
|
5679
6325
|
if (!fused) continue;
|
|
5680
6326
|
localComposedPieces.set(varName, fused.pieces);
|
|
5681
|
-
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.
|
|
6327
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
5682
6328
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5683
6329
|
continue;
|
|
5684
6330
|
}
|
|
@@ -5778,6 +6424,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5778
6424
|
for (const { start, end, replacement } of [...replacements].sort((a, b) => b.start - a.start)) {
|
|
5779
6425
|
ms.overwrite(start, end, replacement);
|
|
5780
6426
|
}
|
|
6427
|
+
const unlowered = endLoweringCapture();
|
|
6428
|
+
if (warnUnloweredRegex) {
|
|
6429
|
+
for (const src of unlowered) {
|
|
6430
|
+
warnings.push(`${id}: regex ${src} did not lower to a fast charCodeAt scan (RegExp.exec fallback)`);
|
|
6431
|
+
}
|
|
6432
|
+
}
|
|
5781
6433
|
return {
|
|
5782
6434
|
code: ms.toString(),
|
|
5783
6435
|
map: ms.generateMap({ hires: true }),
|