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.cjs
CHANGED
|
@@ -126,6 +126,54 @@ function sequenceFirstSet(parsers) {
|
|
|
126
126
|
}
|
|
127
127
|
return fs2;
|
|
128
128
|
}
|
|
129
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
130
|
+
if (seen.has(p)) return any();
|
|
131
|
+
seen.add(p);
|
|
132
|
+
const fs2 = (c) => firstSetOf(c, seen);
|
|
133
|
+
const d = p._def;
|
|
134
|
+
switch (d.tag) {
|
|
135
|
+
case "literal":
|
|
136
|
+
case "regex":
|
|
137
|
+
case "keywords":
|
|
138
|
+
return p._meta.firstSet;
|
|
139
|
+
// terminals: no refs, cached set is exact
|
|
140
|
+
case "lazy":
|
|
141
|
+
try {
|
|
142
|
+
return fs2(d.thunk());
|
|
143
|
+
} catch {
|
|
144
|
+
return any();
|
|
145
|
+
}
|
|
146
|
+
case "choice": {
|
|
147
|
+
let out = empty();
|
|
148
|
+
for (const arm of d.parsers) out = union(out, fs2(arm));
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
case "sequence": {
|
|
152
|
+
let out = empty();
|
|
153
|
+
for (const term of d.parsers) {
|
|
154
|
+
out = union(out, fs2(term));
|
|
155
|
+
if (!matchesEmpty(term)) return out;
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
case "oneOrMore":
|
|
160
|
+
case "many":
|
|
161
|
+
case "optional":
|
|
162
|
+
case "transform":
|
|
163
|
+
case "label":
|
|
164
|
+
case "trivia":
|
|
165
|
+
case "node":
|
|
166
|
+
case "grammar":
|
|
167
|
+
case "expect":
|
|
168
|
+
return fs2(d.parser);
|
|
169
|
+
case "sepBy":
|
|
170
|
+
return fs2(d.parser);
|
|
171
|
+
case "skip":
|
|
172
|
+
return fs2(d.main);
|
|
173
|
+
default:
|
|
174
|
+
return p._meta.firstSet;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
129
177
|
function mergeRanges(ranges) {
|
|
130
178
|
if (ranges.length === 0) return [];
|
|
131
179
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -333,12 +381,16 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
|
333
381
|
}
|
|
334
382
|
|
|
335
383
|
// src/combinators/literal.ts
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
384
|
+
function asciiFoldEq(a, b) {
|
|
385
|
+
if (a.length !== b.length) return false;
|
|
386
|
+
for (let i = 0; i < a.length; i++) {
|
|
387
|
+
let ca = a.charCodeAt(i);
|
|
388
|
+
let cb = b.charCodeAt(i);
|
|
389
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
390
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
391
|
+
if (ca !== cb) return false;
|
|
340
392
|
}
|
|
341
|
-
return
|
|
393
|
+
return true;
|
|
342
394
|
}
|
|
343
395
|
function literal(value, opts = {}) {
|
|
344
396
|
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
@@ -353,8 +405,9 @@ function literal(value, opts = {}) {
|
|
|
353
405
|
const lower = value.toLowerCase();
|
|
354
406
|
const firstUpper = upper.codePointAt(0);
|
|
355
407
|
const firstLower = lower.codePointAt(0);
|
|
356
|
-
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? { kind: "ranges", ranges: [
|
|
357
|
-
{ lo:
|
|
408
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
|
|
409
|
+
{ lo: firstLower, hi: firstLower },
|
|
410
|
+
{ lo: firstUpper, hi: firstUpper }
|
|
358
411
|
] } : firstSet;
|
|
359
412
|
}
|
|
360
413
|
return {
|
|
@@ -367,7 +420,7 @@ function literal(value, opts = {}) {
|
|
|
367
420
|
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
368
421
|
}
|
|
369
422
|
const slice = input.slice(pos, end);
|
|
370
|
-
const matched = caseInsensitive ?
|
|
423
|
+
const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
|
|
371
424
|
if (matched) {
|
|
372
425
|
const span = { start: pos, end };
|
|
373
426
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
@@ -1260,6 +1313,41 @@ function markUnusedValues(root) {
|
|
|
1260
1313
|
visit(resolveRoot(root), true);
|
|
1261
1314
|
}
|
|
1262
1315
|
|
|
1316
|
+
// src/combinators/parser.ts
|
|
1317
|
+
function rules(factory) {
|
|
1318
|
+
const cache = {};
|
|
1319
|
+
const proxy = new Proxy(cache, {
|
|
1320
|
+
get(target, key) {
|
|
1321
|
+
if (typeof key !== "string") return void 0;
|
|
1322
|
+
const record = target;
|
|
1323
|
+
if (!(key in record)) {
|
|
1324
|
+
const r = ref();
|
|
1325
|
+
r._ruleName = key;
|
|
1326
|
+
record[key] = r;
|
|
1327
|
+
}
|
|
1328
|
+
return record[key];
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
const definitions = factory(proxy);
|
|
1332
|
+
for (const key of Object.keys(definitions)) {
|
|
1333
|
+
const placeholder = cache[key];
|
|
1334
|
+
const parser2 = definitions[key];
|
|
1335
|
+
if (placeholder !== void 0 && typeof placeholder.define === "function") {
|
|
1336
|
+
;
|
|
1337
|
+
placeholder.define(parser2);
|
|
1338
|
+
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
1339
|
+
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
1340
|
+
} else {
|
|
1341
|
+
;
|
|
1342
|
+
cache[key] = parser2;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
for (const key of Object.keys(definitions)) {
|
|
1346
|
+
markUnusedValues(cache[key]);
|
|
1347
|
+
}
|
|
1348
|
+
return cache;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1263
1351
|
// src/combinators/not.ts
|
|
1264
1352
|
function not(combinator) {
|
|
1265
1353
|
const meta = {
|
|
@@ -1366,6 +1454,20 @@ function transform(combinator, fn) {
|
|
|
1366
1454
|
}
|
|
1367
1455
|
};
|
|
1368
1456
|
}
|
|
1457
|
+
function skip(main, skipped) {
|
|
1458
|
+
return {
|
|
1459
|
+
_tag: "skip",
|
|
1460
|
+
_meta: main._meta,
|
|
1461
|
+
_def: { tag: "skip", main, skipped },
|
|
1462
|
+
parse(input, pos, ctx) {
|
|
1463
|
+
const result = main.parse(input, pos, ctx);
|
|
1464
|
+
if (!result.ok) return result;
|
|
1465
|
+
const s = skipped.parse(input, result.span.end, ctx);
|
|
1466
|
+
if (!s.ok) return result;
|
|
1467
|
+
return { ...result, span: { start: result.span.start, end: s.span.end } };
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1369
1471
|
function trivia(combinator) {
|
|
1370
1472
|
const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
|
|
1371
1473
|
return {
|
|
@@ -1596,8 +1698,8 @@ function classToRanges(cls) {
|
|
|
1596
1698
|
return parseClassRanges(body);
|
|
1597
1699
|
}
|
|
1598
1700
|
var foldEq = (cVar, cp) => {
|
|
1599
|
-
if (cp >= 65 && cp <= 90) return `(${cVar}
|
|
1600
|
-
if (cp >= 97 && cp <= 122) return `(${cVar}
|
|
1701
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`;
|
|
1702
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`;
|
|
1601
1703
|
return `${cVar} === ${cp}`;
|
|
1602
1704
|
};
|
|
1603
1705
|
function literalCodePoints(frag) {
|
|
@@ -1848,6 +1950,12 @@ function firstSetsDisjoint(a, b) {
|
|
|
1848
1950
|
return classDisjoint(a, b);
|
|
1849
1951
|
}
|
|
1850
1952
|
function fixedClassSeq(shape) {
|
|
1953
|
+
if (shape.kind === "litFold") {
|
|
1954
|
+
return shape.open.map((cp) => {
|
|
1955
|
+
const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp;
|
|
1956
|
+
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 };
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1851
1959
|
if (shape.kind !== "seq") return null;
|
|
1852
1960
|
const out = [];
|
|
1853
1961
|
for (const p of shape.parts) {
|
|
@@ -2269,12 +2377,34 @@ function parseScanShapeCore(source) {
|
|
|
2269
2377
|
}
|
|
2270
2378
|
function scanShapeFromRegex(source, flags) {
|
|
2271
2379
|
if (/[msu]/.test(flags)) return null;
|
|
2272
|
-
if (/i/.test(flags))
|
|
2273
|
-
const open = literalCodePoints(source);
|
|
2274
|
-
return open ? { kind: "litFold", open } : null;
|
|
2275
|
-
}
|
|
2380
|
+
if (/i/.test(flags)) return caseFoldShape(source);
|
|
2276
2381
|
return parseScanShape(source);
|
|
2277
2382
|
}
|
|
2383
|
+
function caseFoldShape(source) {
|
|
2384
|
+
const la = stripTrailingLookahead(source);
|
|
2385
|
+
if (la) {
|
|
2386
|
+
const inner = caseFoldLiteralOrAlt(la.base);
|
|
2387
|
+
if (!inner) return null;
|
|
2388
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2389
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2390
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2391
|
+
}
|
|
2392
|
+
return caseFoldLiteralOrAlt(source);
|
|
2393
|
+
}
|
|
2394
|
+
function caseFoldLiteralOrAlt(source) {
|
|
2395
|
+
const asciiOnly = (cps) => cps.every((cp) => cp <= 127);
|
|
2396
|
+
const open = literalCodePoints(source);
|
|
2397
|
+
if (open) return asciiOnly(open) ? { kind: "litFold", open } : null;
|
|
2398
|
+
const arms = splitTopLevelAlternation(source);
|
|
2399
|
+
if (!arms || arms.length < 2) return null;
|
|
2400
|
+
const litArms = [];
|
|
2401
|
+
for (const arm of arms) {
|
|
2402
|
+
const cp = literalCodePoints(arm);
|
|
2403
|
+
if (!cp || !asciiOnly(cp)) return null;
|
|
2404
|
+
litArms.push({ kind: "litFold", open: cp });
|
|
2405
|
+
}
|
|
2406
|
+
return { kind: "alt", arms: litArms, disjoint: false, firsts: litArms.map(() => null) };
|
|
2407
|
+
}
|
|
2278
2408
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
2279
2409
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
2280
2410
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
@@ -2286,6 +2416,25 @@ function stringHardStop(shape) {
|
|
|
2286
2416
|
);
|
|
2287
2417
|
}
|
|
2288
2418
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2419
|
+
var ALT_SWITCH_RANGE_LIMIT = 4;
|
|
2420
|
+
var ALT_SWITCH_MAX_CASES = 48;
|
|
2421
|
+
var ALT_SWITCH_MIN_CASES = 3;
|
|
2422
|
+
function planAltSwitch(firsts) {
|
|
2423
|
+
const cases = [];
|
|
2424
|
+
let total = 0;
|
|
2425
|
+
for (const fs2 of firsts) {
|
|
2426
|
+
if (!fs2 || fs2.negated) return { kind: "if" };
|
|
2427
|
+
const pts = [];
|
|
2428
|
+
for (const [lo, hi] of fs2.ranges) {
|
|
2429
|
+
if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2430
|
+
for (let cp = lo; cp <= hi; cp++) pts.push(cp);
|
|
2431
|
+
}
|
|
2432
|
+
total += pts.length;
|
|
2433
|
+
if (total > ALT_SWITCH_MAX_CASES) return { kind: "if" };
|
|
2434
|
+
cases.push(pts);
|
|
2435
|
+
}
|
|
2436
|
+
return total >= ALT_SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2437
|
+
}
|
|
2289
2438
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2290
2439
|
if (shape.kind === "lookahead") {
|
|
2291
2440
|
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
@@ -2310,18 +2459,57 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
2310
2459
|
const okV = mint("_ok");
|
|
2311
2460
|
const endV2 = mint("_end");
|
|
2312
2461
|
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2462
|
+
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
2463
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2464
|
+
for (const a of shape.arms) {
|
|
2465
|
+
const open = a.open;
|
|
2466
|
+
const f = open[0];
|
|
2467
|
+
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
2468
|
+
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
2469
|
+
}
|
|
2470
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2471
|
+
for (const [lower, arms] of groups) {
|
|
2472
|
+
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
2473
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2474
|
+
for (const open of arms) {
|
|
2475
|
+
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
2476
|
+
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
2477
|
+
lines.push(`${ind2} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
2478
|
+
}
|
|
2479
|
+
lines.push(`${ind2} break`);
|
|
2480
|
+
lines.push(`${ind2} }`);
|
|
2481
|
+
}
|
|
2482
|
+
lines.push(`${ind2}}`);
|
|
2483
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2484
|
+
}
|
|
2313
2485
|
if (shape.disjoint) {
|
|
2314
|
-
const
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2486
|
+
const plan = planAltSwitch(shape.firsts);
|
|
2487
|
+
if (plan.kind === "switch") {
|
|
2488
|
+
lines.push(`${ind2}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
2489
|
+
shape.arms.forEach((arm, k) => {
|
|
2490
|
+
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
2491
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2492
|
+
lines.push(`${ind2} ${labels} {`);
|
|
2493
|
+
lines.push(...m.setup);
|
|
2494
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2495
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2496
|
+
lines.push(`${ind2} break`);
|
|
2497
|
+
lines.push(`${ind2} }`);
|
|
2498
|
+
});
|
|
2323
2499
|
lines.push(`${ind2}}`);
|
|
2324
|
-
}
|
|
2500
|
+
} else {
|
|
2501
|
+
const c0 = codeAt(start, firstChar);
|
|
2502
|
+
shape.arms.forEach((arm, k) => {
|
|
2503
|
+
const fs2 = shape.firsts[k];
|
|
2504
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2505
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2506
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2507
|
+
lines.push(...m.setup);
|
|
2508
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2509
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2510
|
+
lines.push(`${ind2}}`);
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2325
2513
|
} else {
|
|
2326
2514
|
const lbl = mint("_alt");
|
|
2327
2515
|
lines.push(`${ind2}${lbl}: {`);
|
|
@@ -2809,6 +2997,16 @@ function tryInlineDestructureTransform(src, valueVars) {
|
|
|
2809
2997
|
}
|
|
2810
2998
|
|
|
2811
2999
|
// src/compiler/codegen.ts
|
|
3000
|
+
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 }";
|
|
3001
|
+
var _loweringSink = null;
|
|
3002
|
+
function beginLoweringCapture() {
|
|
3003
|
+
_loweringSink = /* @__PURE__ */ new Set();
|
|
3004
|
+
}
|
|
3005
|
+
function endLoweringCapture() {
|
|
3006
|
+
const misses = _loweringSink ? [..._loweringSink] : [];
|
|
3007
|
+
_loweringSink = null;
|
|
3008
|
+
return misses;
|
|
3009
|
+
}
|
|
2812
3010
|
function v(ctx, prefix = "_v") {
|
|
2813
3011
|
return `${prefix}${ctx.vars++}`;
|
|
2814
3012
|
}
|
|
@@ -2898,16 +3096,12 @@ function namedFnPrelude() {
|
|
|
2898
3096
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
2899
3097
|
ctx.namedFnDecls.push([
|
|
2900
3098
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
2901
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
2902
3099
|
` _pfail: {`,
|
|
2903
3100
|
...reindentStmts(bodyStmts, 2),
|
|
2904
|
-
`
|
|
2905
|
-
`
|
|
2906
|
-
` _pfok = true`,
|
|
3101
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3102
|
+
` return ${valueVar}`,
|
|
2907
3103
|
` }`,
|
|
2908
|
-
`
|
|
2909
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
2910
|
-
` return _pfv`,
|
|
3104
|
+
` return ${NAMED_FN_FAIL}`,
|
|
2911
3105
|
`}`
|
|
2912
3106
|
].join("\n"));
|
|
2913
3107
|
}
|
|
@@ -3207,13 +3401,14 @@ function emitLit(def, ctx, pos) {
|
|
|
3207
3401
|
const vv = v(ctx);
|
|
3208
3402
|
const expectedStr = JSON.stringify(JSON.stringify(value));
|
|
3209
3403
|
const stmts = [];
|
|
3210
|
-
if (caseInsensitive) {
|
|
3211
|
-
|
|
3404
|
+
if (caseInsensitive && len > 0) {
|
|
3405
|
+
const match = Array.from(
|
|
3406
|
+
{ length: len },
|
|
3407
|
+
(_, i) => `(${foldEq(`input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""})`, value.charCodeAt(i))})`
|
|
3408
|
+
).join(" && ");
|
|
3212
3409
|
stmts.push(
|
|
3213
|
-
...emitIfFail(ctx, `${pos} + ${len} > input.length`, failBody(ctx, expectedStr, pos)),
|
|
3214
|
-
`${ind(ctx)}const ${vv}
|
|
3215
|
-
...emitIfFail(ctx, `_collator.compare(${vv}_s, ${JSON.stringify(value)}) !== 0`, failBody(ctx, expectedStr, pos)),
|
|
3216
|
-
`${ind(ctx)}const ${vv} = ${vv}_s`
|
|
3410
|
+
...emitIfFail(ctx, `${pos} + ${len} > input.length || !(${match})`, failBody(ctx, expectedStr, pos)),
|
|
3411
|
+
`${ind(ctx)}const ${vv} = input.slice(${pos}, ${pos} + ${len})`
|
|
3217
3412
|
);
|
|
3218
3413
|
} else if (len === 0) {
|
|
3219
3414
|
stmts.push(`${ind(ctx)}const ${vv} = ''`);
|
|
@@ -3334,6 +3529,7 @@ function emitRegex(def, ctx, pos) {
|
|
|
3334
3529
|
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3335
3530
|
}
|
|
3336
3531
|
}
|
|
3532
|
+
_loweringSink?.add(`/${def.source}/${def.flags}`);
|
|
3337
3533
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3338
3534
|
const key = `${def.source}/${flags}`;
|
|
3339
3535
|
let rName = ctx.regexMap.get(key);
|
|
@@ -3606,11 +3802,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3606
3802
|
const atStart = failsAtStart(p);
|
|
3607
3803
|
const staticFx = armStaticExpected(ctx, p);
|
|
3608
3804
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3609
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3805
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3806
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3807
|
+
const deep = firstSetOf(p);
|
|
3808
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3809
|
+
})();
|
|
3610
3810
|
let gateCond = null;
|
|
3611
3811
|
if (gate) {
|
|
3612
3812
|
const gateIdx = ctx.mapFns.length;
|
|
3613
3813
|
ctx.mapFns.push(gate);
|
|
3814
|
+
ctx.mapFnSrcs.push(null);
|
|
3614
3815
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3615
3816
|
}
|
|
3616
3817
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3669,13 +3870,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3669
3870
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3670
3871
|
return emitFirstMatch(def, ctx, pos);
|
|
3671
3872
|
}
|
|
3873
|
+
function pushMapFn(ctx, fn, src) {
|
|
3874
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3875
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3876
|
+
if (hit !== -1) return hit;
|
|
3877
|
+
}
|
|
3878
|
+
const idx = ctx.mapFns.length;
|
|
3879
|
+
ctx.mapFns.push(fn);
|
|
3880
|
+
ctx.mapFnSrcs.push(src);
|
|
3881
|
+
return idx;
|
|
3882
|
+
}
|
|
3672
3883
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3673
3884
|
const def = p._def;
|
|
3674
3885
|
if (def.tag === "transform") {
|
|
3675
3886
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3676
|
-
const fnIdx = ctx.
|
|
3677
|
-
ctx.mapFns.push(def.fn);
|
|
3678
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3887
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3679
3888
|
const vv = v(ctx);
|
|
3680
3889
|
return {
|
|
3681
3890
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3858,7 +4067,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3858
4067
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3859
4068
|
];
|
|
3860
4069
|
ctx.indent++;
|
|
3861
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4070
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3862
4071
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3863
4072
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3864
4073
|
if (def.skip.length > 0) {
|
|
@@ -3914,15 +4123,19 @@ function emitNode(def, ctx, pos) {
|
|
|
3914
4123
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3915
4124
|
}
|
|
3916
4125
|
const i = ind(ctx);
|
|
3917
|
-
const capturesTrivia = mkType !== null || structural
|
|
3918
|
-
const clonesState = structural
|
|
4126
|
+
const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
|
|
4127
|
+
const clonesState = !structural && buildReadsState(def);
|
|
3919
4128
|
const chV = v(ctx, "_ch");
|
|
3920
4129
|
const rawV = v(ctx, "_raw");
|
|
3921
|
-
const
|
|
4130
|
+
const capTLv = structural ? v(ctx, "_ctl") : null;
|
|
4131
|
+
const capSTv = structural ? v(ctx, "_cst") : null;
|
|
4132
|
+
const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
3922
4133
|
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
3923
4134
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
3924
|
-
|
|
3925
|
-
const
|
|
4135
|
+
if (structural) ctx.needsHostReads = true;
|
|
4136
|
+
const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
|
|
4137
|
+
${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
4138
|
+
const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
|
|
3926
4139
|
const stmts = [
|
|
3927
4140
|
allocStmt,
|
|
3928
4141
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
@@ -3933,7 +4146,10 @@ function emitNode(def, ctx, pos) {
|
|
|
3933
4146
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
3934
4147
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
3935
4148
|
let stV = "undefined";
|
|
3936
|
-
if (
|
|
4149
|
+
if (structural) {
|
|
4150
|
+
stV = v(ctx, "_nst");
|
|
4151
|
+
stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
4152
|
+
} else if (clonesState) {
|
|
3937
4153
|
stV = v(ctx, "_nst");
|
|
3938
4154
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3939
4155
|
}
|
|
@@ -4042,6 +4258,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4042
4258
|
return { stmts, valueVar: valVar, endVar };
|
|
4043
4259
|
}
|
|
4044
4260
|
function emit(p, ctx, pos) {
|
|
4261
|
+
const usage = ctx.lazyUsage;
|
|
4262
|
+
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
|
|
4263
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4264
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4265
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4266
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4267
|
+
const existing = ctx.namedParsers.get(p);
|
|
4268
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4269
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4270
|
+
ctx.namedParsers.set(p, fnName);
|
|
4271
|
+
const savedIndent = ctx.indent;
|
|
4272
|
+
const savedFailLabel = ctx.failLabel;
|
|
4273
|
+
const savedRecord = ctx.recordFail;
|
|
4274
|
+
ctx.indent = 1;
|
|
4275
|
+
ctx.failLabel = "_pfail";
|
|
4276
|
+
ctx.recordFail = true;
|
|
4277
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4278
|
+
ctx.indent = savedIndent;
|
|
4279
|
+
ctx.failLabel = savedFailLabel;
|
|
4280
|
+
ctx.recordFail = savedRecord;
|
|
4281
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4282
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4283
|
+
}
|
|
4284
|
+
return emitDispatch(p, ctx, pos);
|
|
4285
|
+
}
|
|
4286
|
+
function emitDispatch(p, ctx, pos) {
|
|
4045
4287
|
const def = p._def;
|
|
4046
4288
|
switch (def.tag) {
|
|
4047
4289
|
case "literal":
|
|
@@ -4087,9 +4329,7 @@ function emit(p, ctx, pos) {
|
|
|
4087
4329
|
};
|
|
4088
4330
|
}
|
|
4089
4331
|
}
|
|
4090
|
-
const fnIdx = ctx.
|
|
4091
|
-
ctx.mapFns.push(def.fn);
|
|
4092
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4332
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4093
4333
|
const mv = v(ctx, "_mapped");
|
|
4094
4334
|
return {
|
|
4095
4335
|
stmts: [
|
|
@@ -4154,6 +4394,7 @@ function emit(p, ctx, pos) {
|
|
|
4154
4394
|
case "guard": {
|
|
4155
4395
|
const fnIdx = ctx.mapFns.length;
|
|
4156
4396
|
ctx.mapFns.push(def.predicate);
|
|
4397
|
+
ctx.mapFnSrcs.push(null);
|
|
4157
4398
|
const vv = v(ctx);
|
|
4158
4399
|
return {
|
|
4159
4400
|
stmts: [
|
|
@@ -4168,6 +4409,7 @@ function emit(p, ctx, pos) {
|
|
|
4168
4409
|
const evIdx = ctx.mapFns.length;
|
|
4169
4410
|
const extra = def.extra;
|
|
4170
4411
|
ctx.mapFns.push((() => extra));
|
|
4412
|
+
ctx.mapFnSrcs.push(null);
|
|
4171
4413
|
const innerParser = def.parser;
|
|
4172
4414
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4173
4415
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4287,9 +4529,58 @@ function childrenOf(def) {
|
|
|
4287
4529
|
return [];
|
|
4288
4530
|
}
|
|
4289
4531
|
}
|
|
4532
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4533
|
+
"sequence",
|
|
4534
|
+
"choice",
|
|
4535
|
+
"many",
|
|
4536
|
+
"oneOrMore",
|
|
4537
|
+
"optional",
|
|
4538
|
+
"sepBy",
|
|
4539
|
+
"transform",
|
|
4540
|
+
"node",
|
|
4541
|
+
"not"
|
|
4542
|
+
]);
|
|
4543
|
+
function isHoistableTag(tag) {
|
|
4544
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4545
|
+
}
|
|
4546
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4290
4547
|
function analyzeLazyUsage(root) {
|
|
4291
4548
|
return analyzeLazyUsageMulti([root]);
|
|
4292
4549
|
}
|
|
4550
|
+
function subtreeSizes(roots) {
|
|
4551
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4552
|
+
const sz = (p) => {
|
|
4553
|
+
const cached = sizes.get(p);
|
|
4554
|
+
if (cached !== void 0) return cached;
|
|
4555
|
+
const def = p._def;
|
|
4556
|
+
let s = 1;
|
|
4557
|
+
if (def.tag !== "lazy") {
|
|
4558
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4559
|
+
}
|
|
4560
|
+
sizes.set(p, s);
|
|
4561
|
+
return s;
|
|
4562
|
+
};
|
|
4563
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4564
|
+
const visit = (p) => {
|
|
4565
|
+
const def = p._def;
|
|
4566
|
+
if (def.tag === "lazy") {
|
|
4567
|
+
if (visitedLazy.has(p)) return;
|
|
4568
|
+
visitedLazy.add(p);
|
|
4569
|
+
let resolved;
|
|
4570
|
+
try {
|
|
4571
|
+
resolved = def.thunk();
|
|
4572
|
+
} catch {
|
|
4573
|
+
return;
|
|
4574
|
+
}
|
|
4575
|
+
visit(resolved);
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4578
|
+
sz(p);
|
|
4579
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4580
|
+
};
|
|
4581
|
+
for (const root of roots) visit(root);
|
|
4582
|
+
return sizes;
|
|
4583
|
+
}
|
|
4293
4584
|
function analyzeLazyUsageMulti(roots) {
|
|
4294
4585
|
const counts = /* @__PURE__ */ new Map();
|
|
4295
4586
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4316,10 +4607,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4316
4607
|
active.delete(p);
|
|
4317
4608
|
return;
|
|
4318
4609
|
}
|
|
4610
|
+
if (isHoistableTag(def.tag)) {
|
|
4611
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4612
|
+
}
|
|
4319
4613
|
for (const child of childrenOf(def)) walk2(child);
|
|
4320
4614
|
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4615
|
+
const rootsArr = [...roots];
|
|
4616
|
+
for (const root of rootsArr) walk2(root);
|
|
4617
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4323
4618
|
}
|
|
4324
4619
|
function ruleDependencies(ruleMap) {
|
|
4325
4620
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4369,7 +4664,6 @@ function compile(combinator, mapFnSources) {
|
|
|
4369
4664
|
buildFns: [],
|
|
4370
4665
|
buildSrcs: [],
|
|
4371
4666
|
runtimeParsers: [],
|
|
4372
|
-
needsCollator: false,
|
|
4373
4667
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4374
4668
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4375
4669
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4378,18 +4672,18 @@ function compile(combinator, mapFnSources) {
|
|
|
4378
4672
|
lazyUsage: analyzeLazyUsage(combinator)
|
|
4379
4673
|
};
|
|
4380
4674
|
const r = emit(combinator, ctx, "_pos");
|
|
4381
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4382
|
-
` : "";
|
|
4383
4675
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
4384
4676
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
4677
|
+
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
4385
4678
|
const source = [
|
|
4386
4679
|
...emptyTlDecls,
|
|
4680
|
+
...hostReadsDecls,
|
|
4387
4681
|
...ctx.regexDecls,
|
|
4388
4682
|
...ctx.expectedDecls,
|
|
4389
4683
|
"",
|
|
4390
4684
|
...namedPrelude,
|
|
4391
4685
|
ctx.namedFnDecls.join("\n\n"),
|
|
4392
|
-
|
|
4686
|
+
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
4393
4687
|
` let pos = _pos`,
|
|
4394
4688
|
...r.stmts,
|
|
4395
4689
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
@@ -4397,9 +4691,9 @@ function compile(combinator, mapFnSources) {
|
|
|
4397
4691
|
].join("\n");
|
|
4398
4692
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
4399
4693
|
...emptyTlDecls,
|
|
4694
|
+
...hostReadsDecls,
|
|
4400
4695
|
...ctx.regexDecls,
|
|
4401
4696
|
...ctx.expectedDecls,
|
|
4402
|
-
collatorDecl,
|
|
4403
4697
|
...namedPrelude,
|
|
4404
4698
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
4405
4699
|
`let pos = _pos`,
|
|
@@ -4416,7 +4710,7 @@ function compile(combinator, mapFnSources) {
|
|
|
4416
4710
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
4417
4711
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
4418
4712
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
4419
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r,
|
|
4713
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
4420
4714
|
return {
|
|
4421
4715
|
source,
|
|
4422
4716
|
inlineExpression,
|
|
@@ -4461,7 +4755,6 @@ function compileRuleMap(ruleMap) {
|
|
|
4461
4755
|
buildFns: [],
|
|
4462
4756
|
buildSrcs: [],
|
|
4463
4757
|
runtimeParsers: [],
|
|
4464
|
-
needsCollator: false,
|
|
4465
4758
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4466
4759
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4467
4760
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4480,8 +4773,6 @@ function compileRuleMap(ruleMap) {
|
|
|
4480
4773
|
}
|
|
4481
4774
|
ctx.ruleNames = ruleNames;
|
|
4482
4775
|
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
4483
|
-
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
4484
|
-
` : "";
|
|
4485
4776
|
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
4486
4777
|
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
4487
4778
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
@@ -4493,9 +4784,9 @@ function compileRuleMap(ruleMap) {
|
|
|
4493
4784
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4494
4785
|
const hoistedDecls = [
|
|
4495
4786
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
4787
|
+
ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
|
|
4496
4788
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4497
4789
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4498
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4499
4790
|
mfDecl,
|
|
4500
4791
|
buildDecl,
|
|
4501
4792
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4548,7 +4839,6 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4548
4839
|
buildFns: [],
|
|
4549
4840
|
buildSrcs: [],
|
|
4550
4841
|
runtimeParsers: [],
|
|
4551
|
-
needsCollator: false,
|
|
4552
4842
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
4553
4843
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4554
4844
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
@@ -4679,12 +4969,12 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4679
4969
|
firstSets,
|
|
4680
4970
|
deps: ruleDependencies(ruleMap),
|
|
4681
4971
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4682
|
-
|
|
4972
|
+
needsHostReads: !!ctx.needsHostReads,
|
|
4683
4973
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4684
4974
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4685
4975
|
};
|
|
4686
4976
|
}
|
|
4687
|
-
function buildInlineExpression(ctx, r,
|
|
4977
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
4688
4978
|
const bodyLines = [
|
|
4689
4979
|
` let pos = _pos`,
|
|
4690
4980
|
...r.stmts,
|
|
@@ -4698,14 +4988,15 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4698
4988
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
4699
4989
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
4700
4990
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
4701
|
-
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;
|
|
4702
4993
|
if (!needsWrapper) return innerFn;
|
|
4703
4994
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
4704
4995
|
const hoistedDecls = [
|
|
4705
4996
|
emptyTlDecl,
|
|
4997
|
+
hostReadsDecl,
|
|
4706
4998
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4707
4999
|
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
4708
|
-
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
4709
5000
|
mfDecl,
|
|
4710
5001
|
buildDecl,
|
|
4711
5002
|
...namedPrelude.map((l) => ` ${l}`),
|
|
@@ -4722,64 +5013,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4722
5013
|
].join("\n");
|
|
4723
5014
|
}
|
|
4724
5015
|
|
|
4725
|
-
// src/compiler/linker.ts
|
|
4726
|
-
function fusedBody(pieces) {
|
|
4727
|
-
const winner = /* @__PURE__ */ new Map();
|
|
4728
|
-
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4729
|
-
for (const [k, p] of winner) {
|
|
4730
|
-
for (const d of p.deps.get(k) ?? []) {
|
|
4731
|
-
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4732
|
-
}
|
|
4733
|
-
}
|
|
4734
|
-
const contributing = new Set(winner.values());
|
|
4735
|
-
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4736
|
-
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4737
|
-
const lines = [
|
|
4738
|
-
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4739
|
-
"const _pfFail = {}",
|
|
4740
|
-
"let _pfEnd",
|
|
4741
|
-
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4742
|
-
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4743
|
-
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4744
|
-
...[...contributing].flatMap((p) => p.prelude),
|
|
4745
|
-
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4746
|
-
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4747
|
-
];
|
|
4748
|
-
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4749
|
-
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4750
|
-
const finalFS = /* @__PURE__ */ new Map();
|
|
4751
|
-
for (const [k, p] of winner) {
|
|
4752
|
-
const fs2 = p.firstSets?.get(k);
|
|
4753
|
-
if (fs2) finalFS.set(k, fs2);
|
|
4754
|
-
}
|
|
4755
|
-
const body = rawBody.replace(
|
|
4756
|
-
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4757
|
-
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4758
|
-
// matches every well-formed placeholder.
|
|
4759
|
-
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4760
|
-
(_m, name, codevar) => {
|
|
4761
|
-
const fs2 = finalFS.get(name);
|
|
4762
|
-
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4763
|
-
return `(${firstSetCond(codevar, fs2)})`;
|
|
4764
|
-
}
|
|
4765
|
-
);
|
|
4766
|
-
const env = {};
|
|
4767
|
-
for (const p of contributing) {
|
|
4768
|
-
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4769
|
-
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4770
|
-
}
|
|
4771
|
-
return { body, env };
|
|
4772
|
-
}
|
|
4773
|
-
function emitFusedSource(pieces) {
|
|
4774
|
-
const { body, env } = fusedBody(pieces);
|
|
4775
|
-
if (Object.keys(env).length > 0) {
|
|
4776
|
-
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4777
|
-
}
|
|
4778
|
-
return `/* @__PURE__ */ (() => {
|
|
4779
|
-
${body}
|
|
4780
|
-
})()`;
|
|
4781
|
-
}
|
|
4782
|
-
|
|
4783
5016
|
// src/combinators/scanTo.ts
|
|
4784
5017
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4785
5018
|
const meta = {
|
|
@@ -4874,6 +5107,354 @@ function escapeClassChar(ch) {
|
|
|
4874
5107
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4875
5108
|
}
|
|
4876
5109
|
|
|
5110
|
+
// src/compiler/ir-serialize.ts
|
|
5111
|
+
function childrenOf2(def) {
|
|
5112
|
+
switch (def.tag) {
|
|
5113
|
+
case "sequence":
|
|
5114
|
+
case "choice":
|
|
5115
|
+
return def.parsers;
|
|
5116
|
+
case "many":
|
|
5117
|
+
case "oneOrMore":
|
|
5118
|
+
case "optional":
|
|
5119
|
+
case "transform":
|
|
5120
|
+
case "trivia":
|
|
5121
|
+
case "label":
|
|
5122
|
+
case "not":
|
|
5123
|
+
case "node":
|
|
5124
|
+
case "expect":
|
|
5125
|
+
return [def.parser];
|
|
5126
|
+
case "grammar":
|
|
5127
|
+
return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
|
|
5128
|
+
case "sepBy":
|
|
5129
|
+
return [def.parser, def.separator];
|
|
5130
|
+
case "skip":
|
|
5131
|
+
return [def.main, def.skipped];
|
|
5132
|
+
case "scanTo":
|
|
5133
|
+
return [def.sentinel, ...def.skip];
|
|
5134
|
+
case "lazy":
|
|
5135
|
+
case "literal":
|
|
5136
|
+
case "regex":
|
|
5137
|
+
case "keywords":
|
|
5138
|
+
case "guard":
|
|
5139
|
+
case "withCtx":
|
|
5140
|
+
case "recover":
|
|
5141
|
+
case "unknown":
|
|
5142
|
+
return [];
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
var ruleNameOf = (c) => c._ruleName;
|
|
5146
|
+
function lazyTarget(c) {
|
|
5147
|
+
if (c._def.tag !== "lazy") return null;
|
|
5148
|
+
try {
|
|
5149
|
+
return c._def.thunk();
|
|
5150
|
+
} catch {
|
|
5151
|
+
return null;
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
var Unserializable = class extends Error {
|
|
5155
|
+
};
|
|
5156
|
+
function serializeRuleMap(ruleMap) {
|
|
5157
|
+
try {
|
|
5158
|
+
return new Serializer(ruleMap).run();
|
|
5159
|
+
} catch (e) {
|
|
5160
|
+
if (e instanceof Unserializable) {
|
|
5161
|
+
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
5162
|
+
return null;
|
|
5163
|
+
}
|
|
5164
|
+
throw e;
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
function evalRuleMapIR(ir) {
|
|
5168
|
+
const _tf = (child, src) => {
|
|
5169
|
+
let fn2;
|
|
5170
|
+
try {
|
|
5171
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5172
|
+
} catch {
|
|
5173
|
+
fn2 = () => {
|
|
5174
|
+
throw new Error("IR transform fn not materialized");
|
|
5175
|
+
};
|
|
5176
|
+
}
|
|
5177
|
+
const t = transform(child, fn2);
|
|
5178
|
+
t._def.fnSrc = src;
|
|
5179
|
+
return t;
|
|
5180
|
+
};
|
|
5181
|
+
const _nd = (type, child, src, opts) => {
|
|
5182
|
+
const n = node(type, child, void 0, opts);
|
|
5183
|
+
n._def.buildSrc = src;
|
|
5184
|
+
return n;
|
|
5185
|
+
};
|
|
5186
|
+
const fn = new Function(
|
|
5187
|
+
"rules",
|
|
5188
|
+
"ref",
|
|
5189
|
+
"regex",
|
|
5190
|
+
"literal",
|
|
5191
|
+
"keywords",
|
|
5192
|
+
"sequence",
|
|
5193
|
+
"choice",
|
|
5194
|
+
"many",
|
|
5195
|
+
"oneOrMore",
|
|
5196
|
+
"optional",
|
|
5197
|
+
"sepBy",
|
|
5198
|
+
"not",
|
|
5199
|
+
"node",
|
|
5200
|
+
"parser",
|
|
5201
|
+
"scanTo",
|
|
5202
|
+
"transform",
|
|
5203
|
+
"skip",
|
|
5204
|
+
"trivia",
|
|
5205
|
+
"label",
|
|
5206
|
+
"expect",
|
|
5207
|
+
"_tf",
|
|
5208
|
+
"_nd",
|
|
5209
|
+
`return (${ir})`
|
|
5210
|
+
);
|
|
5211
|
+
const map = fn(
|
|
5212
|
+
rules,
|
|
5213
|
+
ref,
|
|
5214
|
+
regex,
|
|
5215
|
+
literal,
|
|
5216
|
+
keywords,
|
|
5217
|
+
sequence,
|
|
5218
|
+
choice,
|
|
5219
|
+
many,
|
|
5220
|
+
oneOrMore,
|
|
5221
|
+
optional,
|
|
5222
|
+
sepBy,
|
|
5223
|
+
not,
|
|
5224
|
+
node,
|
|
5225
|
+
parser,
|
|
5226
|
+
scanTo,
|
|
5227
|
+
transform,
|
|
5228
|
+
skip,
|
|
5229
|
+
trivia,
|
|
5230
|
+
label,
|
|
5231
|
+
expect,
|
|
5232
|
+
_tf,
|
|
5233
|
+
_nd
|
|
5234
|
+
);
|
|
5235
|
+
return Object.entries(map);
|
|
5236
|
+
}
|
|
5237
|
+
var Serializer = class {
|
|
5238
|
+
counts = /* @__PURE__ */ new Map();
|
|
5239
|
+
selfRef = /* @__PURE__ */ new Set();
|
|
5240
|
+
// combinators reached by an unnamed lazy pointing back into their own subtree
|
|
5241
|
+
constName = /* @__PURE__ */ new Map();
|
|
5242
|
+
emitted = /* @__PURE__ */ new Set();
|
|
5243
|
+
decls = [];
|
|
5244
|
+
ruleValues = /* @__PURE__ */ new Set();
|
|
5245
|
+
ruleMap;
|
|
5246
|
+
constructor(ruleMap) {
|
|
5247
|
+
this.ruleMap = ruleMap;
|
|
5248
|
+
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
5249
|
+
}
|
|
5250
|
+
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
5251
|
+
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
5252
|
+
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
5253
|
+
body(c) {
|
|
5254
|
+
return c._def.tag === "lazy" && ruleNameOf(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
5255
|
+
}
|
|
5256
|
+
run() {
|
|
5257
|
+
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
5258
|
+
let n = 0;
|
|
5259
|
+
for (const [c, count] of this.counts) {
|
|
5260
|
+
if (this.ruleValues.has(c)) continue;
|
|
5261
|
+
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
5262
|
+
}
|
|
5263
|
+
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
5264
|
+
if (this.decls.length === 0) return `rules((g) => ({
|
|
5265
|
+
${body}
|
|
5266
|
+
}))`;
|
|
5267
|
+
return `rules((g) => {
|
|
5268
|
+
${this.decls.map((d) => " " + d).join("\n")}
|
|
5269
|
+
return ({
|
|
5270
|
+
${body}
|
|
5271
|
+
})
|
|
5272
|
+
})`;
|
|
5273
|
+
}
|
|
5274
|
+
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
5275
|
+
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
5276
|
+
analyze(c, active) {
|
|
5277
|
+
const def = c._def;
|
|
5278
|
+
if (def.tag === "lazy") {
|
|
5279
|
+
if (ruleNameOf(c) !== void 0) return;
|
|
5280
|
+
const target = lazyTarget(c);
|
|
5281
|
+
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
5282
|
+
if (active.has(target)) {
|
|
5283
|
+
this.selfRef.add(target);
|
|
5284
|
+
return;
|
|
5285
|
+
}
|
|
5286
|
+
this.analyze(target, active);
|
|
5287
|
+
return;
|
|
5288
|
+
}
|
|
5289
|
+
if (def.tag === "guard" || def.tag === "withCtx" || def.tag === "recover" || def.tag === "unknown") {
|
|
5290
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5291
|
+
}
|
|
5292
|
+
this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
|
|
5293
|
+
if ((this.counts.get(c) ?? 0) > 1) return;
|
|
5294
|
+
active.add(c);
|
|
5295
|
+
for (const child of childrenOf2(def)) this.analyze(child, active);
|
|
5296
|
+
active.delete(c);
|
|
5297
|
+
}
|
|
5298
|
+
/** Reference `c`: a const var if it has one (emitting its decl first), else inline.
|
|
5299
|
+
* `selfOf` is the const currently being wrapped in a ref()/define closure — an
|
|
5300
|
+
* unnamed lazy back into it emits the local ref var instead of recursing. */
|
|
5301
|
+
ref(c, selfOf) {
|
|
5302
|
+
const cn = this.constName.get(c);
|
|
5303
|
+
if (cn) {
|
|
5304
|
+
this.emitDecl(c);
|
|
5305
|
+
return cn;
|
|
5306
|
+
}
|
|
5307
|
+
return this.expr(c, selfOf);
|
|
5308
|
+
}
|
|
5309
|
+
emitDecl(c) {
|
|
5310
|
+
const name = this.constName.get(c);
|
|
5311
|
+
if (this.emitted.has(name)) return;
|
|
5312
|
+
this.emitted.add(name);
|
|
5313
|
+
this.decls.push(`const ${name} = ${this.wrap(c)}`);
|
|
5314
|
+
}
|
|
5315
|
+
selfN = 0;
|
|
5316
|
+
/** Emit `c`'s own constructor call, wrapping it in a `ref()`+`.define()` closure
|
|
5317
|
+
* if it is self-referential (an unnamed lazy in its subtree points back to it) —
|
|
5318
|
+
* the shape `balanced()` and other recursive library combinators build. */
|
|
5319
|
+
wrap(c) {
|
|
5320
|
+
if (!this.selfRef.has(c)) return this.expr(c, null);
|
|
5321
|
+
const rv = `_rr${this.selfN++}`;
|
|
5322
|
+
const bodyExpr = this.expr(c, { comb: c, var: rv });
|
|
5323
|
+
return `(() => { const ${rv} = ref(); const _b = ${bodyExpr}; ${rv}.define(_b); return _b })()`;
|
|
5324
|
+
}
|
|
5325
|
+
/** Emit the constructor-call source for `c` (never a const shortcut for `c` itself). */
|
|
5326
|
+
expr(c, selfOf) {
|
|
5327
|
+
const def = c._def;
|
|
5328
|
+
const kid = (x) => this.ref(x, selfOf);
|
|
5329
|
+
switch (def.tag) {
|
|
5330
|
+
case "lazy": {
|
|
5331
|
+
if (ruleNameOf(c) !== void 0) return `g[${JSON.stringify(ruleNameOf(c))}]`;
|
|
5332
|
+
const target = lazyTarget(c);
|
|
5333
|
+
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
5334
|
+
return this.ref(target, selfOf);
|
|
5335
|
+
}
|
|
5336
|
+
case "literal":
|
|
5337
|
+
return `literal(${JSON.stringify(def.value)}${def.caseInsensitive ? ", { caseInsensitive: true }" : ""})`;
|
|
5338
|
+
case "regex":
|
|
5339
|
+
return `regex(${JSON.stringify(def.source)}, ${JSON.stringify(def.flags)})`;
|
|
5340
|
+
case "keywords":
|
|
5341
|
+
return `keywords(${JSON.stringify(def.words)}, { caseInsensitive: ${def.caseInsensitive}${def.boundary !== void 0 ? `, boundary: ${JSON.stringify(def.boundary)}` : ""} })`;
|
|
5342
|
+
case "sequence":
|
|
5343
|
+
return `sequence(${def.parsers.map(kid).join(", ")})`;
|
|
5344
|
+
case "choice": {
|
|
5345
|
+
if (def.gates.some((g) => g !== null)) throw new Unserializable("choice with a gate() has no source");
|
|
5346
|
+
return `choice(${def.parsers.map(kid).join(", ")})`;
|
|
5347
|
+
}
|
|
5348
|
+
case "many":
|
|
5349
|
+
return `many(${kid(def.parser)})`;
|
|
5350
|
+
case "oneOrMore":
|
|
5351
|
+
return `oneOrMore(${kid(def.parser)})`;
|
|
5352
|
+
case "optional":
|
|
5353
|
+
return `optional(${kid(def.parser)})`;
|
|
5354
|
+
case "sepBy":
|
|
5355
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
5356
|
+
case "not":
|
|
5357
|
+
return `not(${kid(def.parser)})`;
|
|
5358
|
+
case "trivia":
|
|
5359
|
+
return `trivia(${kid(def.parser)})`;
|
|
5360
|
+
case "label":
|
|
5361
|
+
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
5362
|
+
case "expect":
|
|
5363
|
+
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
5364
|
+
case "skip":
|
|
5365
|
+
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
5366
|
+
case "scanTo":
|
|
5367
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
5368
|
+
case "transform": {
|
|
5369
|
+
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
5370
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
5371
|
+
}
|
|
5372
|
+
case "node": {
|
|
5373
|
+
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
5374
|
+
const opts = def.collapse ? `, { collapse: true }` : "";
|
|
5375
|
+
if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
|
|
5376
|
+
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
5377
|
+
}
|
|
5378
|
+
case "grammar": {
|
|
5379
|
+
const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
|
|
5380
|
+
return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
|
|
5381
|
+
}
|
|
5382
|
+
case "guard":
|
|
5383
|
+
case "withCtx":
|
|
5384
|
+
case "recover":
|
|
5385
|
+
case "unknown":
|
|
5386
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
};
|
|
5390
|
+
|
|
5391
|
+
// src/compiler/linker.ts
|
|
5392
|
+
function fusedBody(pieces) {
|
|
5393
|
+
const winner = /* @__PURE__ */ new Map();
|
|
5394
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
5395
|
+
for (const [k, p] of winner) {
|
|
5396
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
5397
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
const contributing = new Set(winner.values());
|
|
5401
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
5402
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
5403
|
+
const lines = [
|
|
5404
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
5405
|
+
"const _pfFail = {}",
|
|
5406
|
+
"let _pfEnd",
|
|
5407
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
5408
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
5409
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
5410
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
5411
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
5412
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
5413
|
+
];
|
|
5414
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
5415
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
5416
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
5417
|
+
for (const [k, p] of winner) {
|
|
5418
|
+
const fs2 = p.firstSets?.get(k);
|
|
5419
|
+
if (fs2) finalFS.set(k, fs2);
|
|
5420
|
+
}
|
|
5421
|
+
const body = rawBody.replace(
|
|
5422
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
5423
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
5424
|
+
// matches every well-formed placeholder.
|
|
5425
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
5426
|
+
(_m, name, codevar) => {
|
|
5427
|
+
const fs2 = finalFS.get(name);
|
|
5428
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
5429
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
5430
|
+
}
|
|
5431
|
+
);
|
|
5432
|
+
const env = {};
|
|
5433
|
+
for (const p of contributing) {
|
|
5434
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
5435
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
5436
|
+
}
|
|
5437
|
+
return { body, env };
|
|
5438
|
+
}
|
|
5439
|
+
function emitFusedSource(pieces) {
|
|
5440
|
+
const { body, env } = fusedBody(pieces);
|
|
5441
|
+
if (Object.keys(env).length > 0) {
|
|
5442
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
5443
|
+
}
|
|
5444
|
+
return `/* @__PURE__ */ (() => {
|
|
5445
|
+
${body}
|
|
5446
|
+
})()`;
|
|
5447
|
+
}
|
|
5448
|
+
function isIRPiece(p) {
|
|
5449
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5450
|
+
}
|
|
5451
|
+
function materializePiece(p) {
|
|
5452
|
+
if (!isIRPiece(p)) return p;
|
|
5453
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5454
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5455
|
+
return pieces;
|
|
5456
|
+
}
|
|
5457
|
+
|
|
4877
5458
|
// src/cst/offset-model.ts
|
|
4878
5459
|
var { isFinite } = Number;
|
|
4879
5460
|
|
|
@@ -5385,7 +5966,7 @@ var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
|
|
|
5385
5966
|
if (!code.includes("parseman")) return null;
|
|
5386
5967
|
if (!code.includes("macro")) return null;
|
|
5387
5968
|
const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
|
|
5388
|
-
const result = transformMacro(code, id, moduleAliases);
|
|
5969
|
+
const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true);
|
|
5389
5970
|
if (result?.warnings.length) {
|
|
5390
5971
|
for (const w of result.warnings) {
|
|
5391
5972
|
if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
|
|
@@ -5447,6 +6028,14 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5447
6028
|
const obj = callee?.object?.name;
|
|
5448
6029
|
const prop = callee?.property?.name;
|
|
5449
6030
|
const args = node2.arguments;
|
|
6031
|
+
if (obj === "Object" && prop === "assign" && args && args[1]?.type === "ObjectExpression") {
|
|
6032
|
+
for (const p of args[1].properties ?? []) {
|
|
6033
|
+
if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
|
|
6034
|
+
found = { start: p.value.start, end: p.value.end };
|
|
6035
|
+
return;
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
5450
6039
|
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5451
6040
|
const descriptor = args[2];
|
|
5452
6041
|
for (const p of descriptor?.properties ?? []) {
|
|
@@ -5467,7 +6056,21 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5467
6056
|
visit(root);
|
|
5468
6057
|
return found;
|
|
5469
6058
|
}
|
|
5470
|
-
function
|
|
6059
|
+
function extractImportBindings(body) {
|
|
6060
|
+
const out = /* @__PURE__ */ new Map();
|
|
6061
|
+
for (const stmt of body) {
|
|
6062
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
6063
|
+
const s = stmt;
|
|
6064
|
+
for (const spec of s.specifiers) {
|
|
6065
|
+
if (spec.type === "ImportSpecifier") {
|
|
6066
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
6067
|
+
out.set(spec.local.name, { source: s.source.value, imported });
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
return out;
|
|
6072
|
+
}
|
|
6073
|
+
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5471
6074
|
let result;
|
|
5472
6075
|
try {
|
|
5473
6076
|
result = (0, import_oxc_parser.parseSync)(id, code);
|
|
@@ -5506,6 +6109,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5506
6109
|
const scope = /* @__PURE__ */ new Map();
|
|
5507
6110
|
const replacements = [];
|
|
5508
6111
|
const warnings = [];
|
|
6112
|
+
beginLoweringCapture();
|
|
5509
6113
|
let anyUnresolved = false;
|
|
5510
6114
|
let ruleMapHolderCounter = 0;
|
|
5511
6115
|
const lineOf = (pos) => {
|
|
@@ -5540,14 +6144,57 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5540
6144
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5541
6145
|
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5542
6146
|
const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
6147
|
+
const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
|
|
5543
6148
|
const serializePieces = (p) => {
|
|
5544
|
-
const mapLit = (m) => `new Map([${[...m].map(
|
|
5545
|
-
|
|
6149
|
+
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
6150
|
+
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
6151
|
+
).join(", ")}])`;
|
|
6152
|
+
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: [] }`;
|
|
5546
6153
|
};
|
|
5547
|
-
const
|
|
5548
|
-
const
|
|
6154
|
+
const isSpread = (it) => "__spreadLocal" in it;
|
|
6155
|
+
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
6156
|
+
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);
|
|
6157
|
+
const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
|
|
6158
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
|
|
5549
6159
|
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5550
6160
|
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
6161
|
+
const resolveModulePieces = (file, exportName, seen) => {
|
|
6162
|
+
const mod = parseModuleCached(file);
|
|
6163
|
+
if (!mod) return null;
|
|
6164
|
+
const localFor = exportLocalName(mod.body, exportName);
|
|
6165
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
6166
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
6167
|
+
if (!literalRange) return null;
|
|
6168
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
6169
|
+
const imports = extractImportBindings(mod.body);
|
|
6170
|
+
const stubNames = [];
|
|
6171
|
+
const stubVals = [];
|
|
6172
|
+
const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
|
|
6173
|
+
const done = /* @__PURE__ */ new Set();
|
|
6174
|
+
for (let m; m = spreadRe.exec(literal2); ) {
|
|
6175
|
+
const local = m[1];
|
|
6176
|
+
if (done.has(local)) continue;
|
|
6177
|
+
done.add(local);
|
|
6178
|
+
const b = imports.get(local);
|
|
6179
|
+
let subPieces = [];
|
|
6180
|
+
if (b) {
|
|
6181
|
+
let subFile = null;
|
|
6182
|
+
try {
|
|
6183
|
+
subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
|
|
6184
|
+
} catch {
|
|
6185
|
+
}
|
|
6186
|
+
if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
|
|
6187
|
+
}
|
|
6188
|
+
stubNames.push(local);
|
|
6189
|
+
stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
|
|
6190
|
+
}
|
|
6191
|
+
try {
|
|
6192
|
+
const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
|
|
6193
|
+
return list.map(materializePiece);
|
|
6194
|
+
} catch {
|
|
6195
|
+
return null;
|
|
6196
|
+
}
|
|
6197
|
+
};
|
|
5551
6198
|
const importedPieces = (localName) => {
|
|
5552
6199
|
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5553
6200
|
let result2 = null;
|
|
@@ -5558,41 +6205,34 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5558
6205
|
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5559
6206
|
} catch {
|
|
5560
6207
|
}
|
|
5561
|
-
|
|
5562
|
-
if (mod) {
|
|
5563
|
-
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5564
|
-
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5565
|
-
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5566
|
-
if (literalRange) {
|
|
5567
|
-
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5568
|
-
try {
|
|
5569
|
-
result2 = new Function(`return (${literal2})`)();
|
|
5570
|
-
} catch {
|
|
5571
|
-
result2 = null;
|
|
5572
|
-
}
|
|
5573
|
-
}
|
|
5574
|
-
}
|
|
6208
|
+
result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
|
|
5575
6209
|
}
|
|
5576
6210
|
importedPiecesCache.set(localName, result2);
|
|
5577
6211
|
return result2;
|
|
5578
6212
|
};
|
|
6213
|
+
const localArg = (rm, label2) => {
|
|
6214
|
+
const entries = [...rm];
|
|
6215
|
+
const ns = nsFor(label2);
|
|
6216
|
+
const p = compileLinkable(entries, ns);
|
|
6217
|
+
if (!p) return null;
|
|
6218
|
+
const ir = serializeRuleMap(entries);
|
|
6219
|
+
return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
|
|
6220
|
+
};
|
|
5579
6221
|
const argPieces = (arg, label2) => {
|
|
5580
6222
|
if (isRulesCall(arg)) {
|
|
5581
6223
|
const factory = arg.arguments[0];
|
|
5582
6224
|
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5583
|
-
|
|
5584
|
-
return p ? [p] : null;
|
|
6225
|
+
return rm ? localArg(rm, label2) : null;
|
|
5585
6226
|
}
|
|
5586
6227
|
if (arg.type === "Identifier") {
|
|
5587
6228
|
const name = arg.name;
|
|
5588
6229
|
const rm = localRuleMaps.get(name);
|
|
5589
|
-
if (rm)
|
|
5590
|
-
const p = compileLinkable([...rm], nsFor(label2));
|
|
5591
|
-
return p ? [p] : null;
|
|
5592
|
-
}
|
|
6230
|
+
if (rm) return localArg(rm, label2);
|
|
5593
6231
|
const composed = localComposedPieces.get(name);
|
|
5594
|
-
if (composed) return composed;
|
|
5595
|
-
|
|
6232
|
+
if (composed) return { pieces: composed };
|
|
6233
|
+
const pieces = importedPieces(name);
|
|
6234
|
+
if (!pieces) return null;
|
|
6235
|
+
return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
|
|
5596
6236
|
}
|
|
5597
6237
|
return null;
|
|
5598
6238
|
};
|
|
@@ -5605,16 +6245,20 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5605
6245
|
}
|
|
5606
6246
|
const elements = arr.elements;
|
|
5607
6247
|
const pieces = [];
|
|
6248
|
+
const carried = [];
|
|
5608
6249
|
for (let i = 0; i < elements.length; i++) {
|
|
5609
|
-
const
|
|
5610
|
-
if (!
|
|
6250
|
+
const r = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
6251
|
+
if (!r) {
|
|
5611
6252
|
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5612
6253
|
return null;
|
|
5613
6254
|
}
|
|
5614
|
-
pieces.push(...
|
|
6255
|
+
pieces.push(...r.pieces);
|
|
6256
|
+
if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
|
|
6257
|
+
else if (r.ir) carried.push(r.ir);
|
|
6258
|
+
else carried.push(...r.pieces);
|
|
5615
6259
|
}
|
|
5616
6260
|
try {
|
|
5617
|
-
return { replacement: emitFusedSource(pieces), pieces };
|
|
6261
|
+
return { replacement: emitFusedSource(pieces), pieces, carried };
|
|
5618
6262
|
} catch (e) {
|
|
5619
6263
|
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5620
6264
|
return null;
|
|
@@ -5701,9 +6345,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5701
6345
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5702
6346
|
let replacement = compiledRules.replacement;
|
|
5703
6347
|
if (exportPrefix) {
|
|
5704
|
-
const
|
|
6348
|
+
const ns = nsFor(varName);
|
|
6349
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], ns);
|
|
5705
6350
|
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5706
|
-
|
|
6351
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
6352
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
5707
6353
|
}
|
|
5708
6354
|
}
|
|
5709
6355
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
@@ -5713,7 +6359,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5713
6359
|
const fused = compileComposeCall(init);
|
|
5714
6360
|
if (!fused) continue;
|
|
5715
6361
|
localComposedPieces.set(varName, fused.pieces);
|
|
5716
|
-
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.
|
|
6362
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
5717
6363
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5718
6364
|
continue;
|
|
5719
6365
|
}
|
|
@@ -5813,6 +6459,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5813
6459
|
for (const { start, end, replacement } of [...replacements].sort((a, b) => b.start - a.start)) {
|
|
5814
6460
|
ms.overwrite(start, end, replacement);
|
|
5815
6461
|
}
|
|
6462
|
+
const unlowered = endLoweringCapture();
|
|
6463
|
+
if (warnUnloweredRegex) {
|
|
6464
|
+
for (const src of unlowered) {
|
|
6465
|
+
warnings.push(`${id}: regex ${src} did not lower to a fast charCodeAt scan (RegExp.exec fallback)`);
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
5816
6468
|
return {
|
|
5817
6469
|
code: ms.toString(),
|
|
5818
6470
|
map: ms.generateMap({ hires: true }),
|