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.js
CHANGED
|
@@ -84,6 +84,54 @@ function sequenceFirstSet(parsers) {
|
|
|
84
84
|
}
|
|
85
85
|
return fs;
|
|
86
86
|
}
|
|
87
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
88
|
+
if (seen.has(p)) return any();
|
|
89
|
+
seen.add(p);
|
|
90
|
+
const fs = (c) => firstSetOf(c, seen);
|
|
91
|
+
const d = p._def;
|
|
92
|
+
switch (d.tag) {
|
|
93
|
+
case "literal":
|
|
94
|
+
case "regex":
|
|
95
|
+
case "keywords":
|
|
96
|
+
return p._meta.firstSet;
|
|
97
|
+
// terminals: no refs, cached set is exact
|
|
98
|
+
case "lazy":
|
|
99
|
+
try {
|
|
100
|
+
return fs(d.thunk());
|
|
101
|
+
} catch {
|
|
102
|
+
return any();
|
|
103
|
+
}
|
|
104
|
+
case "choice": {
|
|
105
|
+
let out = empty();
|
|
106
|
+
for (const arm of d.parsers) out = union(out, fs(arm));
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
case "sequence": {
|
|
110
|
+
let out = empty();
|
|
111
|
+
for (const term of d.parsers) {
|
|
112
|
+
out = union(out, fs(term));
|
|
113
|
+
if (!matchesEmpty(term)) return out;
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
case "oneOrMore":
|
|
118
|
+
case "many":
|
|
119
|
+
case "optional":
|
|
120
|
+
case "transform":
|
|
121
|
+
case "label":
|
|
122
|
+
case "trivia":
|
|
123
|
+
case "node":
|
|
124
|
+
case "grammar":
|
|
125
|
+
case "expect":
|
|
126
|
+
return fs(d.parser);
|
|
127
|
+
case "sepBy":
|
|
128
|
+
return fs(d.parser);
|
|
129
|
+
case "skip":
|
|
130
|
+
return fs(d.main);
|
|
131
|
+
default:
|
|
132
|
+
return p._meta.firstSet;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
87
135
|
function mergeRanges(ranges) {
|
|
88
136
|
if (ranges.length === 0) return [];
|
|
89
137
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -261,12 +309,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
261
309
|
}
|
|
262
310
|
|
|
263
311
|
// src/combinators/literal.ts
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
312
|
+
function asciiFoldEq(a, b) {
|
|
313
|
+
if (a.length !== b.length) return false;
|
|
314
|
+
for (let i = 0; i < a.length; i++) {
|
|
315
|
+
let ca = a.charCodeAt(i);
|
|
316
|
+
let cb = b.charCodeAt(i);
|
|
317
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
318
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
319
|
+
if (ca !== cb) return false;
|
|
268
320
|
}
|
|
269
|
-
return
|
|
321
|
+
return true;
|
|
270
322
|
}
|
|
271
323
|
function literal(value, opts = {}) {
|
|
272
324
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -281,8 +333,9 @@ function literal(value, opts = {}) {
|
|
|
281
333
|
const lower = value.toLowerCase();
|
|
282
334
|
const firstUpper = upper.codePointAt(0);
|
|
283
335
|
const firstLower = lower.codePointAt(0);
|
|
284
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
285
|
-
{ lo:
|
|
336
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
337
|
+
{ lo: firstLower, hi: firstLower },
|
|
338
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
286
339
|
] } : firstSet;
|
|
287
340
|
}
|
|
288
341
|
return {
|
|
@@ -295,7 +348,7 @@ function literal(value, opts = {}) {
|
|
|
295
348
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
296
349
|
}
|
|
297
350
|
const slice = input.slice(pos, end);
|
|
298
|
-
const matched = caseInsensitive ?
|
|
351
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
299
352
|
if (matched) {
|
|
300
353
|
const span = { start: pos, end };
|
|
301
354
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -1629,8 +1682,8 @@ function classToRanges(cls) {
|
|
|
1629
1682
|
return parseClassRanges(body);
|
|
1630
1683
|
}
|
|
1631
1684
|
var foldEq = (cVar, cp) => {
|
|
1632
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1633
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1685
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1686
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1634
1687
|
return `${cVar} === ${cp}`;
|
|
1635
1688
|
};
|
|
1636
1689
|
function literalCodePoints(frag) {
|
|
@@ -1881,6 +1934,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1881
1934
|
return classDisjoint(a, b);
|
|
1882
1935
|
}
|
|
1883
1936
|
function fixedClassSeq(shape) {
|
|
1937
|
+
if (shape.kind === "litFold") {
|
|
1938
|
+
return shape.open.map((cp) => {
|
|
1939
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1940
|
+
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 };
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1884
1943
|
if (shape.kind !== "seq") return null;
|
|
1885
1944
|
const out = [];
|
|
1886
1945
|
for (const p of shape.parts) {
|
|
@@ -2302,12 +2361,34 @@ function parseScanShapeCore(source) {
|
|
|
2302
2361
|
}
|
|
2303
2362
|
function scanShapeFromRegex(source, flags) {
|
|
2304
2363
|
if (/[msu]/.test(flags)) return null;
|
|
2305
|
-
if (/i/.test(flags))
|
|
2306
|
-
const open = literalCodePoints(source);
|
|
2307
|
-
return open ? { kind: "litFold", open } : null;
|
|
2308
|
-
}
|
|
2364
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2309
2365
|
return parseScanShape(source);
|
|
2310
2366
|
}
|
|
2367
|
+
function caseFoldShape(source) {
|
|
2368
|
+
const la = stripTrailingLookahead(source);
|
|
2369
|
+
if (la) {
|
|
2370
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2371
|
+
if (!inner) return null;
|
|
2372
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2373
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2374
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2375
|
+
}
|
|
2376
|
+
return caseFoldLiteralOrAlt(source);
|
|
2377
|
+
}
|
|
2378
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2379
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2380
|
+
const open = literalCodePoints(source);
|
|
2381
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2382
|
+
const arms = splitTopLevelAlternation(source);
|
|
2383
|
+
if (!arms || arms.length < 2) return null;
|
|
2384
|
+
const litArms = [];
|
|
2385
|
+
for (const arm of arms) {
|
|
2386
|
+
const cp = literalCodePoints(arm);
|
|
2387
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2388
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2389
|
+
}
|
|
2390
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2391
|
+
}
|
|
2311
2392
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2312
2393
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2313
2394
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2319,6 +2400,25 @@ function stringHardStop(shape) {
|
|
|
2319
2400
|
);
|
|
2320
2401
|
}
|
|
2321
2402
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2403
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2404
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2405
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2406
|
+
function planAltSwitch(firsts) {
|
|
2407
|
+
const cases = [];
|
|
2408
|
+
let total = 0;
|
|
2409
|
+
for (const fs of firsts) {
|
|
2410
|
+
if (!fs || fs.negated) return { kind: "if" };
|
|
2411
|
+
const pts = [];
|
|
2412
|
+
for (const [lo, hi] of fs.ranges) {
|
|
2413
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2414
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2415
|
+
}
|
|
2416
|
+
total += pts.length;
|
|
2417
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2418
|
+
cases.push(pts);
|
|
2419
|
+
}
|
|
2420
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2421
|
+
}
|
|
2322
2422
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2323
2423
|
if (shape.kind === "lookahead") {
|
|
2324
2424
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2343,18 +2443,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2343
2443
|
const okV = mint("_ok");
|
|
2344
2444
|
const endV2 = mint("_end");
|
|
2345
2445
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2446
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2447
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2448
|
+
for (const a of shape.arms) {
|
|
2449
|
+
const open = a.open;
|
|
2450
|
+
const f = open[0];
|
|
2451
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2452
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2453
|
+
}
|
|
2454
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2455
|
+
for (const [lower, arms] of groups) {
|
|
2456
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2457
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2458
|
+
for (const open of arms) {
|
|
2459
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2460
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2461
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2462
|
+
}
|
|
2463
|
+
lines.push(`${ind2} break`);
|
|
2464
|
+
lines.push(`${ind2} }`);
|
|
2465
|
+
}
|
|
2466
|
+
lines.push(`${ind2}}`);
|
|
2467
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2468
|
+
}
|
|
2346
2469
|
if (shape.disjoint) {
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2470
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2471
|
+
if (plan.kind === "switch") {
|
|
2472
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2473
|
+
shape.arms.forEach((arm, k) => {
|
|
2474
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2475
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2476
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2477
|
+
lines.push(...m.setup);
|
|
2478
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2479
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2480
|
+
lines.push(`${ind2} break`);
|
|
2481
|
+
lines.push(`${ind2} }`);
|
|
2482
|
+
});
|
|
2356
2483
|
lines.push(`${ind2}}`);
|
|
2357
|
-
}
|
|
2484
|
+
} else {
|
|
2485
|
+
const c0 = codeAt(start, firstChar);
|
|
2486
|
+
shape.arms.forEach((arm, k) => {
|
|
2487
|
+
const fs = shape.firsts[k];
|
|
2488
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2489
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2490
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2491
|
+
lines.push(...m.setup);
|
|
2492
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2493
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2494
|
+
lines.push(`${ind2}}`);
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2358
2497
|
} else {
|
|
2359
2498
|
const lbl = mint("_alt");
|
|
2360
2499
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2842,6 +2981,8 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2842
2981
|
}
|
|
2843
2982
|
|
|
2844
2983
|
// src/compiler/codegen.ts
|
|
2984
|
+
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 }";
|
|
2985
|
+
var _loweringSink = null;
|
|
2845
2986
|
function v(ctx, prefix = "_v") {
|
|
2846
2987
|
return `${prefix}${ctx.vars++}`;
|
|
2847
2988
|
}
|
|
@@ -2931,16 +3072,12 @@ function namedFnPrelude() {
|
|
|
2931
3072
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
2932
3073
|
ctx.namedFnDecls.push([
|
|
2933
3074
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
2934
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
2935
3075
|
` _pfail: {`,
|
|
2936
3076
|
...reindentStmts(bodyStmts, 2),
|
|
2937
|
-
`
|
|
2938
|
-
`
|
|
2939
|
-
` _pfok = true`,
|
|
3077
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3078
|
+
` return ${valueVar}`,
|
|
2940
3079
|
` }`,
|
|
2941
|
-
`
|
|
2942
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
2943
|
-
` return _pfv`,
|
|
3080
|
+
` return ${NAMED_FN_FAIL}`,
|
|
2944
3081
|
`}`
|
|
2945
3082
|
].join("\n"));
|
|
2946
3083
|
}
|
|
@@ -3240,13 +3377,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3240
3377
|
const vv = v(ctx);
|
|
3241
3378
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3242
3379
|
const stmts = [];
|
|
3243
|
-
if (caseInsensitive) {
|
|
3244
|
-
|
|
3380
|
+
if (caseInsensitive && len > 0) {
|
|
3381
|
+
const match = Array.from(
|
|
3382
|
+
{ length: len },
|
|
3383
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3384
|
+
).join(" && ");
|
|
3245
3385
|
stmts.push(
|
|
3246
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3247
|
-
`${ind(ctx)}const ${vv}
|
|
3248
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3249
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3386
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3387
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3250
3388
|
);
|
|
3251
3389
|
} else if (len === 0) {
|
|
3252
3390
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3367,6 +3505,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3367
3505
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3368
3506
|
}
|
|
3369
3507
|
}
|
|
3508
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3370
3509
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3371
3510
|
const key = `${def.source}/${flags}`;
|
|
3372
3511
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3639,11 +3778,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3639
3778
|
const atStart = failsAtStart(p);
|
|
3640
3779
|
const staticFx = armStaticExpected(ctx, p);
|
|
3641
3780
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3642
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3781
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3782
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3783
|
+
const deep = firstSetOf(p);
|
|
3784
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3785
|
+
})();
|
|
3643
3786
|
let gateCond = null;
|
|
3644
3787
|
if (gate) {
|
|
3645
3788
|
const gateIdx = ctx.mapFns.length;
|
|
3646
3789
|
ctx.mapFns.push(gate);
|
|
3790
|
+
ctx.mapFnSrcs.push(null);
|
|
3647
3791
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3648
3792
|
}
|
|
3649
3793
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3702,13 +3846,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3702
3846
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3703
3847
|
return emitFirstMatch(def, ctx, pos);
|
|
3704
3848
|
}
|
|
3849
|
+
function pushMapFn(ctx, fn, src) {
|
|
3850
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3851
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3852
|
+
if (hit !== -1) return hit;
|
|
3853
|
+
}
|
|
3854
|
+
const idx = ctx.mapFns.length;
|
|
3855
|
+
ctx.mapFns.push(fn);
|
|
3856
|
+
ctx.mapFnSrcs.push(src);
|
|
3857
|
+
return idx;
|
|
3858
|
+
}
|
|
3705
3859
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3706
3860
|
const def = p._def;
|
|
3707
3861
|
if (def.tag === "transform") {
|
|
3708
3862
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3709
|
-
const fnIdx = ctx.
|
|
3710
|
-
ctx.mapFns.push(def.fn);
|
|
3711
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3863
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3712
3864
|
const vv = v(ctx);
|
|
3713
3865
|
return {
|
|
3714
3866
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3891,7 +4043,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3891
4043
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3892
4044
|
];
|
|
3893
4045
|
ctx.indent++;
|
|
3894
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4046
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3895
4047
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3896
4048
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3897
4049
|
if (def.skip.length > 0) {
|
|
@@ -3947,15 +4099,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3947
4099
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3948
4100
|
}
|
|
3949
4101
|
const i = ind(ctx);
|
|
3950
|
-
const capturesTrivia = mkType !== null || structural
|
|
3951
|
-
const clonesState = structural
|
|
4102
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4103
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3952
4104
|
const chV = v(ctx, "_ch");
|
|
3953
4105
|
const rawV = v(ctx, "_raw");
|
|
3954
|
-
const
|
|
4106
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4107
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4108
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3955
4109
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3956
4110
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3957
|
-
|
|
3958
|
-
const
|
|
4111
|
+
if (structural) ctx.needsHostReads = true;
|
|
4112
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4113
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4114
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3959
4115
|
const stmts = [
|
|
3960
4116
|
allocStmt,
|
|
3961
4117
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3966,7 +4122,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3966
4122
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3967
4123
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3968
4124
|
let stV = "undefined";
|
|
3969
|
-
if (
|
|
4125
|
+
if (structural) {
|
|
4126
|
+
stV = v(ctx, "_nst");
|
|
4127
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4128
|
+
} else if (clonesState) {
|
|
3970
4129
|
stV = v(ctx, "_nst");
|
|
3971
4130
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3972
4131
|
}
|
|
@@ -4075,6 +4234,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4075
4234
|
return { stmts, valueVar: valVar, endVar };
|
|
4076
4235
|
}
|
|
4077
4236
|
function emit(p, ctx, pos) {
|
|
4237
|
+
const usage = ctx.lazyUsage;
|
|
4238
|
+
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
|
|
4239
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4240
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4241
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4242
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4243
|
+
const existing = ctx.namedParsers.get(p);
|
|
4244
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4245
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4246
|
+
ctx.namedParsers.set(p, fnName);
|
|
4247
|
+
const savedIndent = ctx.indent;
|
|
4248
|
+
const savedFailLabel = ctx.failLabel;
|
|
4249
|
+
const savedRecord = ctx.recordFail;
|
|
4250
|
+
ctx.indent = 1;
|
|
4251
|
+
ctx.failLabel = "_pfail";
|
|
4252
|
+
ctx.recordFail = true;
|
|
4253
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4254
|
+
ctx.indent = savedIndent;
|
|
4255
|
+
ctx.failLabel = savedFailLabel;
|
|
4256
|
+
ctx.recordFail = savedRecord;
|
|
4257
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4258
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4259
|
+
}
|
|
4260
|
+
return emitDispatch(p, ctx, pos);
|
|
4261
|
+
}
|
|
4262
|
+
function emitDispatch(p, ctx, pos) {
|
|
4078
4263
|
const def = p._def;
|
|
4079
4264
|
switch (def.tag) {
|
|
4080
4265
|
case "literal":
|
|
@@ -4120,9 +4305,7 @@ function emit(p, ctx, pos) {
|
|
|
4120
4305
|
};
|
|
4121
4306
|
}
|
|
4122
4307
|
}
|
|
4123
|
-
const fnIdx = ctx.
|
|
4124
|
-
ctx.mapFns.push(def.fn);
|
|
4125
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4308
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4126
4309
|
const mv = v(ctx, "_mapped");
|
|
4127
4310
|
return {
|
|
4128
4311
|
stmts: [
|
|
@@ -4187,6 +4370,7 @@ function emit(p, ctx, pos) {
|
|
|
4187
4370
|
case "guard": {
|
|
4188
4371
|
const fnIdx = ctx.mapFns.length;
|
|
4189
4372
|
ctx.mapFns.push(def.predicate);
|
|
4373
|
+
ctx.mapFnSrcs.push(null);
|
|
4190
4374
|
const vv = v(ctx);
|
|
4191
4375
|
return {
|
|
4192
4376
|
stmts: [
|
|
@@ -4201,6 +4385,7 @@ function emit(p, ctx, pos) {
|
|
|
4201
4385
|
const evIdx = ctx.mapFns.length;
|
|
4202
4386
|
const extra = def.extra;
|
|
4203
4387
|
ctx.mapFns.push((() => extra));
|
|
4388
|
+
ctx.mapFnSrcs.push(null);
|
|
4204
4389
|
const innerParser = def.parser;
|
|
4205
4390
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4206
4391
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4320,9 +4505,58 @@ function childrenOf(def) {
|
|
|
4320
4505
|
return [];
|
|
4321
4506
|
}
|
|
4322
4507
|
}
|
|
4508
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4509
|
+
"sequence",
|
|
4510
|
+
"choice",
|
|
4511
|
+
"many",
|
|
4512
|
+
"oneOrMore",
|
|
4513
|
+
"optional",
|
|
4514
|
+
"sepBy",
|
|
4515
|
+
"transform",
|
|
4516
|
+
"node",
|
|
4517
|
+
"not"
|
|
4518
|
+
]);
|
|
4519
|
+
function isHoistableTag(tag) {
|
|
4520
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4521
|
+
}
|
|
4522
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4323
4523
|
function analyzeLazyUsage(root) {
|
|
4324
4524
|
return analyzeLazyUsageMulti([root]);
|
|
4325
4525
|
}
|
|
4526
|
+
function subtreeSizes(roots) {
|
|
4527
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4528
|
+
const sz = (p) => {
|
|
4529
|
+
const cached = sizes.get(p);
|
|
4530
|
+
if (cached !== void 0) return cached;
|
|
4531
|
+
const def = p._def;
|
|
4532
|
+
let s = 1;
|
|
4533
|
+
if (def.tag !== "lazy") {
|
|
4534
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4535
|
+
}
|
|
4536
|
+
sizes.set(p, s);
|
|
4537
|
+
return s;
|
|
4538
|
+
};
|
|
4539
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4540
|
+
const visit = (p) => {
|
|
4541
|
+
const def = p._def;
|
|
4542
|
+
if (def.tag === "lazy") {
|
|
4543
|
+
if (visitedLazy.has(p)) return;
|
|
4544
|
+
visitedLazy.add(p);
|
|
4545
|
+
let resolved;
|
|
4546
|
+
try {
|
|
4547
|
+
resolved = def.thunk();
|
|
4548
|
+
} catch {
|
|
4549
|
+
return;
|
|
4550
|
+
}
|
|
4551
|
+
visit(resolved);
|
|
4552
|
+
return;
|
|
4553
|
+
}
|
|
4554
|
+
sz(p);
|
|
4555
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4556
|
+
};
|
|
4557
|
+
for (const root of roots) visit(root);
|
|
4558
|
+
return sizes;
|
|
4559
|
+
}
|
|
4326
4560
|
function analyzeLazyUsageMulti(roots) {
|
|
4327
4561
|
const counts = /* @__PURE__ */ new Map();
|
|
4328
4562
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4349,10 +4583,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4349
4583
|
active.delete(p);
|
|
4350
4584
|
return;
|
|
4351
4585
|
}
|
|
4586
|
+
if (isHoistableTag(def.tag)) {
|
|
4587
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4588
|
+
}
|
|
4352
4589
|
for (const child of childrenOf(def)) walk2(child);
|
|
4353
4590
|
}
|
|
4354
|
-
|
|
4355
|
-
|
|
4591
|
+
const rootsArr = [...roots];
|
|
4592
|
+
for (const root of rootsArr) walk2(root);
|
|
4593
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4356
4594
|
}
|
|
4357
4595
|
function ruleDependencies(ruleMap) {
|
|
4358
4596
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4402,7 +4640,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4402
4640
|
buildFns: [],
|
|
4403
4641
|
buildSrcs: [],
|
|
4404
4642
|
runtimeParsers: [],
|
|
4405
|
-
needsCollator: false,
|
|
4406
4643
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4407
4644
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4408
4645
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4411,18 +4648,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4411
4648
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4412
4649
|
};
|
|
4413
4650
|
const r = emit(combinator, ctx, "_pos");
|
|
4414
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4415
|
-
` : "";
|
|
4416
4651
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4417
4652
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4653
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4418
4654
|
const source = [
|
|
4419
4655
|
...emptyTlDecls,
|
|
4656
|
+
...hostReadsDecls,
|
|
4420
4657
|
...ctx.regexDecls,
|
|
4421
4658
|
...ctx.expectedDecls,
|
|
4422
4659
|
"",
|
|
4423
4660
|
...namedPrelude,
|
|
4424
4661
|
ctx.namedFnDecls.join("\n\n"),
|
|
4425
|
-
|
|
4662
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4426
4663
|
` let pos = _pos`,
|
|
4427
4664
|
...r.stmts,
|
|
4428
4665
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4430,9 +4667,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4430
4667
|
].join("\n");
|
|
4431
4668
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4432
4669
|
...emptyTlDecls,
|
|
4670
|
+
...hostReadsDecls,
|
|
4433
4671
|
...ctx.regexDecls,
|
|
4434
4672
|
...ctx.expectedDecls,
|
|
4435
|
-
collatorDecl,
|
|
4436
4673
|
...namedPrelude,
|
|
4437
4674
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4438
4675
|
`let pos = _pos`,
|
|
@@ -4449,7 +4686,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4449
4686
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4450
4687
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4451
4688
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4452
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4689
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4453
4690
|
return {
|
|
4454
4691
|
source,
|
|
4455
4692
|
inlineExpression,
|
|
@@ -4505,7 +4742,6 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4505
4742
|
buildFns: [],
|
|
4506
4743
|
buildSrcs: [],
|
|
4507
4744
|
runtimeParsers: [],
|
|
4508
|
-
needsCollator: false,
|
|
4509
4745
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4510
4746
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4511
4747
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4636,12 +4872,12 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4636
4872
|
firstSets,
|
|
4637
4873
|
deps: ruleDependencies(ruleMap),
|
|
4638
4874
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4639
|
-
|
|
4875
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4640
4876
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4641
4877
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4642
4878
|
};
|
|
4643
4879
|
}
|
|
4644
|
-
function buildInlineExpression(ctx, r,
|
|
4880
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4645
4881
|
const bodyLines = [
|
|
4646
4882
|
` let pos = _pos`,
|
|
4647
4883
|
...r.stmts,
|
|
@@ -4655,14 +4891,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4655
4891
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4656
4892
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4657
4893
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4658
|
-
const
|
|
4894
|
+
const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
|
|
4895
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
|
|
4659
4896
|
if (!needsWrapper) return innerFn;
|
|
4660
4897
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4661
4898
|
const hoistedDecls = [
|
|
4662
4899
|
emptyTlDecl,
|
|
4900
|
+
hostReadsDecl,
|
|
4663
4901
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4664
4902
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4665
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4666
4903
|
mfDecl,
|
|
4667
4904
|
buildDecl,
|
|
4668
4905
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4679,6 +4916,172 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4679
4916
|
].join("\n");
|
|
4680
4917
|
}
|
|
4681
4918
|
|
|
4919
|
+
// src/combinators/scanTo.ts
|
|
4920
|
+
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4921
|
+
const meta = {
|
|
4922
|
+
firstSet: any(),
|
|
4923
|
+
canMatchNewline: true,
|
|
4924
|
+
isTrivia: false
|
|
4925
|
+
};
|
|
4926
|
+
return {
|
|
4927
|
+
_tag: "scanTo",
|
|
4928
|
+
_meta: meta,
|
|
4929
|
+
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
4930
|
+
parse(input, pos, ctx) {
|
|
4931
|
+
let cur = pos;
|
|
4932
|
+
const probeCtx = {
|
|
4933
|
+
trackLines: ctx.trackLines,
|
|
4934
|
+
state: ctx.state,
|
|
4935
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
4936
|
+
};
|
|
4937
|
+
const emit2 = (end) => {
|
|
4938
|
+
if (end > pos && cstCaptureActive(ctx)) {
|
|
4939
|
+
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
4940
|
+
pushCstLeaf(ctx, leaf);
|
|
4941
|
+
}
|
|
4942
|
+
};
|
|
4943
|
+
while (cur < input.length) {
|
|
4944
|
+
const s = sentinel.parse(input, cur, probeCtx);
|
|
4945
|
+
if (s.ok) {
|
|
4946
|
+
emit2(cur);
|
|
4947
|
+
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
4948
|
+
}
|
|
4949
|
+
let advanced = false;
|
|
4950
|
+
for (const skipper of skip2) {
|
|
4951
|
+
const r = skipper.parse(input, cur, probeCtx);
|
|
4952
|
+
if (r.ok && r.span.end > cur) {
|
|
4953
|
+
cur = r.span.end;
|
|
4954
|
+
advanced = true;
|
|
4955
|
+
break;
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
if (!advanced) cur++;
|
|
4959
|
+
}
|
|
4960
|
+
if (orEOF) {
|
|
4961
|
+
emit2(cur);
|
|
4962
|
+
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
4963
|
+
}
|
|
4964
|
+
const sentDef = sentinel._def;
|
|
4965
|
+
const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
|
|
4966
|
+
return { ok: false, expected, span: { start: pos, end: cur } };
|
|
4967
|
+
}
|
|
4968
|
+
};
|
|
4969
|
+
}
|
|
4970
|
+
function balanced(open, close, options = {}) {
|
|
4971
|
+
const self = ref();
|
|
4972
|
+
const skips = options.skip ?? [];
|
|
4973
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
4974
|
+
let bounded = true;
|
|
4975
|
+
for (const sk of skips) {
|
|
4976
|
+
const cs = firstSetClassChars(sk);
|
|
4977
|
+
if (cs === null) {
|
|
4978
|
+
bounded = false;
|
|
4979
|
+
break;
|
|
4980
|
+
}
|
|
4981
|
+
for (const ch of cs) stop.add(ch);
|
|
4982
|
+
}
|
|
4983
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
4984
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
4985
|
+
const inner = many(choice(self, ...skips, content));
|
|
4986
|
+
const combi = transform(
|
|
4987
|
+
sequence(literal(open), inner, expect(literal(close), close)),
|
|
4988
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
4989
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
4990
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
4991
|
+
);
|
|
4992
|
+
if (combi._def.tag === "transform") {
|
|
4993
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
4994
|
+
}
|
|
4995
|
+
self.define(combi);
|
|
4996
|
+
return combi;
|
|
4997
|
+
}
|
|
4998
|
+
function firstSetClassChars(c) {
|
|
4999
|
+
const fs = c._meta.firstSet;
|
|
5000
|
+
if (fs.kind === "empty") return [];
|
|
5001
|
+
if (fs.kind !== "ranges") return null;
|
|
5002
|
+
const out = [];
|
|
5003
|
+
for (const { lo, hi } of fs.ranges) {
|
|
5004
|
+
if (hi - lo > 8) return null;
|
|
5005
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
5006
|
+
}
|
|
5007
|
+
return out;
|
|
5008
|
+
}
|
|
5009
|
+
function escapeClassChar(ch) {
|
|
5010
|
+
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
5011
|
+
}
|
|
5012
|
+
|
|
5013
|
+
// src/compiler/ir-serialize.ts
|
|
5014
|
+
function evalRuleMapIR(ir) {
|
|
5015
|
+
const _tf = (child, src) => {
|
|
5016
|
+
let fn2;
|
|
5017
|
+
try {
|
|
5018
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5019
|
+
} catch {
|
|
5020
|
+
fn2 = () => {
|
|
5021
|
+
throw new Error("IR transform fn not materialized");
|
|
5022
|
+
};
|
|
5023
|
+
}
|
|
5024
|
+
const t = transform(child, fn2);
|
|
5025
|
+
t._def.fnSrc = src;
|
|
5026
|
+
return t;
|
|
5027
|
+
};
|
|
5028
|
+
const _nd = (type, child, src, opts) => {
|
|
5029
|
+
const n = node(type, child, void 0, opts);
|
|
5030
|
+
n._def.buildSrc = src;
|
|
5031
|
+
return n;
|
|
5032
|
+
};
|
|
5033
|
+
const fn = new Function(
|
|
5034
|
+
"rules",
|
|
5035
|
+
"ref",
|
|
5036
|
+
"regex",
|
|
5037
|
+
"literal",
|
|
5038
|
+
"keywords",
|
|
5039
|
+
"sequence",
|
|
5040
|
+
"choice",
|
|
5041
|
+
"many",
|
|
5042
|
+
"oneOrMore",
|
|
5043
|
+
"optional",
|
|
5044
|
+
"sepBy",
|
|
5045
|
+
"not",
|
|
5046
|
+
"node",
|
|
5047
|
+
"parser",
|
|
5048
|
+
"scanTo",
|
|
5049
|
+
"transform",
|
|
5050
|
+
"skip",
|
|
5051
|
+
"trivia",
|
|
5052
|
+
"label",
|
|
5053
|
+
"expect",
|
|
5054
|
+
"_tf",
|
|
5055
|
+
"_nd",
|
|
5056
|
+
`return (${ir})`
|
|
5057
|
+
);
|
|
5058
|
+
const map = fn(
|
|
5059
|
+
rules,
|
|
5060
|
+
ref,
|
|
5061
|
+
regex,
|
|
5062
|
+
literal,
|
|
5063
|
+
keywords,
|
|
5064
|
+
sequence,
|
|
5065
|
+
choice,
|
|
5066
|
+
many,
|
|
5067
|
+
oneOrMore,
|
|
5068
|
+
optional,
|
|
5069
|
+
sepBy,
|
|
5070
|
+
not,
|
|
5071
|
+
node,
|
|
5072
|
+
parser,
|
|
5073
|
+
scanTo,
|
|
5074
|
+
transform,
|
|
5075
|
+
skip,
|
|
5076
|
+
trivia,
|
|
5077
|
+
label,
|
|
5078
|
+
expect,
|
|
5079
|
+
_tf,
|
|
5080
|
+
_nd
|
|
5081
|
+
);
|
|
5082
|
+
return Object.entries(map);
|
|
5083
|
+
}
|
|
5084
|
+
|
|
4682
5085
|
// src/compiler/linker.ts
|
|
4683
5086
|
var _nsCounter = 0;
|
|
4684
5087
|
function linkable(rulesMap, ns) {
|
|
@@ -4721,13 +5124,13 @@ function fusedBody(pieces) {
|
|
|
4721
5124
|
}
|
|
4722
5125
|
const contributing = new Set(winner.values());
|
|
4723
5126
|
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4724
|
-
const
|
|
5127
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4725
5128
|
const lines = [
|
|
4726
5129
|
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4727
5130
|
"const _pfFail = {}",
|
|
4728
5131
|
"let _pfEnd",
|
|
4729
5132
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4730
|
-
...
|
|
5133
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4731
5134
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4732
5135
|
...[...contributing].flatMap((p) => p.prelude),
|
|
4733
5136
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
@@ -4763,9 +5166,18 @@ function fuseRules(pieces) {
|
|
|
4763
5166
|
return new Function("_env", body)(env);
|
|
4764
5167
|
}
|
|
4765
5168
|
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
5169
|
+
function isIRPiece(p) {
|
|
5170
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5171
|
+
}
|
|
5172
|
+
function materializePiece(p) {
|
|
5173
|
+
if (!isIRPiece(p)) return p;
|
|
5174
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5175
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5176
|
+
return pieces;
|
|
5177
|
+
}
|
|
4766
5178
|
function itemPieces(item) {
|
|
4767
5179
|
const carried = item[COMPOSED_PIECES];
|
|
4768
|
-
if (Array.isArray(carried)) return carried;
|
|
5180
|
+
if (Array.isArray(carried)) return carried.map(materializePiece);
|
|
4769
5181
|
if (item.ruleFns instanceof Map) return [item];
|
|
4770
5182
|
return [linkable(item)];
|
|
4771
5183
|
}
|
|
@@ -4861,100 +5273,6 @@ function deeperFail(a, b) {
|
|
|
4861
5273
|
return a.span.start >= b.span.start ? a : b;
|
|
4862
5274
|
}
|
|
4863
5275
|
|
|
4864
|
-
// src/combinators/scanTo.ts
|
|
4865
|
-
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4866
|
-
const meta = {
|
|
4867
|
-
firstSet: any(),
|
|
4868
|
-
canMatchNewline: true,
|
|
4869
|
-
isTrivia: false
|
|
4870
|
-
};
|
|
4871
|
-
return {
|
|
4872
|
-
_tag: "scanTo",
|
|
4873
|
-
_meta: meta,
|
|
4874
|
-
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
4875
|
-
parse(input, pos, ctx) {
|
|
4876
|
-
let cur = pos;
|
|
4877
|
-
const probeCtx = {
|
|
4878
|
-
trackLines: ctx.trackLines,
|
|
4879
|
-
state: ctx.state,
|
|
4880
|
-
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
4881
|
-
};
|
|
4882
|
-
const emit2 = (end) => {
|
|
4883
|
-
if (end > pos && cstCaptureActive(ctx)) {
|
|
4884
|
-
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
4885
|
-
pushCstLeaf(ctx, leaf);
|
|
4886
|
-
}
|
|
4887
|
-
};
|
|
4888
|
-
while (cur < input.length) {
|
|
4889
|
-
const s = sentinel.parse(input, cur, probeCtx);
|
|
4890
|
-
if (s.ok) {
|
|
4891
|
-
emit2(cur);
|
|
4892
|
-
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
4893
|
-
}
|
|
4894
|
-
let advanced = false;
|
|
4895
|
-
for (const skipper of skip2) {
|
|
4896
|
-
const r = skipper.parse(input, cur, probeCtx);
|
|
4897
|
-
if (r.ok && r.span.end > cur) {
|
|
4898
|
-
cur = r.span.end;
|
|
4899
|
-
advanced = true;
|
|
4900
|
-
break;
|
|
4901
|
-
}
|
|
4902
|
-
}
|
|
4903
|
-
if (!advanced) cur++;
|
|
4904
|
-
}
|
|
4905
|
-
if (orEOF) {
|
|
4906
|
-
emit2(cur);
|
|
4907
|
-
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
4908
|
-
}
|
|
4909
|
-
const sentDef = sentinel._def;
|
|
4910
|
-
const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
|
|
4911
|
-
return { ok: false, expected, span: { start: pos, end: cur } };
|
|
4912
|
-
}
|
|
4913
|
-
};
|
|
4914
|
-
}
|
|
4915
|
-
function balanced(open, close, options = {}) {
|
|
4916
|
-
const self = ref();
|
|
4917
|
-
const skips = options.skip ?? [];
|
|
4918
|
-
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
4919
|
-
let bounded = true;
|
|
4920
|
-
for (const sk of skips) {
|
|
4921
|
-
const cs = firstSetClassChars(sk);
|
|
4922
|
-
if (cs === null) {
|
|
4923
|
-
bounded = false;
|
|
4924
|
-
break;
|
|
4925
|
-
}
|
|
4926
|
-
for (const ch of cs) stop.add(ch);
|
|
4927
|
-
}
|
|
4928
|
-
const cls = [...stop].map(escapeClassChar).join("");
|
|
4929
|
-
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
4930
|
-
const inner = many(choice(self, ...skips, content));
|
|
4931
|
-
const combi = transform(
|
|
4932
|
-
sequence(literal(open), inner, expect(literal(close), close)),
|
|
4933
|
-
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
4934
|
-
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
4935
|
-
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
4936
|
-
);
|
|
4937
|
-
if (combi._def.tag === "transform") {
|
|
4938
|
-
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
4939
|
-
}
|
|
4940
|
-
self.define(combi);
|
|
4941
|
-
return combi;
|
|
4942
|
-
}
|
|
4943
|
-
function firstSetClassChars(c) {
|
|
4944
|
-
const fs = c._meta.firstSet;
|
|
4945
|
-
if (fs.kind === "empty") return [];
|
|
4946
|
-
if (fs.kind !== "ranges") return null;
|
|
4947
|
-
const out = [];
|
|
4948
|
-
for (const { lo, hi } of fs.ranges) {
|
|
4949
|
-
if (hi - lo > 8) return null;
|
|
4950
|
-
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
4951
|
-
}
|
|
4952
|
-
return out;
|
|
4953
|
-
}
|
|
4954
|
-
function escapeClassChar(ch) {
|
|
4955
|
-
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4956
|
-
}
|
|
4957
|
-
|
|
4958
5276
|
// src/combinators/recover-list.ts
|
|
4959
5277
|
function tolerantElement(item, sentinel, until) {
|
|
4960
5278
|
const el = transform(
|