parseman 0.22.0 → 0.24.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/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +6 -0
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +3 -1
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +7 -2
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/index.cjs +429 -75
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +429 -75
- package/dist/index.js.map +3 -3
- package/dist/plugin/index.cjs +284 -66
- package/dist/plugin/index.cjs.map +3 -3
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +284 -66
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/ebnf.d.ts +13 -0
- package/dist/spec/ebnf.d.ts.map +1 -0
- package/dist/spec/index.cjs +977 -0
- package/dist/spec/index.cjs.map +7 -0
- package/dist/spec/index.d.ts +15 -0
- package/dist/spec/index.d.ts.map +1 -0
- package/dist/spec/index.js +954 -0
- package/dist/spec/index.js.map +7 -0
- package/dist/spec/model.d.ts +117 -0
- package/dist/spec/model.d.ts.map +1 -0
- package/dist/spec/railroad-lib.d.ts +11 -0
- package/dist/spec/railroad-lib.d.ts.map +1 -0
- package/dist/spec/railroad.d.ts +18 -0
- package/dist/spec/railroad.d.ts.map +1 -0
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -63,7 +63,6 @@ __export(index_exports, {
|
|
|
63
63
|
parse: () => parse,
|
|
64
64
|
parseDoc: () => parseDoc,
|
|
65
65
|
parser: () => parser,
|
|
66
|
-
pick: () => pick,
|
|
67
66
|
recover: () => recover,
|
|
68
67
|
ref: () => ref,
|
|
69
68
|
regex: () => regex,
|
|
@@ -432,24 +431,35 @@ function literal(value, opts = {}) {
|
|
|
432
431
|
{ lo: firstUpper, hi: firstUpper }
|
|
433
432
|
] } : firstSet2;
|
|
434
433
|
}
|
|
434
|
+
const expected = [JSON.stringify(value)];
|
|
435
|
+
const parse2 = !caseInsensitive && value.length === 1 ? (() => {
|
|
436
|
+
const code = value.charCodeAt(0);
|
|
437
|
+
return function parse3(input, pos, ctx) {
|
|
438
|
+
if (input.charCodeAt(pos) === code) {
|
|
439
|
+
const span = { start: pos, end: pos + 1 };
|
|
440
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
|
|
441
|
+
return { ok: true, value, span };
|
|
442
|
+
}
|
|
443
|
+
return failAt(ctx, expected, pos);
|
|
444
|
+
};
|
|
445
|
+
})() : function parse3(input, pos, ctx) {
|
|
446
|
+
const end = pos + value.length;
|
|
447
|
+
if (end > input.length) {
|
|
448
|
+
return failAt(ctx, expected, pos);
|
|
449
|
+
}
|
|
450
|
+
const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
|
|
451
|
+
if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
|
|
452
|
+
const span = { start: pos, end };
|
|
453
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
|
|
454
|
+
return { ok: true, value: matchedValue, span };
|
|
455
|
+
}
|
|
456
|
+
return failAt(ctx, expected, pos);
|
|
457
|
+
};
|
|
435
458
|
return {
|
|
436
459
|
_tag: "literal",
|
|
437
460
|
_meta: meta,
|
|
438
461
|
_def: { tag: "literal", value, caseInsensitive },
|
|
439
|
-
parse
|
|
440
|
-
const end = pos + value.length;
|
|
441
|
-
if (end > input.length) {
|
|
442
|
-
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
443
|
-
}
|
|
444
|
-
const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
|
|
445
|
-
if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
|
|
446
|
-
const span = { start: pos, end };
|
|
447
|
-
const leaf = { _tag: "leaf", value: matchedValue, span };
|
|
448
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
449
|
-
return { ok: true, value: matchedValue, span };
|
|
450
|
-
}
|
|
451
|
-
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
452
|
-
}
|
|
462
|
+
parse: parse2
|
|
453
463
|
};
|
|
454
464
|
}
|
|
455
465
|
|
|
@@ -584,8 +594,7 @@ function regex(pattern, flags = "") {
|
|
|
584
594
|
if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
|
|
585
595
|
const value = input.slice(pos, scanEnd);
|
|
586
596
|
const span2 = { start: pos, end: scanEnd };
|
|
587
|
-
|
|
588
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
|
|
597
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
|
|
589
598
|
return { ok: true, value, span: span2 };
|
|
590
599
|
}
|
|
591
600
|
anchored.lastIndex = pos;
|
|
@@ -594,8 +603,7 @@ function regex(pattern, flags = "") {
|
|
|
594
603
|
return failAt(ctx, [`/${source}/`], pos);
|
|
595
604
|
}
|
|
596
605
|
const span = { start: pos, end: pos + m[0].length };
|
|
597
|
-
|
|
598
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
606
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
|
|
599
607
|
return { ok: true, value: m[0], span };
|
|
600
608
|
}
|
|
601
609
|
};
|
|
@@ -903,8 +911,7 @@ function keywords(words, opts = {}) {
|
|
|
903
911
|
}
|
|
904
912
|
const value = m[0];
|
|
905
913
|
const span = { start: pos, end: pos + value.length };
|
|
906
|
-
|
|
907
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
914
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
|
|
908
915
|
return { ok: true, value, span };
|
|
909
916
|
}
|
|
910
917
|
};
|
|
@@ -1394,33 +1401,136 @@ function loopScanner(arms) {
|
|
|
1394
1401
|
}
|
|
1395
1402
|
function regexTriviaScanner(parser2) {
|
|
1396
1403
|
if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
|
|
1397
|
-
const
|
|
1398
|
-
|
|
1399
|
-
return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
|
|
1404
|
+
const source = parser2._def.source;
|
|
1405
|
+
return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
|
|
1400
1406
|
}
|
|
1401
|
-
function
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1407
|
+
function inRanges2(cp, ranges) {
|
|
1408
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
1409
|
+
const r = ranges[i];
|
|
1410
|
+
if (cp >= r[0] && cp <= r[1]) return true;
|
|
1411
|
+
}
|
|
1412
|
+
return false;
|
|
1413
|
+
}
|
|
1414
|
+
function classScanner(classBody) {
|
|
1415
|
+
const ranges = parseClassRanges(classBody);
|
|
1416
|
+
if (!ranges) return null;
|
|
1417
|
+
return (input, cur) => {
|
|
1418
|
+
let pos = cur;
|
|
1419
|
+
while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
|
|
1420
|
+
return pos;
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
function lineCommentScanner(commentCode) {
|
|
1424
|
+
return (input, cur) => {
|
|
1425
|
+
if (input.charCodeAt(cur) !== commentCode) return cur;
|
|
1426
|
+
let pos = cur + 1;
|
|
1427
|
+
while (pos < input.length) {
|
|
1428
|
+
const cc = input.charCodeAt(pos);
|
|
1429
|
+
if (cc === 10 || cc === 13) break;
|
|
1430
|
+
pos++;
|
|
1410
1431
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1432
|
+
return pos;
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
function classRunSource(source) {
|
|
1436
|
+
const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
|
|
1437
|
+
return m ? classScanner(m[1]) : null;
|
|
1438
|
+
}
|
|
1439
|
+
function classifyTriviaArm(arm) {
|
|
1440
|
+
const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
|
|
1441
|
+
if (cls) {
|
|
1442
|
+
const ranges = parseClassRanges(cls[1]);
|
|
1443
|
+
return ranges ? { kind: "class", ranges } : null;
|
|
1444
|
+
}
|
|
1445
|
+
const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
|
|
1446
|
+
if (lc) {
|
|
1447
|
+
const marker = lc[1];
|
|
1448
|
+
return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
|
|
1417
1449
|
}
|
|
1450
|
+
return null;
|
|
1451
|
+
}
|
|
1452
|
+
function armScanner(arm) {
|
|
1453
|
+
if (arm.kind === "comment") return lineCommentScanner(arm.code);
|
|
1454
|
+
const ranges = arm.ranges;
|
|
1418
1455
|
return (input, cur) => {
|
|
1419
1456
|
let pos = cur;
|
|
1420
|
-
while (pos < input.length &&
|
|
1457
|
+
while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
|
|
1458
|
+
return pos;
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
function fusedTriviaScanner(ranges, commentCodes) {
|
|
1462
|
+
const c0 = commentCodes[0];
|
|
1463
|
+
const single = commentCodes.length === 1;
|
|
1464
|
+
return (input, cur) => {
|
|
1465
|
+
let pos = cur;
|
|
1466
|
+
const len = input.length;
|
|
1467
|
+
for (; ; ) {
|
|
1468
|
+
const c = input.charCodeAt(pos);
|
|
1469
|
+
if (pos < len && inRanges2(c, ranges)) {
|
|
1470
|
+
pos++;
|
|
1471
|
+
continue;
|
|
1472
|
+
}
|
|
1473
|
+
if (single ? c === c0 : commentCodes.includes(c)) {
|
|
1474
|
+
pos++;
|
|
1475
|
+
while (pos < len) {
|
|
1476
|
+
const cc = input.charCodeAt(pos);
|
|
1477
|
+
if (cc === 10 || cc === 13) break;
|
|
1478
|
+
pos++;
|
|
1479
|
+
}
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1421
1484
|
return pos;
|
|
1422
1485
|
};
|
|
1423
1486
|
}
|
|
1487
|
+
function splitTopLevelAlts(body) {
|
|
1488
|
+
const arms = [];
|
|
1489
|
+
let inClass = false;
|
|
1490
|
+
let start = 0;
|
|
1491
|
+
for (let i = 0; i < body.length; i++) {
|
|
1492
|
+
const c = body[i];
|
|
1493
|
+
if (c === "\\") {
|
|
1494
|
+
i++;
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1497
|
+
if (inClass) {
|
|
1498
|
+
if (c === "]") inClass = false;
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
if (c === "[") inClass = true;
|
|
1502
|
+
else if (c === "(") return null;
|
|
1503
|
+
else if (c === "|") {
|
|
1504
|
+
arms.push(body.slice(start, i));
|
|
1505
|
+
start = i + 1;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
arms.push(body.slice(start));
|
|
1509
|
+
return arms;
|
|
1510
|
+
}
|
|
1511
|
+
function altStarSource(source) {
|
|
1512
|
+
const m = /^\(\?:(.*)\)[*+]$/.exec(source);
|
|
1513
|
+
if (!m) return null;
|
|
1514
|
+
const armSrcs = splitTopLevelAlts(m[1]);
|
|
1515
|
+
if (!armSrcs || armSrcs.length < 2) return null;
|
|
1516
|
+
const arms = [];
|
|
1517
|
+
for (const src of armSrcs) {
|
|
1518
|
+
const arm = classifyTriviaArm(src);
|
|
1519
|
+
if (!arm) return null;
|
|
1520
|
+
arms.push(arm);
|
|
1521
|
+
}
|
|
1522
|
+
const ranges = [];
|
|
1523
|
+
const commentCodes = [];
|
|
1524
|
+
for (const arm of arms) {
|
|
1525
|
+
if (arm.kind === "class") ranges.push(...arm.ranges);
|
|
1526
|
+
else commentCodes.push(arm.code);
|
|
1527
|
+
}
|
|
1528
|
+
if (commentCodes.some((code) => inRanges2(code, ranges))) {
|
|
1529
|
+
return loopScanner(arms.map(armScanner));
|
|
1530
|
+
}
|
|
1531
|
+
if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
|
|
1532
|
+
return fusedTriviaScanner(ranges, commentCodes);
|
|
1533
|
+
}
|
|
1424
1534
|
function blockCommentSource(source) {
|
|
1425
1535
|
return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
|
|
1426
1536
|
}
|
|
@@ -1774,6 +1884,7 @@ function markUnusedValues(root) {
|
|
|
1774
1884
|
}
|
|
1775
1885
|
|
|
1776
1886
|
// src/combinators/parser.ts
|
|
1887
|
+
var RULE_ORDER = "__parsemanRuleOrder";
|
|
1777
1888
|
function tagRule(r, key) {
|
|
1778
1889
|
;
|
|
1779
1890
|
r._ruleName = key;
|
|
@@ -1818,6 +1929,11 @@ function rules(a, b) {
|
|
|
1818
1929
|
for (const key of Object.keys(definitions)) {
|
|
1819
1930
|
markUnusedValues(cache[key]);
|
|
1820
1931
|
}
|
|
1932
|
+
Object.defineProperty(cache, RULE_ORDER, {
|
|
1933
|
+
value: Object.keys(definitions),
|
|
1934
|
+
enumerable: false,
|
|
1935
|
+
configurable: true
|
|
1936
|
+
});
|
|
1821
1937
|
return cache;
|
|
1822
1938
|
}
|
|
1823
1939
|
|
|
@@ -5522,9 +5638,10 @@ function publicRuleWrapperSource(rule, fnSource) {
|
|
|
5522
5638
|
if (labels === void 0) return fnSource;
|
|
5523
5639
|
return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
|
|
5524
5640
|
}
|
|
5525
|
-
function compileLinkable(ruleMapArg, ns) {
|
|
5641
|
+
function compileLinkable(ruleMapArg, ns, opts) {
|
|
5526
5642
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
5527
5643
|
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
5644
|
+
const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
|
|
5528
5645
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
5529
5646
|
const d = val._def;
|
|
5530
5647
|
if (d.tag !== "lazy") return true;
|
|
@@ -5555,7 +5672,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
5555
5672
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
5556
5673
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
5557
5674
|
ns,
|
|
5558
|
-
deferFirstSetRefs: true
|
|
5675
|
+
deferFirstSetRefs: true,
|
|
5676
|
+
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
|
|
5559
5677
|
};
|
|
5560
5678
|
const seen = /* @__PURE__ */ new Map();
|
|
5561
5679
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
@@ -5618,7 +5736,10 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
5618
5736
|
ctx.indent = 1;
|
|
5619
5737
|
ctx.failLabel = "_pfail";
|
|
5620
5738
|
ctx.recordFail = true;
|
|
5739
|
+
const savedTrivia = ctx.activeTrivia;
|
|
5740
|
+
if (rule._meta.isTrivia) ctx.activeTrivia = void 0;
|
|
5621
5741
|
const body = emit(resolved, ctx, "_pos");
|
|
5742
|
+
ctx.activeTrivia = savedTrivia;
|
|
5622
5743
|
ctx.indent = savedIndent;
|
|
5623
5744
|
ctx.failLabel = savedFail;
|
|
5624
5745
|
ctx.recordFail = savedRec;
|
|
@@ -5817,6 +5938,64 @@ function escapeClassChar(ch) {
|
|
|
5817
5938
|
}
|
|
5818
5939
|
|
|
5819
5940
|
// src/compiler/ir-serialize.ts
|
|
5941
|
+
function childrenOf2(def) {
|
|
5942
|
+
switch (def.tag) {
|
|
5943
|
+
case "sequence":
|
|
5944
|
+
case "choice":
|
|
5945
|
+
return def.parsers;
|
|
5946
|
+
case "many":
|
|
5947
|
+
case "oneOrMore":
|
|
5948
|
+
case "optional":
|
|
5949
|
+
case "transform":
|
|
5950
|
+
case "trivia":
|
|
5951
|
+
case "token":
|
|
5952
|
+
case "label":
|
|
5953
|
+
case "field":
|
|
5954
|
+
case "not":
|
|
5955
|
+
case "node":
|
|
5956
|
+
case "expect":
|
|
5957
|
+
return [def.parser];
|
|
5958
|
+
case "grammar":
|
|
5959
|
+
return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
|
|
5960
|
+
case "sepBy":
|
|
5961
|
+
return [def.parser, def.separator];
|
|
5962
|
+
case "skip":
|
|
5963
|
+
return [def.main, def.skipped];
|
|
5964
|
+
case "scanTo":
|
|
5965
|
+
return [def.sentinel, ...def.skip];
|
|
5966
|
+
case "lazy":
|
|
5967
|
+
case "literal":
|
|
5968
|
+
case "regex":
|
|
5969
|
+
case "keywords":
|
|
5970
|
+
case "guard":
|
|
5971
|
+
case "withCtx":
|
|
5972
|
+
case "recover":
|
|
5973
|
+
case "unknown":
|
|
5974
|
+
return [];
|
|
5975
|
+
}
|
|
5976
|
+
}
|
|
5977
|
+
var ruleNameOf = (c) => c._ruleName;
|
|
5978
|
+
function lazyTarget(c) {
|
|
5979
|
+
if (c._def.tag !== "lazy") return null;
|
|
5980
|
+
try {
|
|
5981
|
+
return c._def.thunk();
|
|
5982
|
+
} catch {
|
|
5983
|
+
return null;
|
|
5984
|
+
}
|
|
5985
|
+
}
|
|
5986
|
+
var Unserializable = class extends Error {
|
|
5987
|
+
};
|
|
5988
|
+
function serializeRuleMap(ruleMap) {
|
|
5989
|
+
try {
|
|
5990
|
+
return new Serializer(ruleMap).run();
|
|
5991
|
+
} catch (e) {
|
|
5992
|
+
if (e instanceof Unserializable) {
|
|
5993
|
+
if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
|
|
5994
|
+
return null;
|
|
5995
|
+
}
|
|
5996
|
+
throw e;
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5820
5999
|
function evalRuleMapIR(ir) {
|
|
5821
6000
|
const _tf = (child, src) => {
|
|
5822
6001
|
let fn2;
|
|
@@ -5891,11 +6070,172 @@ function evalRuleMapIR(ir) {
|
|
|
5891
6070
|
);
|
|
5892
6071
|
return Object.entries(map);
|
|
5893
6072
|
}
|
|
6073
|
+
var Serializer = class {
|
|
6074
|
+
counts = /* @__PURE__ */ new Map();
|
|
6075
|
+
selfRef = /* @__PURE__ */ new Set();
|
|
6076
|
+
// combinators reached by an unnamed lazy pointing back into their own subtree
|
|
6077
|
+
constName = /* @__PURE__ */ new Map();
|
|
6078
|
+
emitted = /* @__PURE__ */ new Set();
|
|
6079
|
+
decls = [];
|
|
6080
|
+
ruleValues = /* @__PURE__ */ new Set();
|
|
6081
|
+
ruleMap;
|
|
6082
|
+
constructor(ruleMap) {
|
|
6083
|
+
this.ruleMap = ruleMap;
|
|
6084
|
+
for (const [, c] of ruleMap) this.ruleValues.add(this.body(c));
|
|
6085
|
+
}
|
|
6086
|
+
/** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
|
|
6087
|
+
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
6088
|
+
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
6089
|
+
body(c) {
|
|
6090
|
+
return c._def.tag === "lazy" && ruleNameOf(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
6091
|
+
}
|
|
6092
|
+
run() {
|
|
6093
|
+
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
6094
|
+
let n = 0;
|
|
6095
|
+
for (const [c, count] of this.counts) {
|
|
6096
|
+
if (this.ruleValues.has(c)) continue;
|
|
6097
|
+
if (count >= 2 || this.selfRef.has(c)) this.constName.set(c, `_s${n++}`);
|
|
6098
|
+
}
|
|
6099
|
+
const body = this.ruleMap.map(([name, c]) => ` ${JSON.stringify(name)}: ${this.wrap(this.body(c))}`).join(",\n");
|
|
6100
|
+
if (this.decls.length === 0) return `rules((g) => ({
|
|
6101
|
+
${body}
|
|
6102
|
+
}))`;
|
|
6103
|
+
return `rules((g) => {
|
|
6104
|
+
${this.decls.map((d) => " " + d).join("\n")}
|
|
6105
|
+
return ({
|
|
6106
|
+
${body}
|
|
6107
|
+
})
|
|
6108
|
+
})`;
|
|
6109
|
+
}
|
|
6110
|
+
/** Count identity references and flag self-referential subtrees. `active` is the
|
|
6111
|
+
* set of combinators on the current DFS path (to detect a lazy pointing back). */
|
|
6112
|
+
analyze(c, active) {
|
|
6113
|
+
const def = c._def;
|
|
6114
|
+
if (def.tag === "lazy") {
|
|
6115
|
+
if (ruleNameOf(c) !== void 0) return;
|
|
6116
|
+
const target = lazyTarget(c);
|
|
6117
|
+
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
6118
|
+
if (active.has(target)) {
|
|
6119
|
+
this.selfRef.add(target);
|
|
6120
|
+
return;
|
|
6121
|
+
}
|
|
6122
|
+
this.analyze(target, active);
|
|
6123
|
+
return;
|
|
6124
|
+
}
|
|
6125
|
+
if (def.tag === "guard" || def.tag === "withCtx" || def.tag === "recover" || def.tag === "unknown") {
|
|
6126
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
6127
|
+
}
|
|
6128
|
+
this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
|
|
6129
|
+
if ((this.counts.get(c) ?? 0) > 1) return;
|
|
6130
|
+
active.add(c);
|
|
6131
|
+
for (const child of childrenOf2(def)) this.analyze(child, active);
|
|
6132
|
+
active.delete(c);
|
|
6133
|
+
}
|
|
6134
|
+
/** Reference `c`: a const var if it has one (emitting its decl first), else inline.
|
|
6135
|
+
* `selfOf` is the const currently being wrapped in a ref()/define closure — an
|
|
6136
|
+
* unnamed lazy back into it emits the local ref var instead of recursing. */
|
|
6137
|
+
ref(c, selfOf) {
|
|
6138
|
+
const cn = this.constName.get(c);
|
|
6139
|
+
if (cn) {
|
|
6140
|
+
this.emitDecl(c);
|
|
6141
|
+
return cn;
|
|
6142
|
+
}
|
|
6143
|
+
return this.expr(c, selfOf);
|
|
6144
|
+
}
|
|
6145
|
+
emitDecl(c) {
|
|
6146
|
+
const name = this.constName.get(c);
|
|
6147
|
+
if (this.emitted.has(name)) return;
|
|
6148
|
+
this.emitted.add(name);
|
|
6149
|
+
this.decls.push(`const ${name} = ${this.wrap(c)}`);
|
|
6150
|
+
}
|
|
6151
|
+
selfN = 0;
|
|
6152
|
+
/** Emit `c`'s own constructor call, wrapping it in a `ref()`+`.define()` closure
|
|
6153
|
+
* if it is self-referential (an unnamed lazy in its subtree points back to it) —
|
|
6154
|
+
* the shape `balanced()` and other recursive library combinators build. */
|
|
6155
|
+
wrap(c) {
|
|
6156
|
+
if (!this.selfRef.has(c)) return this.expr(c, null);
|
|
6157
|
+
const rv = `_rr${this.selfN++}`;
|
|
6158
|
+
const bodyExpr = this.expr(c, { comb: c, var: rv });
|
|
6159
|
+
return `(() => { const ${rv} = ref(); const _b = ${bodyExpr}; ${rv}.define(_b); return _b })()`;
|
|
6160
|
+
}
|
|
6161
|
+
/** Emit the constructor-call source for `c` (never a const shortcut for `c` itself). */
|
|
6162
|
+
expr(c, selfOf) {
|
|
6163
|
+
const def = c._def;
|
|
6164
|
+
const kid = (x) => this.ref(x, selfOf);
|
|
6165
|
+
switch (def.tag) {
|
|
6166
|
+
case "lazy": {
|
|
6167
|
+
if (ruleNameOf(c) !== void 0) return `g[${JSON.stringify(ruleNameOf(c))}]`;
|
|
6168
|
+
const target = lazyTarget(c);
|
|
6169
|
+
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
6170
|
+
return this.ref(target, selfOf);
|
|
6171
|
+
}
|
|
6172
|
+
case "literal":
|
|
6173
|
+
return `literal(${JSON.stringify(def.value)}${def.caseInsensitive ? ", { caseInsensitive: true }" : ""})`;
|
|
6174
|
+
case "regex":
|
|
6175
|
+
return `regex(${JSON.stringify(def.source)}, ${JSON.stringify(def.flags)})`;
|
|
6176
|
+
case "keywords":
|
|
6177
|
+
return `keywords(${JSON.stringify(def.words)}, { caseInsensitive: ${def.caseInsensitive}${def.boundary !== void 0 ? `, boundary: ${JSON.stringify(def.boundary)}` : ""} })`;
|
|
6178
|
+
case "sequence":
|
|
6179
|
+
return `sequence(${def.parsers.map(kid).join(", ")})`;
|
|
6180
|
+
case "choice": {
|
|
6181
|
+
if (def.gates.some((g) => g !== null)) throw new Unserializable("choice with a gate() has no source");
|
|
6182
|
+
return `choice(${def.parsers.map(kid).join(", ")})`;
|
|
6183
|
+
}
|
|
6184
|
+
case "many":
|
|
6185
|
+
return `many(${kid(def.parser)})`;
|
|
6186
|
+
case "oneOrMore":
|
|
6187
|
+
return `oneOrMore(${kid(def.parser)})`;
|
|
6188
|
+
case "optional":
|
|
6189
|
+
return `optional(${kid(def.parser)})`;
|
|
6190
|
+
case "sepBy":
|
|
6191
|
+
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
6192
|
+
case "not":
|
|
6193
|
+
return `not(${kid(def.parser)})`;
|
|
6194
|
+
case "trivia":
|
|
6195
|
+
return `trivia(${kid(def.parser)})`;
|
|
6196
|
+
case "token":
|
|
6197
|
+
return `token(${kid(def.parser)})`;
|
|
6198
|
+
case "label":
|
|
6199
|
+
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
6200
|
+
case "field":
|
|
6201
|
+
return `field(${JSON.stringify(def.name)}, ${kid(def.parser)})`;
|
|
6202
|
+
case "expect":
|
|
6203
|
+
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
6204
|
+
case "skip":
|
|
6205
|
+
return `skip(${kid(def.main)}, ${kid(def.skipped)})`;
|
|
6206
|
+
case "scanTo":
|
|
6207
|
+
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
6208
|
+
case "transform": {
|
|
6209
|
+
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
6210
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
6211
|
+
}
|
|
6212
|
+
case "node": {
|
|
6213
|
+
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
6214
|
+
const opts = def.unwrap || def.collapse ? `, { ${def.unwrap ? "unwrap: true" : "collapse: true"} }` : "";
|
|
6215
|
+
if (def.type === void 0) {
|
|
6216
|
+
if (def.buildSrc !== void 0) throw new Unserializable("inferred node build without inferred type");
|
|
6217
|
+
return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
|
|
6218
|
+
}
|
|
6219
|
+
if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
|
|
6220
|
+
return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
|
|
6221
|
+
}
|
|
6222
|
+
case "grammar": {
|
|
6223
|
+
const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
|
|
6224
|
+
return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
|
|
6225
|
+
}
|
|
6226
|
+
case "guard":
|
|
6227
|
+
case "withCtx":
|
|
6228
|
+
case "recover":
|
|
6229
|
+
case "unknown":
|
|
6230
|
+
throw new Unserializable(`unsupported tag ${def.tag}`);
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
};
|
|
5894
6234
|
|
|
5895
6235
|
// src/compiler/linker.ts
|
|
5896
6236
|
var _nsCounter = 0;
|
|
5897
|
-
function linkable(rulesMap, ns) {
|
|
5898
|
-
const pieces = compileLinkable(
|
|
6237
|
+
function linkable(rulesMap, ns, trivia2) {
|
|
6238
|
+
const pieces = compileLinkable(Object.entries(rulesMap), ns ?? `_lk${_nsCounter++}_`, trivia2 ? { trivia: trivia2 } : void 0);
|
|
5899
6239
|
if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
|
|
5900
6240
|
return pieces;
|
|
5901
6241
|
}
|
|
@@ -5919,28 +6259,6 @@ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, trivi
|
|
|
5919
6259
|
if (collapse) host._parsemanCstCollapse = collapse;
|
|
5920
6260
|
return host;
|
|
5921
6261
|
}
|
|
5922
|
-
function pick(grammar, names) {
|
|
5923
|
-
const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
|
|
5924
|
-
const keep = /* @__PURE__ */ new Set();
|
|
5925
|
-
const has = new Set(p.keys);
|
|
5926
|
-
for (const n of names) {
|
|
5927
|
-
if (!has.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${p.keys.join(", ")})`);
|
|
5928
|
-
}
|
|
5929
|
-
const visit = (n) => {
|
|
5930
|
-
if (keep.has(n) || !has.has(n)) return;
|
|
5931
|
-
keep.add(n);
|
|
5932
|
-
for (const d of p.deps.get(n) ?? []) visit(d);
|
|
5933
|
-
};
|
|
5934
|
-
for (const n of names) visit(n);
|
|
5935
|
-
const filt = (m) => new Map([...m].filter(([k]) => keep.has(k)));
|
|
5936
|
-
return {
|
|
5937
|
-
...p,
|
|
5938
|
-
keys: [...keep],
|
|
5939
|
-
ruleFns: filt(p.ruleFns),
|
|
5940
|
-
wrappers: filt(p.wrappers),
|
|
5941
|
-
deps: filt(p.deps)
|
|
5942
|
-
};
|
|
5943
|
-
}
|
|
5944
6262
|
function fusedBody(pieces) {
|
|
5945
6263
|
const winner = /* @__PURE__ */ new Map();
|
|
5946
6264
|
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
@@ -5960,7 +6278,7 @@ function fusedBody(pieces) {
|
|
|
5960
6278
|
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
5961
6279
|
...needsHostReads ? [HOST_READS_DECL] : [],
|
|
5962
6280
|
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
5963
|
-
...
|
|
6281
|
+
...new Set(contributingPieces.flatMap((p) => p.prelude)),
|
|
5964
6282
|
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
5965
6283
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
5966
6284
|
];
|
|
@@ -5994,12 +6312,13 @@ function fuseRules(pieces) {
|
|
|
5994
6312
|
return new Function("_env", body)(env);
|
|
5995
6313
|
}
|
|
5996
6314
|
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
6315
|
+
var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
|
|
5997
6316
|
function isIRPiece(p) {
|
|
5998
6317
|
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
5999
6318
|
}
|
|
6000
|
-
function materializePiece(p) {
|
|
6319
|
+
function materializePiece(p, trivia2) {
|
|
6001
6320
|
if (!isIRPiece(p)) return p;
|
|
6002
|
-
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
|
|
6321
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
|
|
6003
6322
|
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
6004
6323
|
return pieces;
|
|
6005
6324
|
}
|
|
@@ -6011,17 +6330,52 @@ function nextComposeNs(used) {
|
|
|
6011
6330
|
used.add(ns);
|
|
6012
6331
|
return ns;
|
|
6013
6332
|
}
|
|
6014
|
-
function
|
|
6333
|
+
function itemCarried(item, used, trivia2) {
|
|
6015
6334
|
const carried = item[COMPOSED_PIECES];
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6335
|
+
if (Array.isArray(carried)) {
|
|
6336
|
+
const items = carried;
|
|
6337
|
+
for (const p of items) used.add(p.ns);
|
|
6338
|
+
return items;
|
|
6339
|
+
}
|
|
6340
|
+
if (item.ruleFns instanceof Map) {
|
|
6341
|
+
const p = item;
|
|
6342
|
+
used.add(p.ns);
|
|
6343
|
+
return [p];
|
|
6344
|
+
}
|
|
6345
|
+
const map = item;
|
|
6346
|
+
const ns = nextComposeNs(used);
|
|
6347
|
+
const entries = Object.entries(map).filter(([, val]) => {
|
|
6348
|
+
const d = val._def;
|
|
6349
|
+
if (d.tag !== "lazy") return true;
|
|
6350
|
+
try {
|
|
6351
|
+
d.thunk();
|
|
6352
|
+
return true;
|
|
6353
|
+
} catch {
|
|
6354
|
+
return false;
|
|
6355
|
+
}
|
|
6356
|
+
});
|
|
6357
|
+
const ir = serializeRuleMap(entries);
|
|
6358
|
+
return ir ? [{ ns, ir }] : [linkable(map, ns, trivia2)];
|
|
6359
|
+
}
|
|
6360
|
+
function composingTriviaOf(items) {
|
|
6361
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
6362
|
+
const item = items[i];
|
|
6363
|
+
if (!item || item.ruleFns instanceof Map || item[COMPOSED_PIECES]) continue;
|
|
6364
|
+
for (const v2 of Object.values(item)) {
|
|
6365
|
+
const t = v2?._meta?.grammarTrivia;
|
|
6366
|
+
if (t) return t;
|
|
6367
|
+
}
|
|
6368
|
+
}
|
|
6369
|
+
return void 0;
|
|
6019
6370
|
}
|
|
6020
6371
|
function compose(items) {
|
|
6021
6372
|
const used = /* @__PURE__ */ new Set();
|
|
6022
|
-
const
|
|
6373
|
+
const trivia2 = composingTriviaOf(items);
|
|
6374
|
+
const carried = items.flatMap((item) => itemCarried(item, used, trivia2));
|
|
6375
|
+
const pieces = carried.map((p) => materializePiece(p, trivia2));
|
|
6023
6376
|
const map = fuseRules(pieces);
|
|
6024
|
-
Object.defineProperty(map, COMPOSED_PIECES, { value:
|
|
6377
|
+
Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
|
|
6378
|
+
if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
|
|
6025
6379
|
return map;
|
|
6026
6380
|
}
|
|
6027
6381
|
|