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.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);
|
|
@@ -1265,6 +1313,41 @@ function markUnusedValues(root) {
|
|
|
1265
1313
|
visit(resolveRoot(root), true);
|
|
1266
1314
|
}
|
|
1267
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
|
+
|
|
1268
1351
|
// src/combinators/not.ts
|
|
1269
1352
|
function not(combinator) {
|
|
1270
1353
|
const meta = {
|
|
@@ -1371,6 +1454,20 @@ function transform(combinator, fn) {
|
|
|
1371
1454
|
}
|
|
1372
1455
|
};
|
|
1373
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
|
+
}
|
|
1374
1471
|
function trivia(combinator) {
|
|
1375
1472
|
const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
|
|
1376
1473
|
return {
|
|
@@ -2999,16 +3096,12 @@ function namedFnPrelude() {
|
|
|
2999
3096
|
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
3000
3097
|
ctx.namedFnDecls.push([
|
|
3001
3098
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
3002
|
-
` let _pfok = false, _pfv, _pfe = _pos`,
|
|
3003
3099
|
` _pfail: {`,
|
|
3004
3100
|
...reindentStmts(bodyStmts, 2),
|
|
3005
|
-
`
|
|
3006
|
-
`
|
|
3007
|
-
` _pfok = true`,
|
|
3101
|
+
` ${NAMED_FN_END} = ${endVar}`,
|
|
3102
|
+
` return ${valueVar}`,
|
|
3008
3103
|
` }`,
|
|
3009
|
-
`
|
|
3010
|
-
` ${NAMED_FN_END} = _pfe`,
|
|
3011
|
-
` return _pfv`,
|
|
3104
|
+
` return ${NAMED_FN_FAIL}`,
|
|
3012
3105
|
`}`
|
|
3013
3106
|
].join("\n"));
|
|
3014
3107
|
}
|
|
@@ -3709,11 +3802,16 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3709
3802
|
const atStart = failsAtStart(p);
|
|
3710
3803
|
const staticFx = armStaticExpected(ctx, p);
|
|
3711
3804
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3712
|
-
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
|
+
})();
|
|
3713
3810
|
let gateCond = null;
|
|
3714
3811
|
if (gate) {
|
|
3715
3812
|
const gateIdx = ctx.mapFns.length;
|
|
3716
3813
|
ctx.mapFns.push(gate);
|
|
3814
|
+
ctx.mapFnSrcs.push(null);
|
|
3717
3815
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3718
3816
|
}
|
|
3719
3817
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -3772,13 +3870,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
|
3772
3870
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
3773
3871
|
return emitFirstMatch(def, ctx, pos);
|
|
3774
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
|
+
}
|
|
3775
3883
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
3776
3884
|
const def = p._def;
|
|
3777
3885
|
if (def.tag === "transform") {
|
|
3778
3886
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
3779
|
-
const fnIdx = ctx.
|
|
3780
|
-
ctx.mapFns.push(def.fn);
|
|
3781
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3887
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
3782
3888
|
const vv = v(ctx);
|
|
3783
3889
|
return {
|
|
3784
3890
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -3961,7 +4067,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3961
4067
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
3962
4068
|
];
|
|
3963
4069
|
ctx.indent++;
|
|
3964
|
-
const probeCtx = { ...ctx, capturing: false };
|
|
4070
|
+
const probeCtx = { ...ctx, capturing: false, noHoist: true };
|
|
3965
4071
|
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
3966
4072
|
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
3967
4073
|
if (def.skip.length > 0) {
|
|
@@ -4152,6 +4258,32 @@ function emitExpect(def, ctx, pos) {
|
|
|
4152
4258
|
return { stmts, valueVar: valVar, endVar };
|
|
4153
4259
|
}
|
|
4154
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) {
|
|
4155
4287
|
const def = p._def;
|
|
4156
4288
|
switch (def.tag) {
|
|
4157
4289
|
case "literal":
|
|
@@ -4197,9 +4329,7 @@ function emit(p, ctx, pos) {
|
|
|
4197
4329
|
};
|
|
4198
4330
|
}
|
|
4199
4331
|
}
|
|
4200
|
-
const fnIdx = ctx.
|
|
4201
|
-
ctx.mapFns.push(def.fn);
|
|
4202
|
-
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
4332
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
4203
4333
|
const mv = v(ctx, "_mapped");
|
|
4204
4334
|
return {
|
|
4205
4335
|
stmts: [
|
|
@@ -4264,6 +4394,7 @@ function emit(p, ctx, pos) {
|
|
|
4264
4394
|
case "guard": {
|
|
4265
4395
|
const fnIdx = ctx.mapFns.length;
|
|
4266
4396
|
ctx.mapFns.push(def.predicate);
|
|
4397
|
+
ctx.mapFnSrcs.push(null);
|
|
4267
4398
|
const vv = v(ctx);
|
|
4268
4399
|
return {
|
|
4269
4400
|
stmts: [
|
|
@@ -4278,6 +4409,7 @@ function emit(p, ctx, pos) {
|
|
|
4278
4409
|
const evIdx = ctx.mapFns.length;
|
|
4279
4410
|
const extra = def.extra;
|
|
4280
4411
|
ctx.mapFns.push((() => extra));
|
|
4412
|
+
ctx.mapFnSrcs.push(null);
|
|
4281
4413
|
const innerParser = def.parser;
|
|
4282
4414
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
4283
4415
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -4397,9 +4529,58 @@ function childrenOf(def) {
|
|
|
4397
4529
|
return [];
|
|
4398
4530
|
}
|
|
4399
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;
|
|
4400
4547
|
function analyzeLazyUsage(root) {
|
|
4401
4548
|
return analyzeLazyUsageMulti([root]);
|
|
4402
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
|
+
}
|
|
4403
4584
|
function analyzeLazyUsageMulti(roots) {
|
|
4404
4585
|
const counts = /* @__PURE__ */ new Map();
|
|
4405
4586
|
const recursive = /* @__PURE__ */ new Set();
|
|
@@ -4426,10 +4607,14 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4426
4607
|
active.delete(p);
|
|
4427
4608
|
return;
|
|
4428
4609
|
}
|
|
4610
|
+
if (isHoistableTag(def.tag)) {
|
|
4611
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
4612
|
+
}
|
|
4429
4613
|
for (const child of childrenOf(def)) walk2(child);
|
|
4430
4614
|
}
|
|
4431
|
-
|
|
4432
|
-
|
|
4615
|
+
const rootsArr = [...roots];
|
|
4616
|
+
for (const root of rootsArr) walk2(root);
|
|
4617
|
+
return { counts, recursive, sizes: subtreeSizes(rootsArr) };
|
|
4433
4618
|
}
|
|
4434
4619
|
function ruleDependencies(ruleMap) {
|
|
4435
4620
|
const nameOf = /* @__PURE__ */ new Map();
|
|
@@ -4828,64 +5013,6 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
|
|
|
4828
5013
|
].join("\n");
|
|
4829
5014
|
}
|
|
4830
5015
|
|
|
4831
|
-
// src/compiler/linker.ts
|
|
4832
|
-
function fusedBody(pieces) {
|
|
4833
|
-
const winner = /* @__PURE__ */ new Map();
|
|
4834
|
-
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4835
|
-
for (const [k, p] of winner) {
|
|
4836
|
-
for (const d of p.deps.get(k) ?? []) {
|
|
4837
|
-
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
const contributing = new Set(winner.values());
|
|
4841
|
-
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4842
|
-
const needsHostReads = [...contributing].some((p) => p.needsHostReads);
|
|
4843
|
-
const lines = [
|
|
4844
|
-
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4845
|
-
"const _pfFail = {}",
|
|
4846
|
-
"let _pfEnd",
|
|
4847
|
-
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4848
|
-
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
4849
|
-
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4850
|
-
...[...contributing].flatMap((p) => p.prelude),
|
|
4851
|
-
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4852
|
-
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4853
|
-
];
|
|
4854
|
-
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4855
|
-
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4856
|
-
const finalFS = /* @__PURE__ */ new Map();
|
|
4857
|
-
for (const [k, p] of winner) {
|
|
4858
|
-
const fs2 = p.firstSets?.get(k);
|
|
4859
|
-
if (fs2) finalFS.set(k, fs2);
|
|
4860
|
-
}
|
|
4861
|
-
const body = rawBody.replace(
|
|
4862
|
-
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4863
|
-
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4864
|
-
// matches every well-formed placeholder.
|
|
4865
|
-
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4866
|
-
(_m, name, codevar) => {
|
|
4867
|
-
const fs2 = finalFS.get(name);
|
|
4868
|
-
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4869
|
-
return `(${firstSetCond(codevar, fs2)})`;
|
|
4870
|
-
}
|
|
4871
|
-
);
|
|
4872
|
-
const env = {};
|
|
4873
|
-
for (const p of contributing) {
|
|
4874
|
-
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4875
|
-
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4876
|
-
}
|
|
4877
|
-
return { body, env };
|
|
4878
|
-
}
|
|
4879
|
-
function emitFusedSource(pieces) {
|
|
4880
|
-
const { body, env } = fusedBody(pieces);
|
|
4881
|
-
if (Object.keys(env).length > 0) {
|
|
4882
|
-
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4883
|
-
}
|
|
4884
|
-
return `/* @__PURE__ */ (() => {
|
|
4885
|
-
${body}
|
|
4886
|
-
})()`;
|
|
4887
|
-
}
|
|
4888
|
-
|
|
4889
5016
|
// src/combinators/scanTo.ts
|
|
4890
5017
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
4891
5018
|
const meta = {
|
|
@@ -4980,6 +5107,354 @@ function escapeClassChar(ch) {
|
|
|
4980
5107
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
4981
5108
|
}
|
|
4982
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
|
+
|
|
4983
5458
|
// src/cst/offset-model.ts
|
|
4984
5459
|
var { isFinite } = Number;
|
|
4985
5460
|
|
|
@@ -5553,6 +6028,14 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5553
6028
|
const obj = callee?.object?.name;
|
|
5554
6029
|
const prop = callee?.property?.name;
|
|
5555
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
|
+
}
|
|
5556
6039
|
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5557
6040
|
const descriptor = args[2];
|
|
5558
6041
|
for (const p of descriptor?.properties ?? []) {
|
|
@@ -5573,6 +6056,20 @@ function findCarriedPiecesLiteral(root) {
|
|
|
5573
6056
|
visit(root);
|
|
5574
6057
|
return found;
|
|
5575
6058
|
}
|
|
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
|
+
}
|
|
5576
6073
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false) {
|
|
5577
6074
|
let result;
|
|
5578
6075
|
try {
|
|
@@ -5647,14 +6144,57 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5647
6144
|
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5648
6145
|
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5649
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");
|
|
5650
6148
|
const serializePieces = (p) => {
|
|
5651
|
-
const mapLit = (m) => `new Map([${[...m].map(
|
|
5652
|
-
|
|
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: [] }`;
|
|
5653
6153
|
};
|
|
5654
|
-
const
|
|
5655
|
-
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 })`;
|
|
5656
6159
|
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5657
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
|
+
};
|
|
5658
6198
|
const importedPieces = (localName) => {
|
|
5659
6199
|
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5660
6200
|
let result2 = null;
|
|
@@ -5665,41 +6205,34 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5665
6205
|
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5666
6206
|
} catch {
|
|
5667
6207
|
}
|
|
5668
|
-
|
|
5669
|
-
if (mod) {
|
|
5670
|
-
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5671
|
-
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5672
|
-
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5673
|
-
if (literalRange) {
|
|
5674
|
-
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5675
|
-
try {
|
|
5676
|
-
result2 = new Function(`return (${literal2})`)();
|
|
5677
|
-
} catch {
|
|
5678
|
-
result2 = null;
|
|
5679
|
-
}
|
|
5680
|
-
}
|
|
5681
|
-
}
|
|
6208
|
+
result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
|
|
5682
6209
|
}
|
|
5683
6210
|
importedPiecesCache.set(localName, result2);
|
|
5684
6211
|
return result2;
|
|
5685
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
|
+
};
|
|
5686
6221
|
const argPieces = (arg, label2) => {
|
|
5687
6222
|
if (isRulesCall(arg)) {
|
|
5688
6223
|
const factory = arg.arguments[0];
|
|
5689
6224
|
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5690
|
-
|
|
5691
|
-
return p ? [p] : null;
|
|
6225
|
+
return rm ? localArg(rm, label2) : null;
|
|
5692
6226
|
}
|
|
5693
6227
|
if (arg.type === "Identifier") {
|
|
5694
6228
|
const name = arg.name;
|
|
5695
6229
|
const rm = localRuleMaps.get(name);
|
|
5696
|
-
if (rm)
|
|
5697
|
-
const p = compileLinkable([...rm], nsFor(label2));
|
|
5698
|
-
return p ? [p] : null;
|
|
5699
|
-
}
|
|
6230
|
+
if (rm) return localArg(rm, label2);
|
|
5700
6231
|
const composed = localComposedPieces.get(name);
|
|
5701
|
-
if (composed) return composed;
|
|
5702
|
-
|
|
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 };
|
|
5703
6236
|
}
|
|
5704
6237
|
return null;
|
|
5705
6238
|
};
|
|
@@ -5712,16 +6245,20 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5712
6245
|
}
|
|
5713
6246
|
const elements = arr.elements;
|
|
5714
6247
|
const pieces = [];
|
|
6248
|
+
const carried = [];
|
|
5715
6249
|
for (let i = 0; i < elements.length; i++) {
|
|
5716
|
-
const
|
|
5717
|
-
if (!
|
|
6250
|
+
const r = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
6251
|
+
if (!r) {
|
|
5718
6252
|
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5719
6253
|
return null;
|
|
5720
6254
|
}
|
|
5721
|
-
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);
|
|
5722
6259
|
}
|
|
5723
6260
|
try {
|
|
5724
|
-
return { replacement: emitFusedSource(pieces), pieces };
|
|
6261
|
+
return { replacement: emitFusedSource(pieces), pieces, carried };
|
|
5725
6262
|
} catch (e) {
|
|
5726
6263
|
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5727
6264
|
return null;
|
|
@@ -5808,9 +6345,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5808
6345
|
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5809
6346
|
let replacement = compiledRules.replacement;
|
|
5810
6347
|
if (exportPrefix) {
|
|
5811
|
-
const
|
|
6348
|
+
const ns = nsFor(varName);
|
|
6349
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], ns);
|
|
5812
6350
|
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5813
|
-
|
|
6351
|
+
const ir = serializeRuleMap([...compiledRules.ruleMap]);
|
|
6352
|
+
replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
|
|
5814
6353
|
}
|
|
5815
6354
|
}
|
|
5816
6355
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
@@ -5820,7 +6359,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5820
6359
|
const fused = compileComposeCall(init);
|
|
5821
6360
|
if (!fused) continue;
|
|
5822
6361
|
localComposedPieces.set(varName, fused.pieces);
|
|
5823
|
-
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.
|
|
6362
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
|
|
5824
6363
|
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5825
6364
|
continue;
|
|
5826
6365
|
}
|