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.cjs
CHANGED
|
@@ -124,6 +124,63 @@ function any() {
|
|
|
124
124
|
function empty() {
|
|
125
125
|
return { kind: "empty" };
|
|
126
126
|
}
|
|
127
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
128
|
+
if (seen.has(p)) return true;
|
|
129
|
+
seen.add(p);
|
|
130
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
131
|
+
const d = p._def;
|
|
132
|
+
switch (d.tag) {
|
|
133
|
+
case "literal":
|
|
134
|
+
return d.value.length === 0;
|
|
135
|
+
case "keywords":
|
|
136
|
+
return false;
|
|
137
|
+
case "regex":
|
|
138
|
+
try {
|
|
139
|
+
const m = new RegExp(d.source).exec("");
|
|
140
|
+
return m != null && m[0] === "";
|
|
141
|
+
} catch {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
case "many":
|
|
145
|
+
case "optional":
|
|
146
|
+
case "not":
|
|
147
|
+
return true;
|
|
148
|
+
// zero repetitions / absent / lookahead
|
|
149
|
+
case "oneOrMore":
|
|
150
|
+
return me(d.parser);
|
|
151
|
+
case "sequence":
|
|
152
|
+
return d.parsers.every(me);
|
|
153
|
+
case "choice":
|
|
154
|
+
return d.parsers.some(me);
|
|
155
|
+
case "transform":
|
|
156
|
+
case "label":
|
|
157
|
+
case "trivia":
|
|
158
|
+
case "expect":
|
|
159
|
+
case "withCtx":
|
|
160
|
+
case "node":
|
|
161
|
+
case "grammar":
|
|
162
|
+
case "recover":
|
|
163
|
+
return me(d.parser);
|
|
164
|
+
case "skip":
|
|
165
|
+
return me(d.main);
|
|
166
|
+
case "lazy":
|
|
167
|
+
try {
|
|
168
|
+
return me(d.thunk());
|
|
169
|
+
} catch {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function sequenceFirstSet(parsers) {
|
|
177
|
+
let fs = empty();
|
|
178
|
+
for (const p of parsers) {
|
|
179
|
+
fs = union(fs, p._meta.firstSet);
|
|
180
|
+
if (!matchesEmpty(p)) return fs;
|
|
181
|
+
}
|
|
182
|
+
return fs;
|
|
183
|
+
}
|
|
127
184
|
function mergeRanges(ranges) {
|
|
128
185
|
if (ranges.length === 0) return [];
|
|
129
186
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -945,16 +1002,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
945
1002
|
// src/combinators/sequence.ts
|
|
946
1003
|
function sequence(...parsers) {
|
|
947
1004
|
const meta = {
|
|
948
|
-
|
|
1005
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
1006
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
1007
|
+
firstSet: sequenceFirstSet(parsers),
|
|
949
1008
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
950
1009
|
isTrivia: false
|
|
951
1010
|
};
|
|
1011
|
+
const def = { tag: "sequence", parsers };
|
|
952
1012
|
return {
|
|
953
1013
|
_tag: "sequence",
|
|
954
1014
|
_meta: meta,
|
|
955
|
-
_def:
|
|
1015
|
+
_def: def,
|
|
956
1016
|
parse(input, pos, ctx) {
|
|
957
|
-
const values = [];
|
|
1017
|
+
const values = def.valueUnused ? void 0 : [];
|
|
958
1018
|
let cur = pos;
|
|
959
1019
|
for (let i = 0; i < parsers.length; i++) {
|
|
960
1020
|
if (ctx.trivia && i > 0) {
|
|
@@ -974,17 +1034,18 @@ function sequence(...parsers) {
|
|
|
974
1034
|
} else {
|
|
975
1035
|
rollbackTrivia(ctx, mark);
|
|
976
1036
|
}
|
|
977
|
-
values.push(result2.value);
|
|
1037
|
+
if (values !== void 0) values.push(result2.value);
|
|
978
1038
|
continue;
|
|
979
1039
|
}
|
|
980
1040
|
const result = parsers[i].parse(input, cur, ctx);
|
|
981
1041
|
if (!result.ok) return result;
|
|
982
|
-
values.push(result.value);
|
|
1042
|
+
if (values !== void 0) values.push(result.value);
|
|
983
1043
|
cur = result.span.end;
|
|
984
1044
|
}
|
|
985
1045
|
return {
|
|
986
1046
|
ok: true,
|
|
987
|
-
|
|
1047
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
1048
|
+
value: values ?? void 0,
|
|
988
1049
|
span: { start: pos, end: cur }
|
|
989
1050
|
};
|
|
990
1051
|
}
|
|
@@ -1025,20 +1086,21 @@ function many(combinator) {
|
|
|
1025
1086
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1026
1087
|
isTrivia: false
|
|
1027
1088
|
};
|
|
1089
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
1028
1090
|
return {
|
|
1029
1091
|
_tag: "many",
|
|
1030
1092
|
_meta: meta,
|
|
1031
|
-
_def:
|
|
1093
|
+
_def: def,
|
|
1032
1094
|
parse(input, pos, ctx) {
|
|
1033
|
-
const values = [];
|
|
1095
|
+
const values = def.valueUnused ? void 0 : [];
|
|
1034
1096
|
let cur = pos;
|
|
1035
1097
|
while (cur < input.length) {
|
|
1036
1098
|
const item = repItem(combinator, input, cur, ctx);
|
|
1037
1099
|
if (item === "stop" || "fail" in item) break;
|
|
1038
|
-
values.push(item.value);
|
|
1100
|
+
if (values !== void 0) values.push(item.value);
|
|
1039
1101
|
cur = item.end;
|
|
1040
1102
|
}
|
|
1041
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1103
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1042
1104
|
}
|
|
1043
1105
|
};
|
|
1044
1106
|
}
|
|
@@ -1048,22 +1110,23 @@ function oneOrMore(combinator) {
|
|
|
1048
1110
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1049
1111
|
isTrivia: false
|
|
1050
1112
|
};
|
|
1113
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
1051
1114
|
return {
|
|
1052
1115
|
_tag: "oneOrMore",
|
|
1053
1116
|
_meta: meta,
|
|
1054
|
-
_def:
|
|
1117
|
+
_def: def,
|
|
1055
1118
|
parse(input, pos, ctx) {
|
|
1056
1119
|
const first = combinator.parse(input, pos, ctx);
|
|
1057
1120
|
if (!first.ok) return first;
|
|
1058
|
-
const values = [first.value];
|
|
1121
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
1059
1122
|
let cur = first.span.end;
|
|
1060
1123
|
while (cur < input.length) {
|
|
1061
1124
|
const item = repItem(combinator, input, cur, ctx);
|
|
1062
1125
|
if (item === "stop" || "fail" in item) break;
|
|
1063
|
-
values.push(item.value);
|
|
1126
|
+
if (values !== void 0) values.push(item.value);
|
|
1064
1127
|
cur = item.end;
|
|
1065
1128
|
}
|
|
1066
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1129
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1067
1130
|
}
|
|
1068
1131
|
};
|
|
1069
1132
|
}
|
|
@@ -1171,6 +1234,90 @@ function ref() {
|
|
|
1171
1234
|
return slot;
|
|
1172
1235
|
}
|
|
1173
1236
|
|
|
1237
|
+
// src/compiler/value-usage.ts
|
|
1238
|
+
function markUnusedValues(root) {
|
|
1239
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1240
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1241
|
+
const mark = (def, consumed) => {
|
|
1242
|
+
if (consumed) def.valueUnused = false;
|
|
1243
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1244
|
+
};
|
|
1245
|
+
const visit = (c, consumed) => {
|
|
1246
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1247
|
+
if (seen.has(c)) return;
|
|
1248
|
+
seen.add(c);
|
|
1249
|
+
const def = c._def;
|
|
1250
|
+
switch (def.tag) {
|
|
1251
|
+
case "many":
|
|
1252
|
+
case "oneOrMore":
|
|
1253
|
+
mark(def, consumed);
|
|
1254
|
+
visit(def.parser, consumed);
|
|
1255
|
+
return;
|
|
1256
|
+
case "optional":
|
|
1257
|
+
visit(def.parser, consumed);
|
|
1258
|
+
return;
|
|
1259
|
+
case "sequence":
|
|
1260
|
+
mark(def, consumed);
|
|
1261
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1262
|
+
return;
|
|
1263
|
+
case "choice":
|
|
1264
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1265
|
+
return;
|
|
1266
|
+
case "node":
|
|
1267
|
+
visit(def.parser, false);
|
|
1268
|
+
return;
|
|
1269
|
+
case "transform":
|
|
1270
|
+
visit(def.parser, true);
|
|
1271
|
+
return;
|
|
1272
|
+
case "skip":
|
|
1273
|
+
visit(def.main, consumed);
|
|
1274
|
+
visit(def.skipped, false);
|
|
1275
|
+
return;
|
|
1276
|
+
case "sepBy":
|
|
1277
|
+
visit(def.parser, true);
|
|
1278
|
+
visit(def.separator, false);
|
|
1279
|
+
return;
|
|
1280
|
+
case "not":
|
|
1281
|
+
visit(def.parser, false);
|
|
1282
|
+
return;
|
|
1283
|
+
case "grammar":
|
|
1284
|
+
visit(def.parser, consumed);
|
|
1285
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1286
|
+
return;
|
|
1287
|
+
case "recover":
|
|
1288
|
+
visit(def.parser, consumed);
|
|
1289
|
+
visit(def.sentinel, false);
|
|
1290
|
+
return;
|
|
1291
|
+
case "label":
|
|
1292
|
+
case "trivia":
|
|
1293
|
+
case "withCtx":
|
|
1294
|
+
case "expect":
|
|
1295
|
+
visit(def.parser, consumed);
|
|
1296
|
+
return;
|
|
1297
|
+
case "lazy":
|
|
1298
|
+
return;
|
|
1299
|
+
default:
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
const resolveRoot = (c) => {
|
|
1304
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1305
|
+
let cur = c;
|
|
1306
|
+
while (!guard2.has(cur)) {
|
|
1307
|
+
const d = cur._def;
|
|
1308
|
+
if (d.tag !== "lazy") return cur;
|
|
1309
|
+
guard2.add(cur);
|
|
1310
|
+
try {
|
|
1311
|
+
cur = d.thunk();
|
|
1312
|
+
} catch {
|
|
1313
|
+
return c;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return cur;
|
|
1317
|
+
};
|
|
1318
|
+
visit(resolveRoot(root), true);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1174
1321
|
// src/combinators/parser.ts
|
|
1175
1322
|
function rules(factory) {
|
|
1176
1323
|
const cache = {};
|
|
@@ -1200,6 +1347,9 @@ function rules(factory) {
|
|
|
1200
1347
|
cache[key] = parser2;
|
|
1201
1348
|
}
|
|
1202
1349
|
}
|
|
1350
|
+
for (const key of Object.keys(definitions)) {
|
|
1351
|
+
markUnusedValues(cache[key]);
|
|
1352
|
+
}
|
|
1203
1353
|
return cache;
|
|
1204
1354
|
}
|
|
1205
1355
|
|
|
@@ -1416,7 +1566,12 @@ function parser(opts, root) {
|
|
|
1416
1566
|
function noTrivia(root) {
|
|
1417
1567
|
return parser({ trivia: null }, root);
|
|
1418
1568
|
}
|
|
1569
|
+
var _analyzed = /* @__PURE__ */ new WeakSet();
|
|
1419
1570
|
function parse(combinator, input, opts = {}) {
|
|
1571
|
+
if (!_analyzed.has(combinator)) {
|
|
1572
|
+
_analyzed.add(combinator);
|
|
1573
|
+
markUnusedValues(combinator);
|
|
1574
|
+
}
|
|
1420
1575
|
const trackLines = opts.trackLines ?? false;
|
|
1421
1576
|
const _errors = opts.recover ? [] : void 0;
|
|
1422
1577
|
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
@@ -3373,6 +3528,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3373
3528
|
}
|
|
3374
3529
|
function emitSeq(def, ctx, pos) {
|
|
3375
3530
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3531
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3376
3532
|
const arrV = v(ctx, "_arr");
|
|
3377
3533
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3378
3534
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3579,7 +3735,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3579
3735
|
const autoNot = def.autoNot[i];
|
|
3580
3736
|
const atStart = failsAtStart(p);
|
|
3581
3737
|
const staticFx = armStaticExpected(ctx, p);
|
|
3582
|
-
const
|
|
3738
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3739
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3583
3740
|
let gateCond = null;
|
|
3584
3741
|
if (gate) {
|
|
3585
3742
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3670,19 +3827,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3670
3827
|
return checks.join(" && ");
|
|
3671
3828
|
}
|
|
3672
3829
|
function emitMany(def, ctx, pos) {
|
|
3830
|
+
const wantValue = !def.valueUnused;
|
|
3673
3831
|
const arrV = v(ctx, "_arr");
|
|
3674
3832
|
const curV = v(ctx, "_cur");
|
|
3675
3833
|
const stmts = [
|
|
3676
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3834
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3677
3835
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3678
3836
|
];
|
|
3679
3837
|
if (def.min === 1) {
|
|
3680
3838
|
const firstR = emit(def.parser, ctx, curV);
|
|
3681
3839
|
stmts.push(...firstR.stmts);
|
|
3682
|
-
stmts.push(
|
|
3683
|
-
|
|
3684
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3685
|
-
);
|
|
3840
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3841
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3686
3842
|
}
|
|
3687
3843
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3688
3844
|
ctx.indent++;
|
|
@@ -3713,13 +3869,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3713
3869
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3714
3870
|
stmts.push(
|
|
3715
3871
|
...iterStmts,
|
|
3716
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3717
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3718
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3872
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3719
3873
|
);
|
|
3874
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3875
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3720
3876
|
ctx.indent--;
|
|
3721
3877
|
stmts.push(`${ind(ctx)}}`);
|
|
3722
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3878
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3723
3879
|
}
|
|
3724
3880
|
function emitOptional(def, ctx, pos) {
|
|
3725
3881
|
const valV = v(ctx, "_opt");
|
|
@@ -4329,6 +4485,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4329
4485
|
return deps;
|
|
4330
4486
|
}
|
|
4331
4487
|
function compile(combinator, mapFnSources) {
|
|
4488
|
+
markUnusedValues(combinator);
|
|
4332
4489
|
const ctx = {
|
|
4333
4490
|
vars: 0,
|
|
4334
4491
|
indent: 1,
|
|
@@ -4411,8 +4568,17 @@ function compile(combinator, mapFnSources) {
|
|
|
4411
4568
|
}
|
|
4412
4569
|
};
|
|
4413
4570
|
}
|
|
4571
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4572
|
+
function assertRuleName(name) {
|
|
4573
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4574
|
+
throw new Error(
|
|
4575
|
+
`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).`
|
|
4576
|
+
);
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4414
4579
|
function compileLinkable(ruleMapArg, ns) {
|
|
4415
4580
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4581
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4416
4582
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4417
4583
|
const d = val._def;
|
|
4418
4584
|
if (d.tag !== "lazy") return true;
|
|
@@ -4443,13 +4609,15 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4443
4609
|
namedFnDecls: [],
|
|
4444
4610
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4445
4611
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4446
|
-
ns
|
|
4612
|
+
ns,
|
|
4613
|
+
deferFirstSetRefs: true
|
|
4447
4614
|
};
|
|
4448
4615
|
const seen = /* @__PURE__ */ new Map();
|
|
4449
4616
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4450
4617
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4451
4618
|
for (const [key, rule] of ruleMap) {
|
|
4452
|
-
|
|
4619
|
+
assertRuleName(key);
|
|
4620
|
+
const base = `_r_${key}`;
|
|
4453
4621
|
const n = seen.get(base) ?? 0;
|
|
4454
4622
|
seen.set(base, n + 1);
|
|
4455
4623
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4473,7 +4641,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4473
4641
|
if (resolved === void 0) {
|
|
4474
4642
|
const name = p._ruleName;
|
|
4475
4643
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4476
|
-
|
|
4644
|
+
assertRuleName(name);
|
|
4645
|
+
const fn = `_r_${name}`;
|
|
4477
4646
|
ruleNames.set(p, fn);
|
|
4478
4647
|
ctx.namedParsers.set(p, fn);
|
|
4479
4648
|
}
|
|
@@ -4542,12 +4711,26 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4542
4711
|
`}`
|
|
4543
4712
|
].join("\n"));
|
|
4544
4713
|
}
|
|
4714
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4715
|
+
for (const [key, rule] of ruleMap) {
|
|
4716
|
+
let resolved = rule;
|
|
4717
|
+
const d = rule._def;
|
|
4718
|
+
if (d.tag === "lazy") {
|
|
4719
|
+
try {
|
|
4720
|
+
resolved = d.thunk();
|
|
4721
|
+
} catch {
|
|
4722
|
+
resolved = rule;
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4726
|
+
}
|
|
4545
4727
|
return {
|
|
4546
4728
|
ns,
|
|
4547
4729
|
keys: perEntry.map((e) => e.key),
|
|
4548
4730
|
prelude,
|
|
4549
4731
|
ruleFns,
|
|
4550
4732
|
wrappers,
|
|
4733
|
+
firstSets,
|
|
4551
4734
|
deps: ruleDependencies(ruleMap),
|
|
4552
4735
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4553
4736
|
needsCollator: ctx.needsCollator,
|
|
@@ -4648,7 +4831,23 @@ function fusedBody(pieces) {
|
|
|
4648
4831
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4649
4832
|
];
|
|
4650
4833
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4651
|
-
const
|
|
4834
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4835
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4836
|
+
for (const [k, p] of winner) {
|
|
4837
|
+
const fs = p.firstSets?.get(k);
|
|
4838
|
+
if (fs) finalFS.set(k, fs);
|
|
4839
|
+
}
|
|
4840
|
+
const body = rawBody.replace(
|
|
4841
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4842
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4843
|
+
// matches every well-formed placeholder.
|
|
4844
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4845
|
+
(_m, name, codevar) => {
|
|
4846
|
+
const fs = finalFS.get(name);
|
|
4847
|
+
if (!fs || fs.kind === "any" || fs.kind === "empty") return "true";
|
|
4848
|
+
return `(${firstSetCond(codevar, fs)})`;
|
|
4849
|
+
}
|
|
4850
|
+
);
|
|
4652
4851
|
const env = {};
|
|
4653
4852
|
for (const p of contributing) {
|
|
4654
4853
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -5040,6 +5239,11 @@ function parseDoc(registry, rootRule, input, opts = {}) {
|
|
|
5040
5239
|
// src/functional/run.ts
|
|
5041
5240
|
var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
|
|
5042
5241
|
function run(entry, input, options = {}) {
|
|
5242
|
+
if (typeof entry !== "function" && typeof entry?.parse !== "function") {
|
|
5243
|
+
throw new TypeError(
|
|
5244
|
+
`run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
|
|
5245
|
+
);
|
|
5246
|
+
}
|
|
5043
5247
|
const triviaLog = [];
|
|
5044
5248
|
const errors = [];
|
|
5045
5249
|
const ctx = {
|