parseman 0.14.0 → 0.15.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/README.md +41 -2
- package/dist/combinators/first-set.d.ts +18 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +8 -30
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts +24 -0
- package/dist/compiler/value-usage.d.ts.map +1 -0
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/index.cjs +232 -28
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +232 -28
- package/dist/index.js.map +4 -4
- package/dist/plugin/index.cjs +223 -30
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.js +223 -30
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27,6 +27,63 @@ function any() {
|
|
|
27
27
|
function empty() {
|
|
28
28
|
return { kind: "empty" };
|
|
29
29
|
}
|
|
30
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
31
|
+
if (seen.has(p)) return true;
|
|
32
|
+
seen.add(p);
|
|
33
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
34
|
+
const d = p._def;
|
|
35
|
+
switch (d.tag) {
|
|
36
|
+
case "literal":
|
|
37
|
+
return d.value.length === 0;
|
|
38
|
+
case "keywords":
|
|
39
|
+
return false;
|
|
40
|
+
case "regex":
|
|
41
|
+
try {
|
|
42
|
+
const m = new RegExp(d.source).exec("");
|
|
43
|
+
return m != null && m[0] === "";
|
|
44
|
+
} catch {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
case "many":
|
|
48
|
+
case "optional":
|
|
49
|
+
case "not":
|
|
50
|
+
return true;
|
|
51
|
+
// zero repetitions / absent / lookahead
|
|
52
|
+
case "oneOrMore":
|
|
53
|
+
return me(d.parser);
|
|
54
|
+
case "sequence":
|
|
55
|
+
return d.parsers.every(me);
|
|
56
|
+
case "choice":
|
|
57
|
+
return d.parsers.some(me);
|
|
58
|
+
case "transform":
|
|
59
|
+
case "label":
|
|
60
|
+
case "trivia":
|
|
61
|
+
case "expect":
|
|
62
|
+
case "withCtx":
|
|
63
|
+
case "node":
|
|
64
|
+
case "grammar":
|
|
65
|
+
case "recover":
|
|
66
|
+
return me(d.parser);
|
|
67
|
+
case "skip":
|
|
68
|
+
return me(d.main);
|
|
69
|
+
case "lazy":
|
|
70
|
+
try {
|
|
71
|
+
return me(d.thunk());
|
|
72
|
+
} catch {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
default:
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function sequenceFirstSet(parsers) {
|
|
80
|
+
let fs = empty();
|
|
81
|
+
for (const p of parsers) {
|
|
82
|
+
fs = union(fs, p._meta.firstSet);
|
|
83
|
+
if (!matchesEmpty(p)) return fs;
|
|
84
|
+
}
|
|
85
|
+
return fs;
|
|
86
|
+
}
|
|
30
87
|
function mergeRanges(ranges) {
|
|
31
88
|
if (ranges.length === 0) return [];
|
|
32
89
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -848,16 +905,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
848
905
|
// src/combinators/sequence.ts
|
|
849
906
|
function sequence(...parsers) {
|
|
850
907
|
const meta = {
|
|
851
|
-
|
|
908
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
909
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
910
|
+
firstSet: sequenceFirstSet(parsers),
|
|
852
911
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
853
912
|
isTrivia: false
|
|
854
913
|
};
|
|
914
|
+
const def = { tag: "sequence", parsers };
|
|
855
915
|
return {
|
|
856
916
|
_tag: "sequence",
|
|
857
917
|
_meta: meta,
|
|
858
|
-
_def:
|
|
918
|
+
_def: def,
|
|
859
919
|
parse(input, pos, ctx) {
|
|
860
|
-
const values = [];
|
|
920
|
+
const values = def.valueUnused ? void 0 : [];
|
|
861
921
|
let cur = pos;
|
|
862
922
|
for (let i = 0; i < parsers.length; i++) {
|
|
863
923
|
if (ctx.trivia && i > 0) {
|
|
@@ -877,17 +937,18 @@ function sequence(...parsers) {
|
|
|
877
937
|
} else {
|
|
878
938
|
rollbackTrivia(ctx, mark);
|
|
879
939
|
}
|
|
880
|
-
values.push(result2.value);
|
|
940
|
+
if (values !== void 0) values.push(result2.value);
|
|
881
941
|
continue;
|
|
882
942
|
}
|
|
883
943
|
const result = parsers[i].parse(input, cur, ctx);
|
|
884
944
|
if (!result.ok) return result;
|
|
885
|
-
values.push(result.value);
|
|
945
|
+
if (values !== void 0) values.push(result.value);
|
|
886
946
|
cur = result.span.end;
|
|
887
947
|
}
|
|
888
948
|
return {
|
|
889
949
|
ok: true,
|
|
890
|
-
|
|
950
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
951
|
+
value: values ?? void 0,
|
|
891
952
|
span: { start: pos, end: cur }
|
|
892
953
|
};
|
|
893
954
|
}
|
|
@@ -928,20 +989,21 @@ function many(combinator) {
|
|
|
928
989
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
929
990
|
isTrivia: false
|
|
930
991
|
};
|
|
992
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
931
993
|
return {
|
|
932
994
|
_tag: "many",
|
|
933
995
|
_meta: meta,
|
|
934
|
-
_def:
|
|
996
|
+
_def: def,
|
|
935
997
|
parse(input, pos, ctx) {
|
|
936
|
-
const values = [];
|
|
998
|
+
const values = def.valueUnused ? void 0 : [];
|
|
937
999
|
let cur = pos;
|
|
938
1000
|
while (cur < input.length) {
|
|
939
1001
|
const item = repItem(combinator, input, cur, ctx);
|
|
940
1002
|
if (item === "stop" || "fail" in item) break;
|
|
941
|
-
values.push(item.value);
|
|
1003
|
+
if (values !== void 0) values.push(item.value);
|
|
942
1004
|
cur = item.end;
|
|
943
1005
|
}
|
|
944
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1006
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
945
1007
|
}
|
|
946
1008
|
};
|
|
947
1009
|
}
|
|
@@ -951,22 +1013,23 @@ function oneOrMore(combinator) {
|
|
|
951
1013
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
952
1014
|
isTrivia: false
|
|
953
1015
|
};
|
|
1016
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
954
1017
|
return {
|
|
955
1018
|
_tag: "oneOrMore",
|
|
956
1019
|
_meta: meta,
|
|
957
|
-
_def:
|
|
1020
|
+
_def: def,
|
|
958
1021
|
parse(input, pos, ctx) {
|
|
959
1022
|
const first = combinator.parse(input, pos, ctx);
|
|
960
1023
|
if (!first.ok) return first;
|
|
961
|
-
const values = [first.value];
|
|
1024
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
962
1025
|
let cur = first.span.end;
|
|
963
1026
|
while (cur < input.length) {
|
|
964
1027
|
const item = repItem(combinator, input, cur, ctx);
|
|
965
1028
|
if (item === "stop" || "fail" in item) break;
|
|
966
|
-
values.push(item.value);
|
|
1029
|
+
if (values !== void 0) values.push(item.value);
|
|
967
1030
|
cur = item.end;
|
|
968
1031
|
}
|
|
969
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1032
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
970
1033
|
}
|
|
971
1034
|
};
|
|
972
1035
|
}
|
|
@@ -1074,6 +1137,90 @@ function ref() {
|
|
|
1074
1137
|
return slot;
|
|
1075
1138
|
}
|
|
1076
1139
|
|
|
1140
|
+
// src/compiler/value-usage.ts
|
|
1141
|
+
function markUnusedValues(root) {
|
|
1142
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1143
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1144
|
+
const mark = (def, consumed) => {
|
|
1145
|
+
if (consumed) def.valueUnused = false;
|
|
1146
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1147
|
+
};
|
|
1148
|
+
const visit = (c, consumed) => {
|
|
1149
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1150
|
+
if (seen.has(c)) return;
|
|
1151
|
+
seen.add(c);
|
|
1152
|
+
const def = c._def;
|
|
1153
|
+
switch (def.tag) {
|
|
1154
|
+
case "many":
|
|
1155
|
+
case "oneOrMore":
|
|
1156
|
+
mark(def, consumed);
|
|
1157
|
+
visit(def.parser, consumed);
|
|
1158
|
+
return;
|
|
1159
|
+
case "optional":
|
|
1160
|
+
visit(def.parser, consumed);
|
|
1161
|
+
return;
|
|
1162
|
+
case "sequence":
|
|
1163
|
+
mark(def, consumed);
|
|
1164
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1165
|
+
return;
|
|
1166
|
+
case "choice":
|
|
1167
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1168
|
+
return;
|
|
1169
|
+
case "node":
|
|
1170
|
+
visit(def.parser, false);
|
|
1171
|
+
return;
|
|
1172
|
+
case "transform":
|
|
1173
|
+
visit(def.parser, true);
|
|
1174
|
+
return;
|
|
1175
|
+
case "skip":
|
|
1176
|
+
visit(def.main, consumed);
|
|
1177
|
+
visit(def.skipped, false);
|
|
1178
|
+
return;
|
|
1179
|
+
case "sepBy":
|
|
1180
|
+
visit(def.parser, true);
|
|
1181
|
+
visit(def.separator, false);
|
|
1182
|
+
return;
|
|
1183
|
+
case "not":
|
|
1184
|
+
visit(def.parser, false);
|
|
1185
|
+
return;
|
|
1186
|
+
case "grammar":
|
|
1187
|
+
visit(def.parser, consumed);
|
|
1188
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1189
|
+
return;
|
|
1190
|
+
case "recover":
|
|
1191
|
+
visit(def.parser, consumed);
|
|
1192
|
+
visit(def.sentinel, false);
|
|
1193
|
+
return;
|
|
1194
|
+
case "label":
|
|
1195
|
+
case "trivia":
|
|
1196
|
+
case "withCtx":
|
|
1197
|
+
case "expect":
|
|
1198
|
+
visit(def.parser, consumed);
|
|
1199
|
+
return;
|
|
1200
|
+
case "lazy":
|
|
1201
|
+
return;
|
|
1202
|
+
default:
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
const resolveRoot = (c) => {
|
|
1207
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1208
|
+
let cur = c;
|
|
1209
|
+
while (!guard2.has(cur)) {
|
|
1210
|
+
const d = cur._def;
|
|
1211
|
+
if (d.tag !== "lazy") return cur;
|
|
1212
|
+
guard2.add(cur);
|
|
1213
|
+
try {
|
|
1214
|
+
cur = d.thunk();
|
|
1215
|
+
} catch {
|
|
1216
|
+
return c;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
return cur;
|
|
1220
|
+
};
|
|
1221
|
+
visit(resolveRoot(root), true);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1077
1224
|
// src/combinators/parser.ts
|
|
1078
1225
|
function rules(factory) {
|
|
1079
1226
|
const cache = {};
|
|
@@ -1103,6 +1250,9 @@ function rules(factory) {
|
|
|
1103
1250
|
cache[key] = parser2;
|
|
1104
1251
|
}
|
|
1105
1252
|
}
|
|
1253
|
+
for (const key of Object.keys(definitions)) {
|
|
1254
|
+
markUnusedValues(cache[key]);
|
|
1255
|
+
}
|
|
1106
1256
|
return cache;
|
|
1107
1257
|
}
|
|
1108
1258
|
|
|
@@ -1319,7 +1469,12 @@ function parser(opts, root) {
|
|
|
1319
1469
|
function noTrivia(root) {
|
|
1320
1470
|
return parser({ trivia: null }, root);
|
|
1321
1471
|
}
|
|
1472
|
+
var _analyzed = /* @__PURE__ */ new WeakSet();
|
|
1322
1473
|
function parse(combinator, input, opts = {}) {
|
|
1474
|
+
if (!_analyzed.has(combinator)) {
|
|
1475
|
+
_analyzed.add(combinator);
|
|
1476
|
+
markUnusedValues(combinator);
|
|
1477
|
+
}
|
|
1323
1478
|
const trackLines = opts.trackLines ?? false;
|
|
1324
1479
|
const _errors = opts.recover ? [] : void 0;
|
|
1325
1480
|
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
@@ -3276,6 +3431,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3276
3431
|
}
|
|
3277
3432
|
function emitSeq(def, ctx, pos) {
|
|
3278
3433
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3434
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3279
3435
|
const arrV = v(ctx, "_arr");
|
|
3280
3436
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3281
3437
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3482,7 +3638,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3482
3638
|
const autoNot = def.autoNot[i];
|
|
3483
3639
|
const atStart = failsAtStart(p);
|
|
3484
3640
|
const staticFx = armStaticExpected(ctx, p);
|
|
3485
|
-
const
|
|
3641
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3642
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3486
3643
|
let gateCond = null;
|
|
3487
3644
|
if (gate) {
|
|
3488
3645
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3573,19 +3730,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3573
3730
|
return checks.join(" && ");
|
|
3574
3731
|
}
|
|
3575
3732
|
function emitMany(def, ctx, pos) {
|
|
3733
|
+
const wantValue = !def.valueUnused;
|
|
3576
3734
|
const arrV = v(ctx, "_arr");
|
|
3577
3735
|
const curV = v(ctx, "_cur");
|
|
3578
3736
|
const stmts = [
|
|
3579
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3737
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3580
3738
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3581
3739
|
];
|
|
3582
3740
|
if (def.min === 1) {
|
|
3583
3741
|
const firstR = emit(def.parser, ctx, curV);
|
|
3584
3742
|
stmts.push(...firstR.stmts);
|
|
3585
|
-
stmts.push(
|
|
3586
|
-
|
|
3587
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3588
|
-
);
|
|
3743
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3744
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3589
3745
|
}
|
|
3590
3746
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3591
3747
|
ctx.indent++;
|
|
@@ -3616,13 +3772,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3616
3772
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3617
3773
|
stmts.push(
|
|
3618
3774
|
...iterStmts,
|
|
3619
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3620
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3621
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3775
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3622
3776
|
);
|
|
3777
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3778
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3623
3779
|
ctx.indent--;
|
|
3624
3780
|
stmts.push(`${ind(ctx)}}`);
|
|
3625
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3781
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3626
3782
|
}
|
|
3627
3783
|
function emitOptional(def, ctx, pos) {
|
|
3628
3784
|
const valV = v(ctx, "_opt");
|
|
@@ -4232,6 +4388,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4232
4388
|
return deps;
|
|
4233
4389
|
}
|
|
4234
4390
|
function compile(combinator, mapFnSources) {
|
|
4391
|
+
markUnusedValues(combinator);
|
|
4235
4392
|
const ctx = {
|
|
4236
4393
|
vars: 0,
|
|
4237
4394
|
indent: 1,
|
|
@@ -4314,8 +4471,17 @@ function compile(combinator, mapFnSources) {
|
|
|
4314
4471
|
}
|
|
4315
4472
|
};
|
|
4316
4473
|
}
|
|
4474
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4475
|
+
function assertRuleName(name) {
|
|
4476
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4477
|
+
throw new Error(
|
|
4478
|
+
`parseman: grammar rule name ${JSON.stringify(name)} is not a valid JS identifier. Rule names compile to _r_<Name> functions and dispatch guards \u2014 use letters, digits, _ or $ (and don't start with a digit).`
|
|
4479
|
+
);
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4317
4482
|
function compileLinkable(ruleMapArg, ns) {
|
|
4318
4483
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4484
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4319
4485
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4320
4486
|
const d = val._def;
|
|
4321
4487
|
if (d.tag !== "lazy") return true;
|
|
@@ -4346,13 +4512,15 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4346
4512
|
namedFnDecls: [],
|
|
4347
4513
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4348
4514
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4349
|
-
ns
|
|
4515
|
+
ns,
|
|
4516
|
+
deferFirstSetRefs: true
|
|
4350
4517
|
};
|
|
4351
4518
|
const seen = /* @__PURE__ */ new Map();
|
|
4352
4519
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4353
4520
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4354
4521
|
for (const [key, rule] of ruleMap) {
|
|
4355
|
-
|
|
4522
|
+
assertRuleName(key);
|
|
4523
|
+
const base = `_r_${key}`;
|
|
4356
4524
|
const n = seen.get(base) ?? 0;
|
|
4357
4525
|
seen.set(base, n + 1);
|
|
4358
4526
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4376,7 +4544,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4376
4544
|
if (resolved === void 0) {
|
|
4377
4545
|
const name = p._ruleName;
|
|
4378
4546
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4379
|
-
|
|
4547
|
+
assertRuleName(name);
|
|
4548
|
+
const fn = `_r_${name}`;
|
|
4380
4549
|
ruleNames.set(p, fn);
|
|
4381
4550
|
ctx.namedParsers.set(p, fn);
|
|
4382
4551
|
}
|
|
@@ -4445,12 +4614,26 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4445
4614
|
`}`
|
|
4446
4615
|
].join("\n"));
|
|
4447
4616
|
}
|
|
4617
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4618
|
+
for (const [key, rule] of ruleMap) {
|
|
4619
|
+
let resolved = rule;
|
|
4620
|
+
const d = rule._def;
|
|
4621
|
+
if (d.tag === "lazy") {
|
|
4622
|
+
try {
|
|
4623
|
+
resolved = d.thunk();
|
|
4624
|
+
} catch {
|
|
4625
|
+
resolved = rule;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4629
|
+
}
|
|
4448
4630
|
return {
|
|
4449
4631
|
ns,
|
|
4450
4632
|
keys: perEntry.map((e) => e.key),
|
|
4451
4633
|
prelude,
|
|
4452
4634
|
ruleFns,
|
|
4453
4635
|
wrappers,
|
|
4636
|
+
firstSets,
|
|
4454
4637
|
deps: ruleDependencies(ruleMap),
|
|
4455
4638
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4456
4639
|
needsCollator: ctx.needsCollator,
|
|
@@ -4551,7 +4734,23 @@ function fusedBody(pieces) {
|
|
|
4551
4734
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4552
4735
|
];
|
|
4553
4736
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4554
|
-
const
|
|
4737
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4738
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4739
|
+
for (const [k, p] of winner) {
|
|
4740
|
+
const fs = p.firstSets?.get(k);
|
|
4741
|
+
if (fs) finalFS.set(k, fs);
|
|
4742
|
+
}
|
|
4743
|
+
const body = rawBody.replace(
|
|
4744
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4745
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4746
|
+
// matches every well-formed placeholder.
|
|
4747
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4748
|
+
(_m, name, codevar) => {
|
|
4749
|
+
const fs = finalFS.get(name);
|
|
4750
|
+
if (!fs || fs.kind === "any" || fs.kind === "empty") return "true";
|
|
4751
|
+
return `(${firstSetCond(codevar, fs)})`;
|
|
4752
|
+
}
|
|
4753
|
+
);
|
|
4555
4754
|
const env = {};
|
|
4556
4755
|
for (const p of contributing) {
|
|
4557
4756
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -4943,6 +5142,11 @@ function parseDoc(registry, rootRule, input, opts = {}) {
|
|
|
4943
5142
|
// src/functional/run.ts
|
|
4944
5143
|
var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
|
|
4945
5144
|
function run(entry, input, options = {}) {
|
|
5145
|
+
if (typeof entry !== "function" && typeof entry?.parse !== "function") {
|
|
5146
|
+
throw new TypeError(
|
|
5147
|
+
`run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
|
|
5148
|
+
);
|
|
5149
|
+
}
|
|
4946
5150
|
const triviaLog = [];
|
|
4947
5151
|
const errors = [];
|
|
4948
5152
|
const ctx = {
|