parseman 0.14.1 → 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/index.cjs +227 -28
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +227 -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/plugin/index.js
CHANGED
|
@@ -34,6 +34,63 @@ function any() {
|
|
|
34
34
|
function empty() {
|
|
35
35
|
return { kind: "empty" };
|
|
36
36
|
}
|
|
37
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
38
|
+
if (seen.has(p)) return true;
|
|
39
|
+
seen.add(p);
|
|
40
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
41
|
+
const d = p._def;
|
|
42
|
+
switch (d.tag) {
|
|
43
|
+
case "literal":
|
|
44
|
+
return d.value.length === 0;
|
|
45
|
+
case "keywords":
|
|
46
|
+
return false;
|
|
47
|
+
case "regex":
|
|
48
|
+
try {
|
|
49
|
+
const m = new RegExp(d.source).exec("");
|
|
50
|
+
return m != null && m[0] === "";
|
|
51
|
+
} catch {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
case "many":
|
|
55
|
+
case "optional":
|
|
56
|
+
case "not":
|
|
57
|
+
return true;
|
|
58
|
+
// zero repetitions / absent / lookahead
|
|
59
|
+
case "oneOrMore":
|
|
60
|
+
return me(d.parser);
|
|
61
|
+
case "sequence":
|
|
62
|
+
return d.parsers.every(me);
|
|
63
|
+
case "choice":
|
|
64
|
+
return d.parsers.some(me);
|
|
65
|
+
case "transform":
|
|
66
|
+
case "label":
|
|
67
|
+
case "trivia":
|
|
68
|
+
case "expect":
|
|
69
|
+
case "withCtx":
|
|
70
|
+
case "node":
|
|
71
|
+
case "grammar":
|
|
72
|
+
case "recover":
|
|
73
|
+
return me(d.parser);
|
|
74
|
+
case "skip":
|
|
75
|
+
return me(d.main);
|
|
76
|
+
case "lazy":
|
|
77
|
+
try {
|
|
78
|
+
return me(d.thunk());
|
|
79
|
+
} catch {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function sequenceFirstSet(parsers) {
|
|
87
|
+
let fs2 = empty();
|
|
88
|
+
for (const p of parsers) {
|
|
89
|
+
fs2 = union(fs2, p._meta.firstSet);
|
|
90
|
+
if (!matchesEmpty(p)) return fs2;
|
|
91
|
+
}
|
|
92
|
+
return fs2;
|
|
93
|
+
}
|
|
37
94
|
function mergeRanges(ranges) {
|
|
38
95
|
if (ranges.length === 0) return [];
|
|
39
96
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -882,16 +939,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
882
939
|
// src/combinators/sequence.ts
|
|
883
940
|
function sequence(...parsers) {
|
|
884
941
|
const meta = {
|
|
885
|
-
|
|
942
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
943
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
944
|
+
firstSet: sequenceFirstSet(parsers),
|
|
886
945
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
887
946
|
isTrivia: false
|
|
888
947
|
};
|
|
948
|
+
const def = { tag: "sequence", parsers };
|
|
889
949
|
return {
|
|
890
950
|
_tag: "sequence",
|
|
891
951
|
_meta: meta,
|
|
892
|
-
_def:
|
|
952
|
+
_def: def,
|
|
893
953
|
parse(input, pos, ctx) {
|
|
894
|
-
const values = [];
|
|
954
|
+
const values = def.valueUnused ? void 0 : [];
|
|
895
955
|
let cur = pos;
|
|
896
956
|
for (let i = 0; i < parsers.length; i++) {
|
|
897
957
|
if (ctx.trivia && i > 0) {
|
|
@@ -911,17 +971,18 @@ function sequence(...parsers) {
|
|
|
911
971
|
} else {
|
|
912
972
|
rollbackTrivia(ctx, mark);
|
|
913
973
|
}
|
|
914
|
-
values.push(result2.value);
|
|
974
|
+
if (values !== void 0) values.push(result2.value);
|
|
915
975
|
continue;
|
|
916
976
|
}
|
|
917
977
|
const result = parsers[i].parse(input, cur, ctx);
|
|
918
978
|
if (!result.ok) return result;
|
|
919
|
-
values.push(result.value);
|
|
979
|
+
if (values !== void 0) values.push(result.value);
|
|
920
980
|
cur = result.span.end;
|
|
921
981
|
}
|
|
922
982
|
return {
|
|
923
983
|
ok: true,
|
|
924
|
-
|
|
984
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
985
|
+
value: values ?? void 0,
|
|
925
986
|
span: { start: pos, end: cur }
|
|
926
987
|
};
|
|
927
988
|
}
|
|
@@ -962,20 +1023,21 @@ function many(combinator) {
|
|
|
962
1023
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
963
1024
|
isTrivia: false
|
|
964
1025
|
};
|
|
1026
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
965
1027
|
return {
|
|
966
1028
|
_tag: "many",
|
|
967
1029
|
_meta: meta,
|
|
968
|
-
_def:
|
|
1030
|
+
_def: def,
|
|
969
1031
|
parse(input, pos, ctx) {
|
|
970
|
-
const values = [];
|
|
1032
|
+
const values = def.valueUnused ? void 0 : [];
|
|
971
1033
|
let cur = pos;
|
|
972
1034
|
while (cur < input.length) {
|
|
973
1035
|
const item = repItem(combinator, input, cur, ctx);
|
|
974
1036
|
if (item === "stop" || "fail" in item) break;
|
|
975
|
-
values.push(item.value);
|
|
1037
|
+
if (values !== void 0) values.push(item.value);
|
|
976
1038
|
cur = item.end;
|
|
977
1039
|
}
|
|
978
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1040
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
979
1041
|
}
|
|
980
1042
|
};
|
|
981
1043
|
}
|
|
@@ -985,22 +1047,23 @@ function oneOrMore(combinator) {
|
|
|
985
1047
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
986
1048
|
isTrivia: false
|
|
987
1049
|
};
|
|
1050
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
988
1051
|
return {
|
|
989
1052
|
_tag: "oneOrMore",
|
|
990
1053
|
_meta: meta,
|
|
991
|
-
_def:
|
|
1054
|
+
_def: def,
|
|
992
1055
|
parse(input, pos, ctx) {
|
|
993
1056
|
const first = combinator.parse(input, pos, ctx);
|
|
994
1057
|
if (!first.ok) return first;
|
|
995
|
-
const values = [first.value];
|
|
1058
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
996
1059
|
let cur = first.span.end;
|
|
997
1060
|
while (cur < input.length) {
|
|
998
1061
|
const item = repItem(combinator, input, cur, ctx);
|
|
999
1062
|
if (item === "stop" || "fail" in item) break;
|
|
1000
|
-
values.push(item.value);
|
|
1063
|
+
if (values !== void 0) values.push(item.value);
|
|
1001
1064
|
cur = item.end;
|
|
1002
1065
|
}
|
|
1003
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1066
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1004
1067
|
}
|
|
1005
1068
|
};
|
|
1006
1069
|
}
|
|
@@ -1078,6 +1141,90 @@ function sepBy(combinator, separator) {
|
|
|
1078
1141
|
};
|
|
1079
1142
|
}
|
|
1080
1143
|
|
|
1144
|
+
// src/compiler/value-usage.ts
|
|
1145
|
+
function markUnusedValues(root) {
|
|
1146
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1147
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1148
|
+
const mark = (def, consumed) => {
|
|
1149
|
+
if (consumed) def.valueUnused = false;
|
|
1150
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1151
|
+
};
|
|
1152
|
+
const visit = (c, consumed) => {
|
|
1153
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1154
|
+
if (seen.has(c)) return;
|
|
1155
|
+
seen.add(c);
|
|
1156
|
+
const def = c._def;
|
|
1157
|
+
switch (def.tag) {
|
|
1158
|
+
case "many":
|
|
1159
|
+
case "oneOrMore":
|
|
1160
|
+
mark(def, consumed);
|
|
1161
|
+
visit(def.parser, consumed);
|
|
1162
|
+
return;
|
|
1163
|
+
case "optional":
|
|
1164
|
+
visit(def.parser, consumed);
|
|
1165
|
+
return;
|
|
1166
|
+
case "sequence":
|
|
1167
|
+
mark(def, consumed);
|
|
1168
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1169
|
+
return;
|
|
1170
|
+
case "choice":
|
|
1171
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1172
|
+
return;
|
|
1173
|
+
case "node":
|
|
1174
|
+
visit(def.parser, false);
|
|
1175
|
+
return;
|
|
1176
|
+
case "transform":
|
|
1177
|
+
visit(def.parser, true);
|
|
1178
|
+
return;
|
|
1179
|
+
case "skip":
|
|
1180
|
+
visit(def.main, consumed);
|
|
1181
|
+
visit(def.skipped, false);
|
|
1182
|
+
return;
|
|
1183
|
+
case "sepBy":
|
|
1184
|
+
visit(def.parser, true);
|
|
1185
|
+
visit(def.separator, false);
|
|
1186
|
+
return;
|
|
1187
|
+
case "not":
|
|
1188
|
+
visit(def.parser, false);
|
|
1189
|
+
return;
|
|
1190
|
+
case "grammar":
|
|
1191
|
+
visit(def.parser, consumed);
|
|
1192
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1193
|
+
return;
|
|
1194
|
+
case "recover":
|
|
1195
|
+
visit(def.parser, consumed);
|
|
1196
|
+
visit(def.sentinel, false);
|
|
1197
|
+
return;
|
|
1198
|
+
case "label":
|
|
1199
|
+
case "trivia":
|
|
1200
|
+
case "withCtx":
|
|
1201
|
+
case "expect":
|
|
1202
|
+
visit(def.parser, consumed);
|
|
1203
|
+
return;
|
|
1204
|
+
case "lazy":
|
|
1205
|
+
return;
|
|
1206
|
+
default:
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
const resolveRoot = (c) => {
|
|
1211
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1212
|
+
let cur = c;
|
|
1213
|
+
while (!guard2.has(cur)) {
|
|
1214
|
+
const d = cur._def;
|
|
1215
|
+
if (d.tag !== "lazy") return cur;
|
|
1216
|
+
guard2.add(cur);
|
|
1217
|
+
try {
|
|
1218
|
+
cur = d.thunk();
|
|
1219
|
+
} catch {
|
|
1220
|
+
return c;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return cur;
|
|
1224
|
+
};
|
|
1225
|
+
visit(resolveRoot(root), true);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1081
1228
|
// src/combinators/not.ts
|
|
1082
1229
|
function not(combinator) {
|
|
1083
1230
|
const meta = {
|
|
@@ -3216,6 +3363,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3216
3363
|
}
|
|
3217
3364
|
function emitSeq(def, ctx, pos) {
|
|
3218
3365
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3366
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3219
3367
|
const arrV = v(ctx, "_arr");
|
|
3220
3368
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3221
3369
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3422,7 +3570,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3422
3570
|
const autoNot = def.autoNot[i];
|
|
3423
3571
|
const atStart = failsAtStart(p);
|
|
3424
3572
|
const staticFx = armStaticExpected(ctx, p);
|
|
3425
|
-
const
|
|
3573
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3574
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3426
3575
|
let gateCond = null;
|
|
3427
3576
|
if (gate) {
|
|
3428
3577
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3513,19 +3662,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3513
3662
|
return checks.join(" && ");
|
|
3514
3663
|
}
|
|
3515
3664
|
function emitMany(def, ctx, pos) {
|
|
3665
|
+
const wantValue = !def.valueUnused;
|
|
3516
3666
|
const arrV = v(ctx, "_arr");
|
|
3517
3667
|
const curV = v(ctx, "_cur");
|
|
3518
3668
|
const stmts = [
|
|
3519
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3669
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3520
3670
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3521
3671
|
];
|
|
3522
3672
|
if (def.min === 1) {
|
|
3523
3673
|
const firstR = emit(def.parser, ctx, curV);
|
|
3524
3674
|
stmts.push(...firstR.stmts);
|
|
3525
|
-
stmts.push(
|
|
3526
|
-
|
|
3527
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3528
|
-
);
|
|
3675
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3676
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3529
3677
|
}
|
|
3530
3678
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3531
3679
|
ctx.indent++;
|
|
@@ -3556,13 +3704,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3556
3704
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3557
3705
|
stmts.push(
|
|
3558
3706
|
...iterStmts,
|
|
3559
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3560
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3561
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3707
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3562
3708
|
);
|
|
3709
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3710
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3563
3711
|
ctx.indent--;
|
|
3564
3712
|
stmts.push(`${ind(ctx)}}`);
|
|
3565
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3713
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3566
3714
|
}
|
|
3567
3715
|
function emitOptional(def, ctx, pos) {
|
|
3568
3716
|
const valV = v(ctx, "_opt");
|
|
@@ -4172,6 +4320,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4172
4320
|
return deps;
|
|
4173
4321
|
}
|
|
4174
4322
|
function compile(combinator, mapFnSources) {
|
|
4323
|
+
markUnusedValues(combinator);
|
|
4175
4324
|
const ctx = {
|
|
4176
4325
|
vars: 0,
|
|
4177
4326
|
indent: 1,
|
|
@@ -4254,7 +4403,16 @@ function compile(combinator, mapFnSources) {
|
|
|
4254
4403
|
}
|
|
4255
4404
|
};
|
|
4256
4405
|
}
|
|
4406
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4407
|
+
function assertRuleName(name) {
|
|
4408
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4409
|
+
throw new Error(
|
|
4410
|
+
`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).`
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4257
4414
|
function compileRuleMap(ruleMap) {
|
|
4415
|
+
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
4258
4416
|
const ctx = {
|
|
4259
4417
|
vars: 0,
|
|
4260
4418
|
indent: 1,
|
|
@@ -4279,7 +4437,8 @@ function compileRuleMap(ruleMap) {
|
|
|
4279
4437
|
const seenNames = /* @__PURE__ */ new Map();
|
|
4280
4438
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4281
4439
|
for (const [key, rule] of ruleMap) {
|
|
4282
|
-
|
|
4440
|
+
assertRuleName(key);
|
|
4441
|
+
const base = `_r_${key}`;
|
|
4283
4442
|
const n = seenNames.get(base) ?? 0;
|
|
4284
4443
|
seenNames.set(base, n + 1);
|
|
4285
4444
|
ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
|
|
@@ -4330,6 +4489,7 @@ function compileRuleMap(ruleMap) {
|
|
|
4330
4489
|
}
|
|
4331
4490
|
function compileLinkable(ruleMapArg, ns) {
|
|
4332
4491
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4492
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4333
4493
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4334
4494
|
const d = val._def;
|
|
4335
4495
|
if (d.tag !== "lazy") return true;
|
|
@@ -4360,13 +4520,15 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4360
4520
|
namedFnDecls: [],
|
|
4361
4521
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4362
4522
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4363
|
-
ns
|
|
4523
|
+
ns,
|
|
4524
|
+
deferFirstSetRefs: true
|
|
4364
4525
|
};
|
|
4365
4526
|
const seen = /* @__PURE__ */ new Map();
|
|
4366
4527
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4367
4528
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4368
4529
|
for (const [key, rule] of ruleMap) {
|
|
4369
|
-
|
|
4530
|
+
assertRuleName(key);
|
|
4531
|
+
const base = `_r_${key}`;
|
|
4370
4532
|
const n = seen.get(base) ?? 0;
|
|
4371
4533
|
seen.set(base, n + 1);
|
|
4372
4534
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4390,7 +4552,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4390
4552
|
if (resolved === void 0) {
|
|
4391
4553
|
const name = p._ruleName;
|
|
4392
4554
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4393
|
-
|
|
4555
|
+
assertRuleName(name);
|
|
4556
|
+
const fn = `_r_${name}`;
|
|
4394
4557
|
ruleNames.set(p, fn);
|
|
4395
4558
|
ctx.namedParsers.set(p, fn);
|
|
4396
4559
|
}
|
|
@@ -4459,12 +4622,26 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4459
4622
|
`}`
|
|
4460
4623
|
].join("\n"));
|
|
4461
4624
|
}
|
|
4625
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4626
|
+
for (const [key, rule] of ruleMap) {
|
|
4627
|
+
let resolved = rule;
|
|
4628
|
+
const d = rule._def;
|
|
4629
|
+
if (d.tag === "lazy") {
|
|
4630
|
+
try {
|
|
4631
|
+
resolved = d.thunk();
|
|
4632
|
+
} catch {
|
|
4633
|
+
resolved = rule;
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4637
|
+
}
|
|
4462
4638
|
return {
|
|
4463
4639
|
ns,
|
|
4464
4640
|
keys: perEntry.map((e) => e.key),
|
|
4465
4641
|
prelude,
|
|
4466
4642
|
ruleFns,
|
|
4467
4643
|
wrappers,
|
|
4644
|
+
firstSets,
|
|
4468
4645
|
deps: ruleDependencies(ruleMap),
|
|
4469
4646
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4470
4647
|
needsCollator: ctx.needsCollator,
|
|
@@ -4534,7 +4711,23 @@ function fusedBody(pieces) {
|
|
|
4534
4711
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4535
4712
|
];
|
|
4536
4713
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4537
|
-
const
|
|
4714
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4715
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4716
|
+
for (const [k, p] of winner) {
|
|
4717
|
+
const fs2 = p.firstSets?.get(k);
|
|
4718
|
+
if (fs2) finalFS.set(k, fs2);
|
|
4719
|
+
}
|
|
4720
|
+
const body = rawBody.replace(
|
|
4721
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4722
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4723
|
+
// matches every well-formed placeholder.
|
|
4724
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4725
|
+
(_m, name, codevar) => {
|
|
4726
|
+
const fs2 = finalFS.get(name);
|
|
4727
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4728
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
4729
|
+
}
|
|
4730
|
+
);
|
|
4538
4731
|
const env = {};
|
|
4539
4732
|
for (const p of contributing) {
|
|
4540
4733
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -5314,7 +5507,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5314
5507
|
const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
5315
5508
|
const serializePieces = (p) => {
|
|
5316
5509
|
const mapLit = (m) => `new Map([${[...m].map(([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(v2)}]`).join(", ")}])`;
|
|
5317
|
-
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude)}, ruleFns: ${mapLit(p.ruleFns)}, wrappers: ${mapLit(p.wrappers)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsCollator: ${p.needsCollator}, mfFns: [], buildFns: [] }`;
|
|
5510
|
+
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude)}, ruleFns: ${mapLit(p.ruleFns)}, wrappers: ${mapLit(p.wrappers)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsCollator: ${p.needsCollator}, mfFns: [], buildFns: [] }`;
|
|
5318
5511
|
};
|
|
5319
5512
|
const serializeList = (list) => `[${list.map(serializePieces).join(", ")}]`;
|
|
5320
5513
|
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ (() => { const _g = ${grammarExpr}; Object.defineProperty(_g, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)} }); return _g })()`;
|