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/index.cjs
CHANGED
|
@@ -181,6 +181,54 @@ function sequenceFirstSet(parsers) {
|
|
|
181
181
|
}
|
|
182
182
|
return fs;
|
|
183
183
|
}
|
|
184
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
185
|
+
if (seen.has(p)) return any();
|
|
186
|
+
seen.add(p);
|
|
187
|
+
const fs = (c) => firstSetOf(c, seen);
|
|
188
|
+
const d = p._def;
|
|
189
|
+
switch (d.tag) {
|
|
190
|
+
case "literal":
|
|
191
|
+
case "regex":
|
|
192
|
+
case "keywords":
|
|
193
|
+
return p._meta.firstSet;
|
|
194
|
+
// terminals: no refs, cached set is exact
|
|
195
|
+
case "lazy":
|
|
196
|
+
try {
|
|
197
|
+
return fs(d.thunk());
|
|
198
|
+
} catch {
|
|
199
|
+
return any();
|
|
200
|
+
}
|
|
201
|
+
case "choice": {
|
|
202
|
+
let out = empty();
|
|
203
|
+
for (const arm of d.parsers) out = union(out, fs(arm));
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
case "sequence": {
|
|
207
|
+
let out = empty();
|
|
208
|
+
for (const term of d.parsers) {
|
|
209
|
+
out = union(out, fs(term));
|
|
210
|
+
if (!matchesEmpty(term)) return out;
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
case "oneOrMore":
|
|
215
|
+
case "many":
|
|
216
|
+
case "optional":
|
|
217
|
+
case "transform":
|
|
218
|
+
case "label":
|
|
219
|
+
case "trivia":
|
|
220
|
+
case "node":
|
|
221
|
+
case "grammar":
|
|
222
|
+
case "expect":
|
|
223
|
+
return fs(d.parser);
|
|
224
|
+
case "sepBy":
|
|
225
|
+
return fs(d.parser);
|
|
226
|
+
case "skip":
|
|
227
|
+
return fs(d.main);
|
|
228
|
+
default:
|
|
229
|
+
return p._meta.firstSet;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
184
232
|
function mergeRanges(ranges) {
|
|
185
233
|
if (ranges.length === 0) return [];
|
|
186
234
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -358,12 +406,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
358
406
|
}
|
|
359
407
|
|
|
360
408
|
// src/combinators/literal.ts
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
409
|
+
function asciiFoldEq(a, b) {
|
|
410
|
+
if (a.length !== b.length) return false;
|
|
411
|
+
for (let i = 0; i < a.length; i++) {
|
|
412
|
+
let ca = a.charCodeAt(i);
|
|
413
|
+
let cb = b.charCodeAt(i);
|
|
414
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
415
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
416
|
+
if (ca !== cb) return false;
|
|
365
417
|
}
|
|
366
|
-
return
|
|
418
|
+
return true;
|
|
367
419
|
}
|
|
368
420
|
function literal(value, opts = {}) {
|
|
369
421
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -378,8 +430,9 @@ function literal(value, opts = {}) {
|
|
|
378
430
|
const lower = value.toLowerCase();
|
|
379
431
|
const firstUpper = upper.codePointAt(0);
|
|
380
432
|
const firstLower = lower.codePointAt(0);
|
|
381
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
382
|
-
{ lo:
|
|
433
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
434
|
+
{ lo: firstLower, hi: firstLower },
|
|
435
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
383
436
|
] } : firstSet;
|
|
384
437
|
}
|
|
385
438
|
return {
|
|
@@ -392,7 +445,7 @@ function literal(value, opts = {}) {
|
|
|
392
445
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
393
446
|
}
|
|
394
447
|
const slice = input.slice(pos, end);
|
|
395
|
-
const matched = caseInsensitive ?
|
|
448
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
396
449
|
if (matched) {
|
|
397
450
|
const span = { start: pos, end };
|
|
398
451
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -1726,8 +1779,8 @@ function classToRanges(cls) {
|
|
|
1726
1779
|
return parseClassRanges(body);
|
|
1727
1780
|
}
|
|
1728
1781
|
var foldEq = (cVar, cp) => {
|
|
1729
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1730
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1782
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1783
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1731
1784
|
return `${cVar} === ${cp}`;
|
|
1732
1785
|
};
|
|
1733
1786
|
function literalCodePoints(frag) {
|
|
@@ -1978,6 +2031,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1978
2031
|
return classDisjoint(a, b);
|
|
1979
2032
|
}
|
|
1980
2033
|
function fixedClassSeq(shape) {
|
|
2034
|
+
if (shape.kind === "litFold") {
|
|
2035
|
+
return shape.open.map((cp) => {
|
|
2036
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
2037
|
+
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 };
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
1981
2040
|
if (shape.kind !== "seq") return null;
|
|
1982
2041
|
const out = [];
|
|
1983
2042
|
for (const p of shape.parts) {
|
|
@@ -2399,12 +2458,34 @@ function parseScanShapeCore(source) {
|
|
|
2399
2458
|
}
|
|
2400
2459
|
function scanShapeFromRegex(source, flags) {
|
|
2401
2460
|
if (/[msu]/.test(flags)) return null;
|
|
2402
|
-
if (/i/.test(flags))
|
|
2403
|
-
const open = literalCodePoints(source);
|
|
2404
|
-
return open ? { kind: "litFold", open } : null;
|
|
2405
|
-
}
|
|
2461
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2406
2462
|
return parseScanShape(source);
|
|
2407
2463
|
}
|
|
2464
|
+
function caseFoldShape(source) {
|
|
2465
|
+
const la = stripTrailingLookahead(source);
|
|
2466
|
+
if (la) {
|
|
2467
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2468
|
+
if (!inner) return null;
|
|
2469
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2470
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2471
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2472
|
+
}
|
|
2473
|
+
return caseFoldLiteralOrAlt(source);
|
|
2474
|
+
}
|
|
2475
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2476
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2477
|
+
const open = literalCodePoints(source);
|
|
2478
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2479
|
+
const arms = splitTopLevelAlternation(source);
|
|
2480
|
+
if (!arms || arms.length < 2) return null;
|
|
2481
|
+
const litArms = [];
|
|
2482
|
+
for (const arm of arms) {
|
|
2483
|
+
const cp = literalCodePoints(arm);
|
|
2484
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2485
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2486
|
+
}
|
|
2487
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2488
|
+
}
|
|
2408
2489
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2409
2490
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2410
2491
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2416,6 +2497,25 @@ function stringHardStop(shape) {
|
|
|
2416
2497
|
);
|
|
2417
2498
|
}
|
|
2418
2499
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2500
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2501
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2502
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2503
|
+
function planAltSwitch(firsts) {
|
|
2504
|
+
const cases = [];
|
|
2505
|
+
let total = 0;
|
|
2506
|
+
for (const fs of firsts) {
|
|
2507
|
+
if (!fs || fs.negated) return { kind: "if" };
|
|
2508
|
+
const pts = [];
|
|
2509
|
+
for (const [lo, hi] of fs.ranges) {
|
|
2510
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2511
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2512
|
+
}
|
|
2513
|
+
total += pts.length;
|
|
2514
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2515
|
+
cases.push(pts);
|
|
2516
|
+
}
|
|
2517
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2518
|
+
}
|
|
2419
2519
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2420
2520
|
if (shape.kind === "lookahead") {
|
|
2421
2521
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2440,18 +2540,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2440
2540
|
const okV = mint("_ok");
|
|
2441
2541
|
const endV2 = mint("_end");
|
|
2442
2542
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2543
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2544
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2545
|
+
for (const a of shape.arms) {
|
|
2546
|
+
const open = a.open;
|
|
2547
|
+
const f = open[0];
|
|
2548
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2549
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2550
|
+
}
|
|
2551
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2552
|
+
for (const [lower, arms] of groups) {
|
|
2553
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2554
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2555
|
+
for (const open of arms) {
|
|
2556
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2557
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2558
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2559
|
+
}
|
|
2560
|
+
lines.push(`${ind2} break`);
|
|
2561
|
+
lines.push(`${ind2} }`);
|
|
2562
|
+
}
|
|
2563
|
+
lines.push(`${ind2}}`);
|
|
2564
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2565
|
+
}
|
|
2443
2566
|
if (shape.disjoint) {
|
|
2444
|
-
const
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2567
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2568
|
+
if (plan.kind === "switch") {
|
|
2569
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2570
|
+
shape.arms.forEach((arm, k) => {
|
|
2571
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2572
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2573
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2574
|
+
lines.push(...m.setup);
|
|
2575
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2576
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2577
|
+
lines.push(`${ind2} break`);
|
|
2578
|
+
lines.push(`${ind2} }`);
|
|
2579
|
+
});
|
|
2453
2580
|
lines.push(`${ind2}}`);
|
|
2454
|
-
}
|
|
2581
|
+
} else {
|
|
2582
|
+
const c0 = codeAt(start, firstChar);
|
|
2583
|
+
shape.arms.forEach((arm, k) => {
|
|
2584
|
+
const fs = shape.firsts[k];
|
|
2585
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2586
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2587
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2588
|
+
lines.push(...m.setup);
|
|
2589
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2590
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2591
|
+
lines.push(`${ind2}}`);
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2455
2594
|
} else {
|
|
2456
2595
|
const lbl = mint("_alt");
|
|
2457
2596
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2939,6 +3078,8 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2939
3078
|
}
|
|
2940
3079
|
|
|
2941
3080
|
// src/compiler/codegen.ts
|
|
3081
|
+
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 }";
|
|
3082
|
+
var _loweringSink = null;
|
|
2942
3083
|
function v(ctx, prefix = "_v") {
|
|
2943
3084
|
return `${prefix}${ctx.vars++}`;
|
|
2944
3085
|
}
|
|
@@ -3028,16 +3169,12 @@ function namedFnPrelude() {
|
|
|
3028
3169
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
3029
3170
|
ctx.namedFnDecls.push([
|
|
3030
3171
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
3031
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
3032
3172
|
` _pfail: {`,
|
|
3033
3173
|
...reindentStmts(bodyStmts, 2),
|
|
3034
|
-
`
|
|
3035
|
-
`
|
|
3036
|
-
` _pfok = true`,
|
|
3174
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3175
|
+
` return ${valueVar}`,
|
|
3037
3176
|
` }`,
|
|
3038
|
-
`
|
|
3039
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
3040
|
-
` return _pfv`,
|
|
3177
|
+
` return ${NAMED_FN_FAIL}`,
|
|
3041
3178
|
`}`
|
|
3042
3179
|
].join("\n"));
|
|
3043
3180
|
}
|
|
@@ -3337,13 +3474,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3337
3474
|
const vv = v(ctx);
|
|
3338
3475
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3339
3476
|
const stmts = [];
|
|
3340
|
-
if (caseInsensitive) {
|
|
3341
|
-
|
|
3477
|
+
if (caseInsensitive && len > 0) {
|
|
3478
|
+
const match = Array.from(
|
|
3479
|
+
{ length: len },
|
|
3480
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3481
|
+
).join(" && ");
|
|
3342
3482
|
stmts.push(
|
|
3343
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3344
|
-
`${ind(ctx)}const ${vv}
|
|
3345
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3346
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3483
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3484
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3347
3485
|
);
|
|
3348
3486
|
} else if (len === 0) {
|
|
3349
3487
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3464,6 +3602,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3464
3602
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3465
3603
|
}
|
|
3466
3604
|
}
|
|
3605
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3467
3606
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3468
3607
|
const key = `${def.source}/${flags}`;
|
|
3469
3608
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3736,11 +3875,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3736
3875
|
const atStart = failsAtStart(p);
|
|
3737
3876
|
const staticFx = armStaticExpected(ctx, p);
|
|
3738
3877
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3739
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3878
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3879
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3880
|
+
const deep = firstSetOf(p);
|
|
3881
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3882
|
+
})();
|
|
3740
3883
|
let gateCond = null;
|
|
3741
3884
|
if (gate) {
|
|
3742
3885
|
const gateIdx = ctx.mapFns.length;
|
|
3743
3886
|
ctx.mapFns.push(gate);
|
|
3887
|
+
ctx.mapFnSrcs.push(null);
|
|
3744
3888
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3745
3889
|
}
|
|
3746
3890
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3799,13 +3943,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3799
3943
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3800
3944
|
return emitFirstMatch(def, ctx, pos);
|
|
3801
3945
|
}
|
|
3946
|
+
function pushMapFn(ctx, fn, src) {
|
|
3947
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3948
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3949
|
+
if (hit !== -1) return hit;
|
|
3950
|
+
}
|
|
3951
|
+
const idx = ctx.mapFns.length;
|
|
3952
|
+
ctx.mapFns.push(fn);
|
|
3953
|
+
ctx.mapFnSrcs.push(src);
|
|
3954
|
+
return idx;
|
|
3955
|
+
}
|
|
3802
3956
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3803
3957
|
const def = p._def;
|
|
3804
3958
|
if (def.tag === "transform") {
|
|
3805
3959
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3806
|
-
const fnIdx = ctx.
|
|
3807
|
-
ctx.mapFns.push(def.fn);
|
|
3808
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3960
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3809
3961
|
const vv = v(ctx);
|
|
3810
3962
|
return {
|
|
3811
3963
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3988,7 +4140,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3988
4140
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3989
4141
|
];
|
|
3990
4142
|
ctx.indent++;
|
|
3991
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4143
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3992
4144
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3993
4145
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3994
4146
|
if (def.skip.length > 0) {
|
|
@@ -4044,15 +4196,19 @@ function emitNode(def, ctx, pos) {
|
|
|
4044
4196
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
4045
4197
|
}
|
|
4046
4198
|
const i = ind(ctx);
|
|
4047
|
-
const capturesTrivia = mkType !== null || structural
|
|
4048
|
-
const clonesState = structural
|
|
4199
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4200
|
+
const clonesState = !structural && buildReadsState(def);
|
|
4049
4201
|
const chV = v(ctx, "_ch");
|
|
4050
4202
|
const rawV = v(ctx, "_raw");
|
|
4051
|
-
const
|
|
4203
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4204
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4205
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
4052
4206
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
4053
4207
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
4054
|
-
|
|
4055
|
-
const
|
|
4208
|
+
if (structural) ctx.needsHostReads = true;
|
|
4209
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4210
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4211
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
4056
4212
|
const stmts = [
|
|
4057
4213
|
allocStmt,
|
|
4058
4214
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -4063,7 +4219,10 @@ function emitNode(def, ctx, pos) {
|
|
|
4063
4219
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
4064
4220
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
4065
4221
|
let stV = "undefined";
|
|
4066
|
-
if (
|
|
4222
|
+
if (structural) {
|
|
4223
|
+
stV = v(ctx, "_nst");
|
|
4224
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4225
|
+
} else if (clonesState) {
|
|
4067
4226
|
stV = v(ctx, "_nst");
|
|
4068
4227
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4069
4228
|
}
|
|
@@ -4172,6 +4331,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4172
4331
|
return { stmts, valueVar: valVar, endVar };
|
|
4173
4332
|
}
|
|
4174
4333
|
function emit(p, ctx, pos) {
|
|
4334
|
+
const usage = ctx.lazyUsage;
|
|
4335
|
+
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
|
|
4336
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4337
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4338
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4339
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4340
|
+
const existing = ctx.namedParsers.get(p);
|
|
4341
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4342
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4343
|
+
ctx.namedParsers.set(p, fnName);
|
|
4344
|
+
const savedIndent = ctx.indent;
|
|
4345
|
+
const savedFailLabel = ctx.failLabel;
|
|
4346
|
+
const savedRecord = ctx.recordFail;
|
|
4347
|
+
ctx.indent = 1;
|
|
4348
|
+
ctx.failLabel = "_pfail";
|
|
4349
|
+
ctx.recordFail = true;
|
|
4350
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4351
|
+
ctx.indent = savedIndent;
|
|
4352
|
+
ctx.failLabel = savedFailLabel;
|
|
4353
|
+
ctx.recordFail = savedRecord;
|
|
4354
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4355
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4356
|
+
}
|
|
4357
|
+
return emitDispatch(p, ctx, pos);
|
|
4358
|
+
}
|
|
4359
|
+
function emitDispatch(p, ctx, pos) {
|
|
4175
4360
|
const def = p._def;
|
|
4176
4361
|
switch (def.tag) {
|
|
4177
4362
|
case "literal":
|
|
@@ -4217,9 +4402,7 @@ function emit(p, ctx, pos) {
|
|
|
4217
4402
|
};
|
|
4218
4403
|
}
|
|
4219
4404
|
}
|
|
4220
|
-
const fnIdx = ctx.
|
|
4221
|
-
ctx.mapFns.push(def.fn);
|
|
4222
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4405
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4223
4406
|
const mv = v(ctx, "_mapped");
|
|
4224
4407
|
return {
|
|
4225
4408
|
stmts: [
|
|
@@ -4284,6 +4467,7 @@ function emit(p, ctx, pos) {
|
|
|
4284
4467
|
case "guard": {
|
|
4285
4468
|
const fnIdx = ctx.mapFns.length;
|
|
4286
4469
|
ctx.mapFns.push(def.predicate);
|
|
4470
|
+
ctx.mapFnSrcs.push(null);
|
|
4287
4471
|
const vv = v(ctx);
|
|
4288
4472
|
return {
|
|
4289
4473
|
stmts: [
|
|
@@ -4298,6 +4482,7 @@ function emit(p, ctx, pos) {
|
|
|
4298
4482
|
const evIdx = ctx.mapFns.length;
|
|
4299
4483
|
const extra = def.extra;
|
|
4300
4484
|
ctx.mapFns.push((() => extra));
|
|
4485
|
+
ctx.mapFnSrcs.push(null);
|
|
4301
4486
|
const innerParser = def.parser;
|
|
4302
4487
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4303
4488
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4417,9 +4602,58 @@ function childrenOf(def) {
|
|
|
4417
4602
|
return [];
|
|
4418
4603
|
}
|
|
4419
4604
|
}
|
|
4605
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4606
|
+
"sequence",
|
|
4607
|
+
"choice",
|
|
4608
|
+
"many",
|
|
4609
|
+
"oneOrMore",
|
|
4610
|
+
"optional",
|
|
4611
|
+
"sepBy",
|
|
4612
|
+
"transform",
|
|
4613
|
+
"node",
|
|
4614
|
+
"not"
|
|
4615
|
+
]);
|
|
4616
|
+
function isHoistableTag(tag) {
|
|
4617
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4618
|
+
}
|
|
4619
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4420
4620
|
function analyzeLazyUsage(root) {
|
|
4421
4621
|
return analyzeLazyUsageMulti([root]);
|
|
4422
4622
|
}
|
|
4623
|
+
function subtreeSizes(roots) {
|
|
4624
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4625
|
+
const sz = (p) => {
|
|
4626
|
+
const cached = sizes.get(p);
|
|
4627
|
+
if (cached !== void 0) return cached;
|
|
4628
|
+
const def = p._def;
|
|
4629
|
+
let s = 1;
|
|
4630
|
+
if (def.tag !== "lazy") {
|
|
4631
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4632
|
+
}
|
|
4633
|
+
sizes.set(p, s);
|
|
4634
|
+
return s;
|
|
4635
|
+
};
|
|
4636
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4637
|
+
const visit = (p) => {
|
|
4638
|
+
const def = p._def;
|
|
4639
|
+
if (def.tag === "lazy") {
|
|
4640
|
+
if (visitedLazy.has(p)) return;
|
|
4641
|
+
visitedLazy.add(p);
|
|
4642
|
+
let resolved;
|
|
4643
|
+
try {
|
|
4644
|
+
resolved = def.thunk();
|
|
4645
|
+
} catch {
|
|
4646
|
+
return;
|
|
4647
|
+
}
|
|
4648
|
+
visit(resolved);
|
|
4649
|
+
return;
|
|
4650
|
+
}
|
|
4651
|
+
sz(p);
|
|
4652
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4653
|
+
};
|
|
4654
|
+
for (const root of roots) visit(root);
|
|
4655
|
+
return sizes;
|
|
4656
|
+
}
|
|
4423
4657
|
function analyzeLazyUsageMulti(roots) {
|
|
4424
4658
|
const counts = /* @__PURE__ */ new Map();
|
|
4425
4659
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4446,10 +4680,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4446
4680
|
active.delete(p);
|
|
4447
4681
|
return;
|
|
4448
4682
|
}
|
|
4683
|
+
if (isHoistableTag(def.tag)) {
|
|
4684
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4685
|
+
}
|
|
4449
4686
|
for (const child of childrenOf(def)) walk2(child);
|
|
4450
4687
|
}
|
|
4451
|
-
|
|
4452
|
-
|
|
4688
|
+
const rootsArr = [...roots];
|
|
4689
|
+
for (const root of rootsArr) walk2(root);
|
|
4690
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4453
4691
|
}
|
|
4454
4692
|
function ruleDependencies(ruleMap) {
|
|
4455
4693
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4499,7 +4737,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4499
4737
|
buildFns: [],
|
|
4500
4738
|
buildSrcs: [],
|
|
4501
4739
|
runtimeParsers: [],
|
|
4502
|
-
needsCollator: false,
|
|
4503
4740
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4504
4741
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4505
4742
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4508,18 +4745,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4508
4745
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4509
4746
|
};
|
|
4510
4747
|
const r = emit(combinator, ctx, "_pos");
|
|
4511
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4512
|
-
` : "";
|
|
4513
4748
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4514
4749
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4750
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4515
4751
|
const source = [
|
|
4516
4752
|
...emptyTlDecls,
|
|
4753
|
+
...hostReadsDecls,
|
|
4517
4754
|
...ctx.regexDecls,
|
|
4518
4755
|
...ctx.expectedDecls,
|
|
4519
4756
|
"",
|
|
4520
4757
|
...namedPrelude,
|
|
4521
4758
|
ctx.namedFnDecls.join("\n\n"),
|
|
4522
|
-
|
|
4759
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4523
4760
|
` let pos = _pos`,
|
|
4524
4761
|
...r.stmts,
|
|
4525
4762
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4527,9 +4764,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4527
4764
|
].join("\n");
|
|
4528
4765
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4529
4766
|
...emptyTlDecls,
|
|
4767
|
+
...hostReadsDecls,
|
|
4530
4768
|
...ctx.regexDecls,
|
|
4531
4769
|
...ctx.expectedDecls,
|
|
4532
|
-
collatorDecl,
|
|
4533
4770
|
...namedPrelude,
|
|
4534
4771
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4535
4772
|
`let pos = _pos`,
|
|
@@ -4546,7 +4783,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4546
4783
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4547
4784
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4548
4785
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4549
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4786
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4550
4787
|
return {
|
|
4551
4788
|
source,
|
|
4552
4789
|
inlineExpression,
|
|
@@ -4602,7 +4839,6 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4602
4839
|
buildFns: [],
|
|
4603
4840
|
buildSrcs: [],
|
|
4604
4841
|
runtimeParsers: [],
|
|
4605
|
-
needsCollator: false,
|
|
4606
4842
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4607
4843
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4608
4844
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4733,12 +4969,12 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4733
4969
|
firstSets,
|
|
4734
4970
|
deps: ruleDependencies(ruleMap),
|
|
4735
4971
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4736
|
-
|
|
4972
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4737
4973
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4738
4974
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4739
4975
|
};
|
|
4740
4976
|
}
|
|
4741
|
-
function buildInlineExpression(ctx, r,
|
|
4977
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4742
4978
|
const bodyLines = [
|
|
4743
4979
|
` let pos = _pos`,
|
|
4744
4980
|
...r.stmts,
|
|
@@ -4752,14 +4988,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4752
4988
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4753
4989
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4754
4990
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4755
|
-
const
|
|
4991
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4992
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4756
4993
|
if (!needsWrapper) return innerFn;
|
|
4757
4994
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4758
4995
|
const hoistedDecls = [
|
|
4759
4996
|
emptyTlDecl,
|
|
4997
|
+
hostReadsDecl,
|
|
4760
4998
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4761
4999
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4762
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4763
5000
|
mfDecl,
|
|
4764
5001
|
buildDecl,
|
|
4765
5002
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4776,6 +5013,172 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4776
5013
|
].join("\n");
|
|
4777
5014
|
}
|
|
4778
5015
|
|
|
5016
|
+
// src/combinators/scanTo.ts
|
|
5017
|
+
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
5018
|
+
const meta = {
|
|
5019
|
+
firstSet: any(),
|
|
5020
|
+
canMatchNewline: true,
|
|
5021
|
+
isTrivia: false
|
|
5022
|
+
};
|
|
5023
|
+
return {
|
|
5024
|
+
_tag: "scanTo",
|
|
5025
|
+
_meta: meta,
|
|
5026
|
+
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
5027
|
+
parse(input, pos, ctx) {
|
|
5028
|
+
let cur = pos;
|
|
5029
|
+
const probeCtx = {
|
|
5030
|
+
trackLines: ctx.trackLines,
|
|
5031
|
+
state: ctx.state,
|
|
5032
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
5033
|
+
};
|
|
5034
|
+
const emit2 = (end) => {
|
|
5035
|
+
if (end > pos && cstCaptureActive(ctx)) {
|
|
5036
|
+
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
5037
|
+
pushCstLeaf(ctx, leaf);
|
|
5038
|
+
}
|
|
5039
|
+
};
|
|
5040
|
+
while (cur < input.length) {
|
|
5041
|
+
const s = sentinel.parse(input, cur, probeCtx);
|
|
5042
|
+
if (s.ok) {
|
|
5043
|
+
emit2(cur);
|
|
5044
|
+
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
5045
|
+
}
|
|
5046
|
+
let advanced = false;
|
|
5047
|
+
for (const skipper of skip2) {
|
|
5048
|
+
const r = skipper.parse(input, cur, probeCtx);
|
|
5049
|
+
if (r.ok && r.span.end > cur) {
|
|
5050
|
+
cur = r.span.end;
|
|
5051
|
+
advanced = true;
|
|
5052
|
+
break;
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
if (!advanced) cur++;
|
|
5056
|
+
}
|
|
5057
|
+
if (orEOF) {
|
|
5058
|
+
emit2(cur);
|
|
5059
|
+
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
5060
|
+
}
|
|
5061
|
+
const sentDef = sentinel._def;
|
|
5062
|
+
const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
|
|
5063
|
+
return { ok: false, expected, span: { start: pos, end: cur } };
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
5066
|
+
}
|
|
5067
|
+
function balanced(open, close, options = {}) {
|
|
5068
|
+
const self = ref();
|
|
5069
|
+
const skips = options.skip ?? [];
|
|
5070
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
5071
|
+
let bounded = true;
|
|
5072
|
+
for (const sk of skips) {
|
|
5073
|
+
const cs = firstSetClassChars(sk);
|
|
5074
|
+
if (cs === null) {
|
|
5075
|
+
bounded = false;
|
|
5076
|
+
break;
|
|
5077
|
+
}
|
|
5078
|
+
for (const ch of cs) stop.add(ch);
|
|
5079
|
+
}
|
|
5080
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
5081
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
5082
|
+
const inner = many(choice(self, ...skips, content));
|
|
5083
|
+
const combi = transform(
|
|
5084
|
+
sequence(literal(open), inner, expect(literal(close), close)),
|
|
5085
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
5086
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
5087
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
5088
|
+
);
|
|
5089
|
+
if (combi._def.tag === "transform") {
|
|
5090
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
5091
|
+
}
|
|
5092
|
+
self.define(combi);
|
|
5093
|
+
return combi;
|
|
5094
|
+
}
|
|
5095
|
+
function firstSetClassChars(c) {
|
|
5096
|
+
const fs = c._meta.firstSet;
|
|
5097
|
+
if (fs.kind === "empty") return [];
|
|
5098
|
+
if (fs.kind !== "ranges") return null;
|
|
5099
|
+
const out = [];
|
|
5100
|
+
for (const { lo, hi } of fs.ranges) {
|
|
5101
|
+
if (hi - lo > 8) return null;
|
|
5102
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
5103
|
+
}
|
|
5104
|
+
return out;
|
|
5105
|
+
}
|
|
5106
|
+
function escapeClassChar(ch) {
|
|
5107
|
+
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
5108
|
+
}
|
|
5109
|
+
|
|
5110
|
+
// src/compiler/ir-serialize.ts
|
|
5111
|
+
function evalRuleMapIR(ir) {
|
|
5112
|
+
const _tf = (child, src) => {
|
|
5113
|
+
let fn2;
|
|
5114
|
+
try {
|
|
5115
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5116
|
+
} catch {
|
|
5117
|
+
fn2 = () => {
|
|
5118
|
+
throw new Error("IR transform fn not materialized");
|
|
5119
|
+
};
|
|
5120
|
+
}
|
|
5121
|
+
const t = transform(child, fn2);
|
|
5122
|
+
t._def.fnSrc = src;
|
|
5123
|
+
return t;
|
|
5124
|
+
};
|
|
5125
|
+
const _nd = (type, child, src, opts) => {
|
|
5126
|
+
const n = node(type, child, void 0, opts);
|
|
5127
|
+
n._def.buildSrc = src;
|
|
5128
|
+
return n;
|
|
5129
|
+
};
|
|
5130
|
+
const fn = new Function(
|
|
5131
|
+
"rules",
|
|
5132
|
+
"ref",
|
|
5133
|
+
"regex",
|
|
5134
|
+
"literal",
|
|
5135
|
+
"keywords",
|
|
5136
|
+
"sequence",
|
|
5137
|
+
"choice",
|
|
5138
|
+
"many",
|
|
5139
|
+
"oneOrMore",
|
|
5140
|
+
"optional",
|
|
5141
|
+
"sepBy",
|
|
5142
|
+
"not",
|
|
5143
|
+
"node",
|
|
5144
|
+
"parser",
|
|
5145
|
+
"scanTo",
|
|
5146
|
+
"transform",
|
|
5147
|
+
"skip",
|
|
5148
|
+
"trivia",
|
|
5149
|
+
"label",
|
|
5150
|
+
"expect",
|
|
5151
|
+
"_tf",
|
|
5152
|
+
"_nd",
|
|
5153
|
+
`return (${ir})`
|
|
5154
|
+
);
|
|
5155
|
+
const map = fn(
|
|
5156
|
+
rules,
|
|
5157
|
+
ref,
|
|
5158
|
+
regex,
|
|
5159
|
+
literal,
|
|
5160
|
+
keywords,
|
|
5161
|
+
sequence,
|
|
5162
|
+
choice,
|
|
5163
|
+
many,
|
|
5164
|
+
oneOrMore,
|
|
5165
|
+
optional,
|
|
5166
|
+
sepBy,
|
|
5167
|
+
not,
|
|
5168
|
+
node,
|
|
5169
|
+
parser,
|
|
5170
|
+
scanTo,
|
|
5171
|
+
transform,
|
|
5172
|
+
skip,
|
|
5173
|
+
trivia,
|
|
5174
|
+
label,
|
|
5175
|
+
expect,
|
|
5176
|
+
_tf,
|
|
5177
|
+
_nd
|
|
5178
|
+
);
|
|
5179
|
+
return Object.entries(map);
|
|
5180
|
+
}
|
|
5181
|
+
|
|
4779
5182
|
// src/compiler/linker.ts
|
|
4780
5183
|
var _nsCounter = 0;
|
|
4781
5184
|
function linkable(rulesMap, ns) {
|
|
@@ -4818,13 +5221,13 @@ function fusedBody(pieces) {
|
|
|
4818
5221
|
}
|
|
4819
5222
|
const contributing = new Set(winner.values());
|
|
4820
5223
|
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4821
|
-
const
|
|
5224
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4822
5225
|
const lines = [
|
|
4823
5226
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4824
5227
|
"const _pfFail = {}",
|
|
4825
5228
|
"let _pfEnd",
|
|
4826
5229
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4827
|
-
...
|
|
5230
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4828
5231
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4829
5232
|
...[...contributing].flatMap((p) => p.prelude),
|
|
4830
5233
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
@@ -4860,9 +5263,18 @@ function fuseRules(pieces) {
|
|
|
4860
5263
|
return new Function("_env", body)(env);
|
|
4861
5264
|
}
|
|
4862
5265
|
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
5266
|
+
function isIRPiece(p) {
|
|
5267
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5268
|
+
}
|
|
5269
|
+
function materializePiece(p) {
|
|
5270
|
+
if (!isIRPiece(p)) return p;
|
|
5271
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5272
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5273
|
+
return pieces;
|
|
5274
|
+
}
|
|
4863
5275
|
function itemPieces(item) {
|
|
4864
5276
|
const carried = item[COMPOSED_PIECES];
|
|
4865
|
-
if (Array.isArray(carried)) return carried;
|
|
5277
|
+
if (Array.isArray(carried)) return carried.map(materializePiece);
|
|
4866
5278
|
if (item.ruleFns instanceof Map) return [item];
|
|
4867
5279
|
return [linkable(item)];
|
|
4868
5280
|
}
|
|
@@ -4958,100 +5370,6 @@ function deeperFail(a, b) {
|
|
|
4958
5370
|
return a.span.start >= b.span.start ? a : b;
|
|
4959
5371
|
}
|
|
4960
5372
|
|
|
4961
|
-
// src/combinators/scanTo.ts
|
|
4962
|
-
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4963
|
-
const meta = {
|
|
4964
|
-
firstSet: any(),
|
|
4965
|
-
canMatchNewline: true,
|
|
4966
|
-
isTrivia: false
|
|
4967
|
-
};
|
|
4968
|
-
return {
|
|
4969
|
-
_tag: "scanTo",
|
|
4970
|
-
_meta: meta,
|
|
4971
|
-
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
4972
|
-
parse(input, pos, ctx) {
|
|
4973
|
-
let cur = pos;
|
|
4974
|
-
const probeCtx = {
|
|
4975
|
-
trackLines: ctx.trackLines,
|
|
4976
|
-
state: ctx.state,
|
|
4977
|
-
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
4978
|
-
};
|
|
4979
|
-
const emit2 = (end) => {
|
|
4980
|
-
if (end > pos && cstCaptureActive(ctx)) {
|
|
4981
|
-
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
4982
|
-
pushCstLeaf(ctx, leaf);
|
|
4983
|
-
}
|
|
4984
|
-
};
|
|
4985
|
-
while (cur < input.length) {
|
|
4986
|
-
const s = sentinel.parse(input, cur, probeCtx);
|
|
4987
|
-
if (s.ok) {
|
|
4988
|
-
emit2(cur);
|
|
4989
|
-
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
4990
|
-
}
|
|
4991
|
-
let advanced = false;
|
|
4992
|
-
for (const skipper of skip2) {
|
|
4993
|
-
const r = skipper.parse(input, cur, probeCtx);
|
|
4994
|
-
if (r.ok && r.span.end > cur) {
|
|
4995
|
-
cur = r.span.end;
|
|
4996
|
-
advanced = true;
|
|
4997
|
-
break;
|
|
4998
|
-
}
|
|
4999
|
-
}
|
|
5000
|
-
if (!advanced) cur++;
|
|
5001
|
-
}
|
|
5002
|
-
if (orEOF) {
|
|
5003
|
-
emit2(cur);
|
|
5004
|
-
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
5005
|
-
}
|
|
5006
|
-
const sentDef = sentinel._def;
|
|
5007
|
-
const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
|
|
5008
|
-
return { ok: false, expected, span: { start: pos, end: cur } };
|
|
5009
|
-
}
|
|
5010
|
-
};
|
|
5011
|
-
}
|
|
5012
|
-
function balanced(open, close, options = {}) {
|
|
5013
|
-
const self = ref();
|
|
5014
|
-
const skips = options.skip ?? [];
|
|
5015
|
-
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
5016
|
-
let bounded = true;
|
|
5017
|
-
for (const sk of skips) {
|
|
5018
|
-
const cs = firstSetClassChars(sk);
|
|
5019
|
-
if (cs === null) {
|
|
5020
|
-
bounded = false;
|
|
5021
|
-
break;
|
|
5022
|
-
}
|
|
5023
|
-
for (const ch of cs) stop.add(ch);
|
|
5024
|
-
}
|
|
5025
|
-
const cls = [...stop].map(escapeClassChar).join("");
|
|
5026
|
-
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
5027
|
-
const inner = many(choice(self, ...skips, content));
|
|
5028
|
-
const combi = transform(
|
|
5029
|
-
sequence(literal(open), inner, expect(literal(close), close)),
|
|
5030
|
-
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
5031
|
-
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
5032
|
-
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
5033
|
-
);
|
|
5034
|
-
if (combi._def.tag === "transform") {
|
|
5035
|
-
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
5036
|
-
}
|
|
5037
|
-
self.define(combi);
|
|
5038
|
-
return combi;
|
|
5039
|
-
}
|
|
5040
|
-
function firstSetClassChars(c) {
|
|
5041
|
-
const fs = c._meta.firstSet;
|
|
5042
|
-
if (fs.kind === "empty") return [];
|
|
5043
|
-
if (fs.kind !== "ranges") return null;
|
|
5044
|
-
const out = [];
|
|
5045
|
-
for (const { lo, hi } of fs.ranges) {
|
|
5046
|
-
if (hi - lo > 8) return null;
|
|
5047
|
-
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
5048
|
-
}
|
|
5049
|
-
return out;
|
|
5050
|
-
}
|
|
5051
|
-
function escapeClassChar(ch) {
|
|
5052
|
-
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
5053
|
-
}
|
|
5054
|
-
|
|
5055
5373
|
// src/combinators/recover-list.ts
|
|
5056
5374
|
function tolerantElement(item, sentinel, until) {
|
|
5057
5375
|
const el = transform(
|