parseman 0.16.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/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/index.cjs +329 -112
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +329 -112
- package/dist/index.js.map +4 -4
- package/dist/plugin/index.cjs +647 -108
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +647 -108
- package/dist/plugin/index.js.map +4 -4
- package/package.json +1 -1
package/dist/plugin/index.js
CHANGED
|
@@ -91,6 +91,54 @@ function sequenceFirstSet(parsers) {
|
|
|
91
91
|
}
|
|
92
92
|
return fs2;
|
|
93
93
|
}
|
|
94
|
+
function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
95
|
+
if (seen.has(p)) return any();
|
|
96
|
+
seen.add(p);
|
|
97
|
+
const fs2 = (c) => firstSetOf(c, seen);
|
|
98
|
+
const d = p._def;
|
|
99
|
+
switch (d.tag) {
|
|
100
|
+
case "literal":
|
|
101
|
+
case "regex":
|
|
102
|
+
case "keywords":
|
|
103
|
+
return p._meta.firstSet;
|
|
104
|
+
// terminals: no refs, cached set is exact
|
|
105
|
+
case "lazy":
|
|
106
|
+
try {
|
|
107
|
+
return fs2(d.thunk());
|
|
108
|
+
} catch {
|
|
109
|
+
return any();
|
|
110
|
+
}
|
|
111
|
+
case "choice": {
|
|
112
|
+
let out = empty();
|
|
113
|
+
for (const arm of d.parsers) out = union(out, fs2(arm));
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
case "sequence": {
|
|
117
|
+
let out = empty();
|
|
118
|
+
for (const term of d.parsers) {
|
|
119
|
+
out = union(out, fs2(term));
|
|
120
|
+
if (!matchesEmpty(term)) return out;
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
case "oneOrMore":
|
|
125
|
+
case "many":
|
|
126
|
+
case "optional":
|
|
127
|
+
case "transform":
|
|
128
|
+
case "label":
|
|
129
|
+
case "trivia":
|
|
130
|
+
case "node":
|
|
131
|
+
case "grammar":
|
|
132
|
+
case "expect":
|
|
133
|
+
return fs2(d.parser);
|
|
134
|
+
case "sepBy":
|
|
135
|
+
return fs2(d.parser);
|
|
136
|
+
case "skip":
|
|
137
|
+
return fs2(d.main);
|
|
138
|
+
default:
|
|
139
|
+
return p._meta.firstSet;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
94
142
|
function mergeRanges(ranges) {
|
|
95
143
|
if (ranges.length === 0) return [];
|
|
96
144
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -1230,6 +1278,41 @@ function markUnusedValues(root) {
|
|
|
1230
1278
|
visit(resolveRoot(root), true);
|
|
1231
1279
|
}
|
|
1232
1280
|
|
|
1281
|
+
// src/combinators/parser.ts
|
|
1282
|
+
function rules(factory) {
|
|
1283
|
+
const cache = {};
|
|
1284
|
+
const proxy = new Proxy(cache, {
|
|
1285
|
+
get(target, key) {
|
|
1286
|
+
if (typeof key !== "string") return void 0;
|
|
1287
|
+
const record = target;
|
|
1288
|
+
if (!(key in record)) {
|
|
1289
|
+
const r = ref();
|
|
1290
|
+
r._ruleName = key;
|
|
1291
|
+
record[key] = r;
|
|
1292
|
+
}
|
|
1293
|
+
return record[key];
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
const definitions = factory(proxy);
|
|
1297
|
+
for (const key of Object.keys(definitions)) {
|
|
1298
|
+
const placeholder = cache[key];
|
|
1299
|
+
const parser2 = definitions[key];
|
|
1300
|
+
if (placeholder !== void 0 && typeof placeholder.define === "function") {
|
|
1301
|
+
;
|
|
1302
|
+
placeholder.define(parser2);
|
|
1303
|
+
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
1304
|
+
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
1305
|
+
} else {
|
|
1306
|
+
;
|
|
1307
|
+
cache[key] = parser2;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
for (const key of Object.keys(definitions)) {
|
|
1311
|
+
markUnusedValues(cache[key]);
|
|
1312
|
+
}
|
|
1313
|
+
return cache;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1233
1316
|
// src/combinators/not.ts
|
|
1234
1317
|
function not(combinator) {
|
|
1235
1318
|
const meta = {
|
|
@@ -1336,6 +1419,20 @@ function transform(combinator, fn) {
|
|
|
1336
1419
|
}
|
|
1337
1420
|
};
|
|
1338
1421
|
}
|
|
1422
|
+
function skip(main, skipped) {
|
|
1423
|
+
return {
|
|
1424
|
+
_tag: "skip",
|
|
1425
|
+
_meta: main._meta,
|
|
1426
|
+
_def: { tag: "skip", main, skipped },
|
|
1427
|
+
parse(input, pos, ctx) {
|
|
1428
|
+
const result = main.parse(input, pos, ctx);
|
|
1429
|
+
if (!result.ok) return result;
|
|
1430
|
+
const s = skipped.parse(input, result.span.end, ctx);
|
|
1431
|
+
if (!s.ok) return result;
|
|
1432
|
+
return { ...result, span: { start: result.span.start, end: s.span.end } };
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1339
1436
|
function trivia(combinator) {
|
|
1340
1437
|
const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
|
|
1341
1438
|
return {
|
|
@@ -2964,16 +3061,12 @@ function namedFnPrelude() {
|
|
|
2964
3061
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
2965
3062
|
ctx.namedFnDecls.push([
|
|
2966
3063
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
2967
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
2968
3064
|
` _pfail: {`,
|
|
2969
3065
|
...reindentStmts(bodyStmts, 2),
|
|
2970
|
-
`
|
|
2971
|
-
`
|
|
2972
|
-
` _pfok = true`,
|
|
3066
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3067
|
+
` return ${valueVar}`,
|
|
2973
3068
|
` }`,
|
|
2974
|
-
`
|
|
2975
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
2976
|
-
` return _pfv`,
|
|
3069
|
+
` return ${NAMED_FN_FAIL}`,
|
|
2977
3070
|
`}`
|
|
2978
3071
|
].join("\n"));
|
|
2979
3072
|
}
|
|
@@ -3674,11 +3767,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3674
3767
|
const atStart = failsAtStart(p);
|
|
3675
3768
|
const staticFx = armStaticExpected(ctx, p);
|
|
3676
3769
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3677
|
-
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) :
|
|
3770
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
3771
|
+
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
3772
|
+
const deep = firstSetOf(p);
|
|
3773
|
+
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
3774
|
+
})();
|
|
3678
3775
|
let gateCond = null;
|
|
3679
3776
|
if (gate) {
|
|
3680
3777
|
const gateIdx = ctx.mapFns.length;
|
|
3681
3778
|
ctx.mapFns.push(gate);
|
|
3779
|
+
ctx.mapFnSrcs.push(null);
|
|
3682
3780
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3683
3781
|
}
|
|
3684
3782
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3737,13 +3835,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3737
3835
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3738
3836
|
return emitFirstMatch(def, ctx, pos);
|
|
3739
3837
|
}
|
|
3838
|
+
function pushMapFn(ctx, fn, src) {
|
|
3839
|
+
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
3840
|
+
const hit = ctx.mapFnSrcs.indexOf(src);
|
|
3841
|
+
if (hit !== -1) return hit;
|
|
3842
|
+
}
|
|
3843
|
+
const idx = ctx.mapFns.length;
|
|
3844
|
+
ctx.mapFns.push(fn);
|
|
3845
|
+
ctx.mapFnSrcs.push(src);
|
|
3846
|
+
return idx;
|
|
3847
|
+
}
|
|
3740
3848
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3741
3849
|
const def = p._def;
|
|
3742
3850
|
if (def.tag === "transform") {
|
|
3743
3851
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3744
|
-
const fnIdx = ctx.
|
|
3745
|
-
ctx.mapFns.push(def.fn);
|
|
3746
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3852
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3747
3853
|
const vv = v(ctx);
|
|
3748
3854
|
return {
|
|
3749
3855
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3926,7 +4032,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3926
4032
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3927
4033
|
];
|
|
3928
4034
|
ctx.indent++;
|
|
3929
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4035
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3930
4036
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3931
4037
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3932
4038
|
if (def.skip.length > 0) {
|
|
@@ -4117,6 +4223,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4117
4223
|
return { stmts, valueVar: valVar, endVar };
|
|
4118
4224
|
}
|
|
4119
4225
|
function emit(p, ctx, pos) {
|
|
4226
|
+
const usage = ctx.lazyUsage;
|
|
4227
|
+
if (usage && !ctx.noHoist && !ctx.capAsTrivia && !ctx.ruleNames?.has(p) && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && // Only hoist subtrees big enough that de-duplicating them beats the call
|
|
4228
|
+
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
4229
|
+
// faster than it calls and barely moves the byte count; the size explosion
|
|
4230
|
+
// is all in LARGE choices/sequences referenced from many positions.
|
|
4231
|
+
(usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
|
|
4232
|
+
const existing = ctx.namedParsers.get(p);
|
|
4233
|
+
if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
|
|
4234
|
+
const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
4235
|
+
ctx.namedParsers.set(p, fnName);
|
|
4236
|
+
const savedIndent = ctx.indent;
|
|
4237
|
+
const savedFailLabel = ctx.failLabel;
|
|
4238
|
+
const savedRecord = ctx.recordFail;
|
|
4239
|
+
ctx.indent = 1;
|
|
4240
|
+
ctx.failLabel = "_pfail";
|
|
4241
|
+
ctx.recordFail = true;
|
|
4242
|
+
const r = emitDispatch(p, ctx, "_pos");
|
|
4243
|
+
ctx.indent = savedIndent;
|
|
4244
|
+
ctx.failLabel = savedFailLabel;
|
|
4245
|
+
ctx.recordFail = savedRecord;
|
|
4246
|
+
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
4247
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
4248
|
+
}
|
|
4249
|
+
return emitDispatch(p, ctx, pos);
|
|
4250
|
+
}
|
|
4251
|
+
function emitDispatch(p, ctx, pos) {
|
|
4120
4252
|
const def = p._def;
|
|
4121
4253
|
switch (def.tag) {
|
|
4122
4254
|
case "literal":
|
|
@@ -4162,9 +4294,7 @@ function emit(p, ctx, pos) {
|
|
|
4162
4294
|
};
|
|
4163
4295
|
}
|
|
4164
4296
|
}
|
|
4165
|
-
const fnIdx = ctx.
|
|
4166
|
-
ctx.mapFns.push(def.fn);
|
|
4167
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4297
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4168
4298
|
const mv = v(ctx, "_mapped");
|
|
4169
4299
|
return {
|
|
4170
4300
|
stmts: [
|
|
@@ -4229,6 +4359,7 @@ function emit(p, ctx, pos) {
|
|
|
4229
4359
|
case "guard": {
|
|
4230
4360
|
const fnIdx = ctx.mapFns.length;
|
|
4231
4361
|
ctx.mapFns.push(def.predicate);
|
|
4362
|
+
ctx.mapFnSrcs.push(null);
|
|
4232
4363
|
const vv = v(ctx);
|
|
4233
4364
|
return {
|
|
4234
4365
|
stmts: [
|
|
@@ -4243,6 +4374,7 @@ function emit(p, ctx, pos) {
|
|
|
4243
4374
|
const evIdx = ctx.mapFns.length;
|
|
4244
4375
|
const extra = def.extra;
|
|
4245
4376
|
ctx.mapFns.push((() => extra));
|
|
4377
|
+
ctx.mapFnSrcs.push(null);
|
|
4246
4378
|
const innerParser = def.parser;
|
|
4247
4379
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4248
4380
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4362,9 +4494,58 @@ function childrenOf(def) {
|
|
|
4362
4494
|
return [];
|
|
4363
4495
|
}
|
|
4364
4496
|
}
|
|
4497
|
+
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
4498
|
+
"sequence",
|
|
4499
|
+
"choice",
|
|
4500
|
+
"many",
|
|
4501
|
+
"oneOrMore",
|
|
4502
|
+
"optional",
|
|
4503
|
+
"sepBy",
|
|
4504
|
+
"transform",
|
|
4505
|
+
"node",
|
|
4506
|
+
"not"
|
|
4507
|
+
]);
|
|
4508
|
+
function isHoistableTag(tag) {
|
|
4509
|
+
return HOISTABLE_TAGS.has(tag);
|
|
4510
|
+
}
|
|
4511
|
+
var HOIST_MIN_SUBTREE = 3;
|
|
4365
4512
|
function analyzeLazyUsage(root) {
|
|
4366
4513
|
return analyzeLazyUsageMulti([root]);
|
|
4367
4514
|
}
|
|
4515
|
+
function subtreeSizes(roots) {
|
|
4516
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
4517
|
+
const sz = (p) => {
|
|
4518
|
+
const cached = sizes.get(p);
|
|
4519
|
+
if (cached !== void 0) return cached;
|
|
4520
|
+
const def = p._def;
|
|
4521
|
+
let s = 1;
|
|
4522
|
+
if (def.tag !== "lazy") {
|
|
4523
|
+
for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
|
|
4524
|
+
}
|
|
4525
|
+
sizes.set(p, s);
|
|
4526
|
+
return s;
|
|
4527
|
+
};
|
|
4528
|
+
const visitedLazy = /* @__PURE__ */ new Set();
|
|
4529
|
+
const visit = (p) => {
|
|
4530
|
+
const def = p._def;
|
|
4531
|
+
if (def.tag === "lazy") {
|
|
4532
|
+
if (visitedLazy.has(p)) return;
|
|
4533
|
+
visitedLazy.add(p);
|
|
4534
|
+
let resolved;
|
|
4535
|
+
try {
|
|
4536
|
+
resolved = def.thunk();
|
|
4537
|
+
} catch {
|
|
4538
|
+
return;
|
|
4539
|
+
}
|
|
4540
|
+
visit(resolved);
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
sz(p);
|
|
4544
|
+
for (const child of childrenOf(def)) visit(child);
|
|
4545
|
+
};
|
|
4546
|
+
for (const root of roots) visit(root);
|
|
4547
|
+
return sizes;
|
|
4548
|
+
}
|
|
4368
4549
|
function analyzeLazyUsageMulti(roots) {
|
|
4369
4550
|
const counts = /* @__PURE__ */ new Map();
|
|
4370
4551
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4391,10 +4572,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4391
4572
|
active.delete(p);
|
|
4392
4573
|
return;
|
|
4393
4574
|
}
|
|
4575
|
+
if (isHoistableTag(def.tag)) {
|
|
4576
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4577
|
+
}
|
|
4394
4578
|
for (const child of childrenOf(def)) walk2(child);
|
|
4395
4579
|
}
|
|
4396
|
-
|
|
4397
|
-
|
|
4580
|
+
const rootsArr = [...roots];
|
|
4581
|
+
for (const root of rootsArr) walk2(root);
|
|
4582
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4398
4583
|
}
|
|
4399
4584
|
function ruleDependencies(ruleMap) {
|
|
4400
4585
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4793,64 +4978,6 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
|
4793
4978
|
].join("\n");
|
|
4794
4979
|
}
|
|
4795
4980
|
|
|
4796
|
-
// src/compiler/linker.ts
|
|
4797
|
-
function fusedBody(pieces) {
|
|
4798
|
-
const winner = /* @__PURE__ */ new Map();
|
|
4799
|
-
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4800
|
-
for (const [k, p] of winner) {
|
|
4801
|
-
for (const d of p.deps.get(k) ?? []) {
|
|
4802
|
-
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4803
|
-
}
|
|
4804
|
-
}
|
|
4805
|
-
const contributing = new Set(winner.values());
|
|
4806
|
-
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4807
|
-
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4808
|
-
const lines = [
|
|
4809
|
-
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4810
|
-
"const _pfFail = {}",
|
|
4811
|
-
"let _pfEnd",
|
|
4812
|
-
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4813
|
-
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4814
|
-
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4815
|
-
...[...contributing].flatMap((p) => p.prelude),
|
|
4816
|
-
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4817
|
-
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4818
|
-
];
|
|
4819
|
-
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4820
|
-
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4821
|
-
const finalFS = /* @__PURE__ */ new Map();
|
|
4822
|
-
for (const [k, p] of winner) {
|
|
4823
|
-
const fs2 = p.firstSets?.get(k);
|
|
4824
|
-
if (fs2) finalFS.set(k, fs2);
|
|
4825
|
-
}
|
|
4826
|
-
const body = rawBody.replace(
|
|
4827
|
-
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4828
|
-
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4829
|
-
// matches every well-formed placeholder.
|
|
4830
|
-
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4831
|
-
(_m, name, codevar) => {
|
|
4832
|
-
const fs2 = finalFS.get(name);
|
|
4833
|
-
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4834
|
-
return `(${firstSetCond(codevar, fs2)})`;
|
|
4835
|
-
}
|
|
4836
|
-
);
|
|
4837
|
-
const env = {};
|
|
4838
|
-
for (const p of contributing) {
|
|
4839
|
-
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4840
|
-
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4841
|
-
}
|
|
4842
|
-
return { body, env };
|
|
4843
|
-
}
|
|
4844
|
-
function emitFusedSource(pieces) {
|
|
4845
|
-
const { body, env } = fusedBody(pieces);
|
|
4846
|
-
if (Object.keys(env).length > 0) {
|
|
4847
|
-
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4848
|
-
}
|
|
4849
|
-
return `/* @__PURE__ */ (() => {
|
|
4850
|
-
${body}
|
|
4851
|
-
})()`;
|
|
4852
|
-
}
|
|
4853
|
-
|
|
4854
4981
|
// src/combinators/scanTo.ts
|
|
4855
4982
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4856
4983
|
const meta = {
|
|
@@ -4945,6 +5072,354 @@ function escapeClassChar(ch) {
|
|
|
4945
5072
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4946
5073
|
}
|
|
4947
5074
|
|
|
5075
|
+
// src/compiler/ir-serialize.ts
|
|
5076
|
+
function childrenOf2(def) {
|
|
5077
|
+
switch (def.tag) {
|
|
5078
|
+
case "sequence":
|
|
5079
|
+
case "choice":
|
|
5080
|
+
return def.parsers;
|
|
5081
|
+
case "many":
|
|
5082
|
+
case "oneOrMore":
|
|
5083
|
+
case "optional":
|
|
5084
|
+
case "transform":
|
|
5085
|
+
case "trivia":
|
|
5086
|
+
case "label":
|
|
5087
|
+
case "not":
|
|
5088
|
+
case "node":
|
|
5089
|
+
case "expect":
|
|
5090
|
+
return [def.parser];
|
|
5091
|
+
case "grammar":
|
|
5092
|
+
return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
|
|
5093
|
+
case "sepBy":
|
|
5094
|
+
return [def.parser, def.separator];
|
|
5095
|
+
case "skip":
|
|
5096
|
+
return [def.main, def.skipped];
|
|
5097
|
+
case "scanTo":
|
|
5098
|
+
return [def.sentinel, ...def.skip];
|
|
5099
|
+
case "lazy":
|
|
5100
|
+
case "literal":
|
|
5101
|
+
case "regex":
|
|
5102
|
+
case "keywords":
|
|
5103
|
+
case "guard":
|
|
5104
|
+
case "withCtx":
|
|
5105
|
+
case "recover":
|
|
5106
|
+
case "unknown":
|
|
5107
|
+
return [];
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
var ruleNameOf = (c) => c._ruleName;
|
|
5111
|
+
function lazyTarget(c) {
|
|
5112
|
+
if (c._def.tag !== "lazy") return null;
|
|
5113
|
+
try {
|
|
5114
|
+
return c._def.thunk();
|
|
5115
|
+
} catch {
|
|
5116
|
+
return null;
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
var Unserializable = class extends Error {
|
|
5120
|
+
};
|
|
5121
|
+
function serializeRuleMap(ruleMap) {
|
|
5122
|
+
try {
|
|
5123
|
+
return new Serializer(ruleMap).run();
|
|
5124
|
+
} catch (e) {
|
|
5125
|
+
if (e instanceof Unserializable) {
|
|
5126
|
+
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
5127
|
+
return null;
|
|
5128
|
+
}
|
|
5129
|
+
throw e;
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
function evalRuleMapIR(ir) {
|
|
5133
|
+
const _tf = (child, src) => {
|
|
5134
|
+
let fn2;
|
|
5135
|
+
try {
|
|
5136
|
+
fn2 = (0, eval)(`(${src})`);
|
|
5137
|
+
} catch {
|
|
5138
|
+
fn2 = () => {
|
|
5139
|
+
throw new Error("IR transform fn not materialized");
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
5142
|
+
const t = transform(child, fn2);
|
|
5143
|
+
t._def.fnSrc = src;
|
|
5144
|
+
return t;
|
|
5145
|
+
};
|
|
5146
|
+
const _nd = (type, child, src, opts) => {
|
|
5147
|
+
const n = node(type, child, void 0, opts);
|
|
5148
|
+
n._def.buildSrc = src;
|
|
5149
|
+
return n;
|
|
5150
|
+
};
|
|
5151
|
+
const fn = new Function(
|
|
5152
|
+
"rules",
|
|
5153
|
+
"ref",
|
|
5154
|
+
"regex",
|
|
5155
|
+
"literal",
|
|
5156
|
+
"keywords",
|
|
5157
|
+
"sequence",
|
|
5158
|
+
"choice",
|
|
5159
|
+
"many",
|
|
5160
|
+
"oneOrMore",
|
|
5161
|
+
"optional",
|
|
5162
|
+
"sepBy",
|
|
5163
|
+
"not",
|
|
5164
|
+
"node",
|
|
5165
|
+
"parser",
|
|
5166
|
+
"scanTo",
|
|
5167
|
+
"transform",
|
|
5168
|
+
"skip",
|
|
5169
|
+
"trivia",
|
|
5170
|
+
"label",
|
|
5171
|
+
"expect",
|
|
5172
|
+
"_tf",
|
|
5173
|
+
"_nd",
|
|
5174
|
+
`return (${ir})`
|
|
5175
|
+
);
|
|
5176
|
+
const map = fn(
|
|
5177
|
+
rules,
|
|
5178
|
+
ref,
|
|
5179
|
+
regex,
|
|
5180
|
+
literal,
|
|
5181
|
+
keywords,
|
|
5182
|
+
sequence,
|
|
5183
|
+
choice,
|
|
5184
|
+
many,
|
|
5185
|
+
oneOrMore,
|
|
5186
|
+
optional,
|
|
5187
|
+
sepBy,
|
|
5188
|
+
not,
|
|
5189
|
+
node,
|
|
5190
|
+
parser,
|
|
5191
|
+
scanTo,
|
|
5192
|
+
transform,
|
|
5193
|
+
skip,
|
|
5194
|
+
trivia,
|
|
5195
|
+
label,
|
|
5196
|
+
expect,
|
|
5197
|
+
_tf,
|
|
5198
|
+
_nd
|
|
5199
|
+
);
|
|
5200
|
+
return Object.entries(map);
|
|
5201
|
+
}
|
|
5202
|
+
var Serializer = class {
|
|
5203
|
+
counts = /* @__PURE__ */ new Map();
|
|
5204
|
+
selfRef = /* @__PURE__ */ new Set();
|
|
5205
|
+
// combinators reached by an unnamed lazy pointing back into their own subtree
|
|
5206
|
+
constName = /* @__PURE__ */ new Map();
|
|
5207
|
+
emitted = /* @__PURE__ */ new Set();
|
|
5208
|
+
decls = [];
|
|
5209
|
+
ruleValues = /* @__PURE__ */ new Set();
|
|
5210
|
+
ruleMap;
|
|
5211
|
+
constructor(ruleMap) {
|
|
5212
|
+
this.ruleMap = ruleMap;
|
|
5213
|
+
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
5214
|
+
}
|
|
5215
|
+
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
5216
|
+
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
5217
|
+
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
5218
|
+
body(c) {
|
|
5219
|
+
return c._def.tag === "lazy" && ruleNameOf(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
5220
|
+
}
|
|
5221
|
+
run() {
|
|
5222
|
+
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
5223
|
+
let n = 0;
|
|
5224
|
+
for (const [c, count] of this.counts) {
|
|
5225
|
+
if (this.ruleValues.has(c)) continue;
|
|
5226
|
+
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
5227
|
+
}
|
|
5228
|
+
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
5229
|
+
if (this.decls.length === 0) return `rules((g) => ({
|
|
5230
|
+
${body}
|
|
5231
|
+
}))`;
|
|
5232
|
+
return `rules((g) => {
|
|
5233
|
+
${this.decls.map((d) => " " + d).join("\n")}
|
|
5234
|
+
return ({
|
|
5235
|
+
${body}
|
|
5236
|
+
})
|
|
5237
|
+
})`;
|
|
5238
|
+
}
|
|
5239
|
+
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
5240
|
+
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
5241
|
+
analyze(c, active) {
|
|
5242
|
+
const def = c._def;
|
|
5243
|
+
if (def.tag === "lazy") {
|
|
5244
|
+
if (ruleNameOf(c) !== void 0) return;
|
|
5245
|
+
const target = lazyTarget(c);
|
|
5246
|
+
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
5247
|
+
if (active.has(target)) {
|
|
5248
|
+
this.selfRef.add(target);
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
5251
|
+
this.analyze(target, active);
|
|
5252
|
+
return;
|
|
5253
|
+
}
|
|
5254
|
+
if (def.tag === "guard" || def.tag === "withCtx" || def.tag === "recover" || def.tag === "unknown") {
|
|
5255
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5256
|
+
}
|
|
5257
|
+
this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
|
|
5258
|
+
if ((this.counts.get(c) ?? 0) > 1) return;
|
|
5259
|
+
active.add(c);
|
|
5260
|
+
for (const child of childrenOf2(def)) this.analyze(child, active);
|
|
5261
|
+
active.delete(c);
|
|
5262
|
+
}
|
|
5263
|
+
/** Reference `c`: a const var if it has one (emitting its decl first), else inline.
|
|
5264
|
+
* `selfOf` is the const currently being wrapped in a ref()/define closure — an
|
|
5265
|
+
* unnamed lazy back into it emits the local ref var instead of recursing. */
|
|
5266
|
+
ref(c, selfOf) {
|
|
5267
|
+
const cn = this.constName.get(c);
|
|
5268
|
+
if (cn) {
|
|
5269
|
+
this.emitDecl(c);
|
|
5270
|
+
return cn;
|
|
5271
|
+
}
|
|
5272
|
+
return this.expr(c, selfOf);
|
|
5273
|
+
}
|
|
5274
|
+
emitDecl(c) {
|
|
5275
|
+
const name = this.constName.get(c);
|
|
5276
|
+
if (this.emitted.has(name)) return;
|
|
5277
|
+
this.emitted.add(name);
|
|
5278
|
+
this.decls.push(`const ${name} = ${this.wrap(c)}`);
|
|
5279
|
+
}
|
|
5280
|
+
selfN = 0;
|
|
5281
|
+
/** Emit `c`'s own constructor call, wrapping it in a `ref()`+`.define()` closure
|
|
5282
|
+
* if it is self-referential (an unnamed lazy in its subtree points back to it) —
|
|
5283
|
+
* the shape `balanced()` and other recursive library combinators build. */
|
|
5284
|
+
wrap(c) {
|
|
5285
|
+
if (!this.selfRef.has(c)) return this.expr(c, null);
|
|
5286
|
+
const rv = `_rr${this.selfN++}`;
|
|
5287
|
+
const bodyExpr = this.expr(c, { comb: c, var: rv });
|
|
5288
|
+
return `(() => { const ${rv} = ref(); const _b = ${bodyExpr}; ${rv}.define(_b); return _b })()`;
|
|
5289
|
+
}
|
|
5290
|
+
/** Emit the constructor-call source for `c` (never a const shortcut for `c` itself). */
|
|
5291
|
+
expr(c, selfOf) {
|
|
5292
|
+
const def = c._def;
|
|
5293
|
+
const kid = (x) => this.ref(x, selfOf);
|
|
5294
|
+
switch (def.tag) {
|
|
5295
|
+
case "lazy": {
|
|
5296
|
+
if (ruleNameOf(c) !== void 0) return `g[${JSON.stringify(ruleNameOf(c))}]`;
|
|
5297
|
+
const target = lazyTarget(c);
|
|
5298
|
+
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
5299
|
+
return this.ref(target, selfOf);
|
|
5300
|
+
}
|
|
5301
|
+
case "literal":
|
|
5302
|
+
return `literal(${JSON.stringify(def.value)}${def.caseInsensitive ? ", { caseInsensitive: true }" : ""})`;
|
|
5303
|
+
case "regex":
|
|
5304
|
+
return `regex(${JSON.stringify(def.source)}, ${JSON.stringify(def.flags)})`;
|
|
5305
|
+
case "keywords":
|
|
5306
|
+
return `keywords(${JSON.stringify(def.words)}, { caseInsensitive: ${def.caseInsensitive}${def.boundary !== void 0 ? `, boundary: ${JSON.stringify(def.boundary)}` : ""} })`;
|
|
5307
|
+
case "sequence":
|
|
5308
|
+
return `sequence(${def.parsers.map(kid).join(", ")})`;
|
|
5309
|
+
case "choice": {
|
|
5310
|
+
if (def.gates.some((g) => g !== null)) throw new Unserializable("choice with a gate() has no source");
|
|
5311
|
+
return `choice(${def.parsers.map(kid).join(", ")})`;
|
|
5312
|
+
}
|
|
5313
|
+
case "many":
|
|
5314
|
+
return `many(${kid(def.parser)})`;
|
|
5315
|
+
case "oneOrMore":
|
|
5316
|
+
return `oneOrMore(${kid(def.parser)})`;
|
|
5317
|
+
case "optional":
|
|
5318
|
+
return `optional(${kid(def.parser)})`;
|
|
5319
|
+
case "sepBy":
|
|
5320
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
5321
|
+
case "not":
|
|
5322
|
+
return `not(${kid(def.parser)})`;
|
|
5323
|
+
case "trivia":
|
|
5324
|
+
return `trivia(${kid(def.parser)})`;
|
|
5325
|
+
case "label":
|
|
5326
|
+
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
5327
|
+
case "expect":
|
|
5328
|
+
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
5329
|
+
case "skip":
|
|
5330
|
+
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
5331
|
+
case "scanTo":
|
|
5332
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
5333
|
+
case "transform": {
|
|
5334
|
+
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
5335
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
5336
|
+
}
|
|
5337
|
+
case "node": {
|
|
5338
|
+
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
5339
|
+
const opts = def.collapse ? `, { collapse: true }` : "";
|
|
5340
|
+
if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
|
|
5341
|
+
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
5342
|
+
}
|
|
5343
|
+
case "grammar": {
|
|
5344
|
+
const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
|
|
5345
|
+
return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
|
|
5346
|
+
}
|
|
5347
|
+
case "guard":
|
|
5348
|
+
case "withCtx":
|
|
5349
|
+
case "recover":
|
|
5350
|
+
case "unknown":
|
|
5351
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
};
|
|
5355
|
+
|
|
5356
|
+
// src/compiler/linker.ts
|
|
5357
|
+
function fusedBody(pieces) {
|
|
5358
|
+
const winner = /* @__PURE__ */ new Map();
|
|
5359
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
5360
|
+
for (const [k, p] of winner) {
|
|
5361
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
5362
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
const contributing = new Set(winner.values());
|
|
5366
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
5367
|
+
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
5368
|
+
const lines = [
|
|
5369
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
5370
|
+
"const _pfFail = {}",
|
|
5371
|
+
"let _pfEnd",
|
|
5372
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
5373
|
+
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
5374
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
5375
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
5376
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
5377
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
5378
|
+
];
|
|
5379
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
5380
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
5381
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
5382
|
+
for (const [k, p] of winner) {
|
|
5383
|
+
const fs2 = p.firstSets?.get(k);
|
|
5384
|
+
if (fs2) finalFS.set(k, fs2);
|
|
5385
|
+
}
|
|
5386
|
+
const body = rawBody.replace(
|
|
5387
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
5388
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
5389
|
+
// matches every well-formed placeholder.
|
|
5390
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
5391
|
+
(_m, name, codevar) => {
|
|
5392
|
+
const fs2 = finalFS.get(name);
|
|
5393
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
5394
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
5395
|
+
}
|
|
5396
|
+
);
|
|
5397
|
+
const env = {};
|
|
5398
|
+
for (const p of contributing) {
|
|
5399
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
5400
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
5401
|
+
}
|
|
5402
|
+
return { body, env };
|
|
5403
|
+
}
|
|
5404
|
+
function emitFusedSource(pieces) {
|
|
5405
|
+
const { body, env } = fusedBody(pieces);
|
|
5406
|
+
if (Object.keys(env).length > 0) {
|
|
5407
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
5408
|
+
}
|
|
5409
|
+
return `/* @__PURE__ */ (() => {
|
|
5410
|
+
${body}
|
|
5411
|
+
})()`;
|
|
5412
|
+
}
|
|
5413
|
+
function isIRPiece(p) {
|
|
5414
|
+
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5415
|
+
}
|
|
5416
|
+
function materializePiece(p) {
|
|
5417
|
+
if (!isIRPiece(p)) return p;
|
|
5418
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
5419
|
+
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
5420
|
+
return pieces;
|
|
5421
|
+
}
|
|
5422
|
+
|
|
4948
5423
|
// src/cst/offset-model.ts
|
|
4949
5424
|
var { isFinite } = Number;
|
|
4950
5425
|
|
|
@@ -5518,6 +5993,14 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5518
5993
|
const obj = callee?.object?.name;
|
|
5519
5994
|
const prop = callee?.property?.name;
|
|
5520
5995
|
const args = node2.arguments;
|
|
5996
|
+
if (obj === "Object" && prop === "assign" && args && args[1]?.type === "ObjectExpression") {
|
|
5997
|
+
for (const p of args[1].properties ?? []) {
|
|
5998
|
+
if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
|
|
5999
|
+
found = { start: p.value.start, end: p.value.end };
|
|
6000
|
+
return;
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
5521
6004
|
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5522
6005
|
const descriptor = args[2];
|
|
5523
6006
|
for (const p of descriptor?.properties ?? []) {
|
|
@@ -5538,6 +6021,20 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5538
6021
|
visit(root);
|
|
5539
6022
|
return found;
|
|
5540
6023
|
}
|
|
6024
|
+
function extractImportBindings(body) {
|
|
6025
|
+
const out = /* @__PURE__ */ new Map();
|
|
6026
|
+
for (const stmt of body) {
|
|
6027
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
6028
|
+
const s = stmt;
|
|
6029
|
+
for (const spec of s.specifiers) {
|
|
6030
|
+
if (spec.type === "ImportSpecifier") {
|
|
6031
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
6032
|
+
out.set(spec.local.name, { source: s.source.value, imported });
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
return out;
|
|
6037
|
+
}
|
|
5541
6038
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5542
6039
|
let result;
|
|
5543
6040
|
try {
|
|
@@ -5612,14 +6109,57 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5612
6109
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5613
6110
|
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5614
6111
|
const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
6112
|
+
const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
|
|
5615
6113
|
const serializePieces = (p) => {
|
|
5616
|
-
const mapLit = (m) => `new Map([${[...m].map(
|
|
5617
|
-
|
|
6114
|
+
const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
|
|
6115
|
+
([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
|
|
6116
|
+
).join(", ")}])`;
|
|
6117
|
+
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, mfFns: [], buildFns: [] }`;
|
|
5618
6118
|
};
|
|
5619
|
-
const
|
|
5620
|
-
const
|
|
6119
|
+
const isSpread = (it) => "__spreadLocal" in it;
|
|
6120
|
+
const isIR = (it) => "ir" in it && !("ruleFns" in it);
|
|
6121
|
+
const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
|
|
6122
|
+
const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
|
|
6123
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
|
|
5621
6124
|
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5622
6125
|
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
6126
|
+
const resolveModulePieces = (file, exportName, seen) => {
|
|
6127
|
+
const mod = parseModuleCached(file);
|
|
6128
|
+
if (!mod) return null;
|
|
6129
|
+
const localFor = exportLocalName(mod.body, exportName);
|
|
6130
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
6131
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
6132
|
+
if (!literalRange) return null;
|
|
6133
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
6134
|
+
const imports = extractImportBindings(mod.body);
|
|
6135
|
+
const stubNames = [];
|
|
6136
|
+
const stubVals = [];
|
|
6137
|
+
const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
|
|
6138
|
+
const done = /* @__PURE__ */ new Set();
|
|
6139
|
+
for (let m; m = spreadRe.exec(literal2); ) {
|
|
6140
|
+
const local = m[1];
|
|
6141
|
+
if (done.has(local)) continue;
|
|
6142
|
+
done.add(local);
|
|
6143
|
+
const b = imports.get(local);
|
|
6144
|
+
let subPieces = [];
|
|
6145
|
+
if (b) {
|
|
6146
|
+
let subFile = null;
|
|
6147
|
+
try {
|
|
6148
|
+
subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
|
|
6149
|
+
} catch {
|
|
6150
|
+
}
|
|
6151
|
+
if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
|
|
6152
|
+
}
|
|
6153
|
+
stubNames.push(local);
|
|
6154
|
+
stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
|
|
6155
|
+
}
|
|
6156
|
+
try {
|
|
6157
|
+
const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
|
|
6158
|
+
return list.map(materializePiece);
|
|
6159
|
+
} catch {
|
|
6160
|
+
return null;
|
|
6161
|
+
}
|
|
6162
|
+
};
|
|
5623
6163
|
const importedPieces = (localName) => {
|
|
5624
6164
|
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5625
6165
|
let result2 = null;
|
|
@@ -5630,41 +6170,34 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5630
6170
|
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5631
6171
|
} catch {
|
|
5632
6172
|
}
|
|
5633
|
-
|
|
5634
|
-
if (mod) {
|
|
5635
|
-
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5636
|
-
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5637
|
-
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5638
|
-
if (literalRange) {
|
|
5639
|
-
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5640
|
-
try {
|
|
5641
|
-
result2 = new Function(`return (${literal2})`)();
|
|
5642
|
-
} catch {
|
|
5643
|
-
result2 = null;
|
|
5644
|
-
}
|
|
5645
|
-
}
|
|
5646
|
-
}
|
|
6173
|
+
result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
|
|
5647
6174
|
}
|
|
5648
6175
|
importedPiecesCache.set(localName, result2);
|
|
5649
6176
|
return result2;
|
|
5650
6177
|
};
|
|
6178
|
+
const localArg = (rm, label2) => {
|
|
6179
|
+
const entries = [...rm];
|
|
6180
|
+
const ns = nsFor(label2);
|
|
6181
|
+
const p = compileLinkable(entries, ns);
|
|
6182
|
+
if (!p) return null;
|
|
6183
|
+
const ir = serializeRuleMap(entries);
|
|
6184
|
+
return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
|
|
6185
|
+
};
|
|
5651
6186
|
const argPieces = (arg, label2) => {
|
|
5652
6187
|
if (isRulesCall(arg)) {
|
|
5653
6188
|
const factory = arg.arguments[0];
|
|
5654
6189
|
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5655
|
-
|
|
5656
|
-
return p ? [p] : null;
|
|
6190
|
+
return rm ? localArg(rm, label2) : null;
|
|
5657
6191
|
}
|
|
5658
6192
|
if (arg.type === "Identifier") {
|
|
5659
6193
|
const name = arg.name;
|
|
5660
6194
|
const rm = localRuleMaps.get(name);
|
|
5661
|
-
if (rm)
|
|
5662
|
-
const p = compileLinkable([...rm], nsFor(label2));
|
|
5663
|
-
return p ? [p] : null;
|
|
5664
|
-
}
|
|
6195
|
+
if (rm) return localArg(rm, label2);
|
|
5665
6196
|
const composed = localComposedPieces.get(name);
|
|
5666
|
-
if (composed) return composed;
|
|
5667
|
-
|
|
6197
|
+
if (composed) return { pieces: composed };
|
|
6198
|
+
const pieces = importedPieces(name);
|
|
6199
|
+
if (!pieces) return null;
|
|
6200
|
+
return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
|
|
5668
6201
|
}
|
|
5669
6202
|
return null;
|
|
5670
6203
|
};
|
|
@@ -5677,16 +6210,20 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5677
6210
|
}
|
|
5678
6211
|
const elements = arr.elements;
|
|
5679
6212
|
const pieces = [];
|
|
6213
|
+
const carried = [];
|
|
5680
6214
|
for (let i = 0; i < elements.length; i++) {
|
|
5681
|
-
const
|
|
5682
|
-
if (!
|
|
6215
|
+
const r = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
6216
|
+
if (!r) {
|
|
5683
6217
|
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5684
6218
|
return null;
|
|
5685
6219
|
}
|
|
5686
|
-
pieces.push(...
|
|
6220
|
+
pieces.push(...r.pieces);
|
|
6221
|
+
if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
|
|
6222
|
+
else if (r.ir) carried.push(r.ir);
|
|
6223
|
+
else carried.push(...r.pieces);
|
|
5687
6224
|
}
|
|
5688
6225
|
try {
|
|
5689
|
-
return { replacement: emitFusedSource(pieces), pieces };
|
|
6226
|
+
return { replacement: emitFusedSource(pieces), pieces, carried };
|
|
5690
6227
|
} catch (e) {
|
|
5691
6228
|
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5692
6229
|
return null;
|
|
@@ -5773,9 +6310,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5773
6310
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5774
6311
|
let replacement = compiledRules.replacement;
|
|
5775
6312
|
if (exportPrefix) {
|
|
5776
|
-
const
|
|
6313
|
+
const ns = nsFor(varName);
|
|
6314
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], ns);
|
|
5777
6315
|
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5778
|
-
|
|
6316
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
6317
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
5779
6318
|
}
|
|
5780
6319
|
}
|
|
5781
6320
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
@@ -5785,7 +6324,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5785
6324
|
const fused = compileComposeCall(init);
|
|
5786
6325
|
if (!fused) continue;
|
|
5787
6326
|
localComposedPieces.set(varName, fused.pieces);
|
|
5788
|
-
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.
|
|
6327
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
5789
6328
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5790
6329
|
continue;
|
|
5791
6330
|
}
|