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.cjs
CHANGED
|
@@ -69,6 +69,63 @@ function any() {
|
|
|
69
69
|
function empty() {
|
|
70
70
|
return { kind: "empty" };
|
|
71
71
|
}
|
|
72
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
73
|
+
if (seen.has(p)) return true;
|
|
74
|
+
seen.add(p);
|
|
75
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
76
|
+
const d = p._def;
|
|
77
|
+
switch (d.tag) {
|
|
78
|
+
case "literal":
|
|
79
|
+
return d.value.length === 0;
|
|
80
|
+
case "keywords":
|
|
81
|
+
return false;
|
|
82
|
+
case "regex":
|
|
83
|
+
try {
|
|
84
|
+
const m = new RegExp(d.source).exec("");
|
|
85
|
+
return m != null && m[0] === "";
|
|
86
|
+
} catch {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
case "many":
|
|
90
|
+
case "optional":
|
|
91
|
+
case "not":
|
|
92
|
+
return true;
|
|
93
|
+
// zero repetitions / absent / lookahead
|
|
94
|
+
case "oneOrMore":
|
|
95
|
+
return me(d.parser);
|
|
96
|
+
case "sequence":
|
|
97
|
+
return d.parsers.every(me);
|
|
98
|
+
case "choice":
|
|
99
|
+
return d.parsers.some(me);
|
|
100
|
+
case "transform":
|
|
101
|
+
case "label":
|
|
102
|
+
case "trivia":
|
|
103
|
+
case "expect":
|
|
104
|
+
case "withCtx":
|
|
105
|
+
case "node":
|
|
106
|
+
case "grammar":
|
|
107
|
+
case "recover":
|
|
108
|
+
return me(d.parser);
|
|
109
|
+
case "skip":
|
|
110
|
+
return me(d.main);
|
|
111
|
+
case "lazy":
|
|
112
|
+
try {
|
|
113
|
+
return me(d.thunk());
|
|
114
|
+
} catch {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function sequenceFirstSet(parsers) {
|
|
122
|
+
let fs2 = empty();
|
|
123
|
+
for (const p of parsers) {
|
|
124
|
+
fs2 = union(fs2, p._meta.firstSet);
|
|
125
|
+
if (!matchesEmpty(p)) return fs2;
|
|
126
|
+
}
|
|
127
|
+
return fs2;
|
|
128
|
+
}
|
|
72
129
|
function mergeRanges(ranges) {
|
|
73
130
|
if (ranges.length === 0) return [];
|
|
74
131
|
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
@@ -917,16 +974,19 @@ function scanTrivia(input, cur, ctx) {
|
|
|
917
974
|
// src/combinators/sequence.ts
|
|
918
975
|
function sequence(...parsers) {
|
|
919
976
|
const meta = {
|
|
920
|
-
|
|
977
|
+
// Union through the nullable prefix — a leading optional()/many() lets a later
|
|
978
|
+
// term's first char start the sequence. Just `parsers[0]` under-approximates.
|
|
979
|
+
firstSet: sequenceFirstSet(parsers),
|
|
921
980
|
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
922
981
|
isTrivia: false
|
|
923
982
|
};
|
|
983
|
+
const def = { tag: "sequence", parsers };
|
|
924
984
|
return {
|
|
925
985
|
_tag: "sequence",
|
|
926
986
|
_meta: meta,
|
|
927
|
-
_def:
|
|
987
|
+
_def: def,
|
|
928
988
|
parse(input, pos, ctx) {
|
|
929
|
-
const values = [];
|
|
989
|
+
const values = def.valueUnused ? void 0 : [];
|
|
930
990
|
let cur = pos;
|
|
931
991
|
for (let i = 0; i < parsers.length; i++) {
|
|
932
992
|
if (ctx.trivia && i > 0) {
|
|
@@ -946,17 +1006,18 @@ function sequence(...parsers) {
|
|
|
946
1006
|
} else {
|
|
947
1007
|
rollbackTrivia(ctx, mark);
|
|
948
1008
|
}
|
|
949
|
-
values.push(result2.value);
|
|
1009
|
+
if (values !== void 0) values.push(result2.value);
|
|
950
1010
|
continue;
|
|
951
1011
|
}
|
|
952
1012
|
const result = parsers[i].parse(input, cur, ctx);
|
|
953
1013
|
if (!result.ok) return result;
|
|
954
|
-
values.push(result.value);
|
|
1014
|
+
if (values !== void 0) values.push(result.value);
|
|
955
1015
|
cur = result.span.end;
|
|
956
1016
|
}
|
|
957
1017
|
return {
|
|
958
1018
|
ok: true,
|
|
959
|
-
|
|
1019
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
1020
|
+
value: values ?? void 0,
|
|
960
1021
|
span: { start: pos, end: cur }
|
|
961
1022
|
};
|
|
962
1023
|
}
|
|
@@ -997,20 +1058,21 @@ function many(combinator) {
|
|
|
997
1058
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
998
1059
|
isTrivia: false
|
|
999
1060
|
};
|
|
1061
|
+
const def = { tag: "many", parser: combinator, min: 0 };
|
|
1000
1062
|
return {
|
|
1001
1063
|
_tag: "many",
|
|
1002
1064
|
_meta: meta,
|
|
1003
|
-
_def:
|
|
1065
|
+
_def: def,
|
|
1004
1066
|
parse(input, pos, ctx) {
|
|
1005
|
-
const values = [];
|
|
1067
|
+
const values = def.valueUnused ? void 0 : [];
|
|
1006
1068
|
let cur = pos;
|
|
1007
1069
|
while (cur < input.length) {
|
|
1008
1070
|
const item = repItem(combinator, input, cur, ctx);
|
|
1009
1071
|
if (item === "stop" || "fail" in item) break;
|
|
1010
|
-
values.push(item.value);
|
|
1072
|
+
if (values !== void 0) values.push(item.value);
|
|
1011
1073
|
cur = item.end;
|
|
1012
1074
|
}
|
|
1013
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1075
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1014
1076
|
}
|
|
1015
1077
|
};
|
|
1016
1078
|
}
|
|
@@ -1020,22 +1082,23 @@ function oneOrMore(combinator) {
|
|
|
1020
1082
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1021
1083
|
isTrivia: false
|
|
1022
1084
|
};
|
|
1085
|
+
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
1023
1086
|
return {
|
|
1024
1087
|
_tag: "oneOrMore",
|
|
1025
1088
|
_meta: meta,
|
|
1026
|
-
_def:
|
|
1089
|
+
_def: def,
|
|
1027
1090
|
parse(input, pos, ctx) {
|
|
1028
1091
|
const first = combinator.parse(input, pos, ctx);
|
|
1029
1092
|
if (!first.ok) return first;
|
|
1030
|
-
const values = [first.value];
|
|
1093
|
+
const values = def.valueUnused ? void 0 : [first.value];
|
|
1031
1094
|
let cur = first.span.end;
|
|
1032
1095
|
while (cur < input.length) {
|
|
1033
1096
|
const item = repItem(combinator, input, cur, ctx);
|
|
1034
1097
|
if (item === "stop" || "fail" in item) break;
|
|
1035
|
-
values.push(item.value);
|
|
1098
|
+
if (values !== void 0) values.push(item.value);
|
|
1036
1099
|
cur = item.end;
|
|
1037
1100
|
}
|
|
1038
|
-
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
1101
|
+
return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
|
|
1039
1102
|
}
|
|
1040
1103
|
};
|
|
1041
1104
|
}
|
|
@@ -1113,6 +1176,90 @@ function sepBy(combinator, separator) {
|
|
|
1113
1176
|
};
|
|
1114
1177
|
}
|
|
1115
1178
|
|
|
1179
|
+
// src/compiler/value-usage.ts
|
|
1180
|
+
function markUnusedValues(root) {
|
|
1181
|
+
const seenConsumed = /* @__PURE__ */ new WeakSet();
|
|
1182
|
+
const seenUnconsumed = /* @__PURE__ */ new WeakSet();
|
|
1183
|
+
const mark = (def, consumed) => {
|
|
1184
|
+
if (consumed) def.valueUnused = false;
|
|
1185
|
+
else if (def.valueUnused !== false) def.valueUnused = true;
|
|
1186
|
+
};
|
|
1187
|
+
const visit = (c, consumed) => {
|
|
1188
|
+
const seen = consumed ? seenConsumed : seenUnconsumed;
|
|
1189
|
+
if (seen.has(c)) return;
|
|
1190
|
+
seen.add(c);
|
|
1191
|
+
const def = c._def;
|
|
1192
|
+
switch (def.tag) {
|
|
1193
|
+
case "many":
|
|
1194
|
+
case "oneOrMore":
|
|
1195
|
+
mark(def, consumed);
|
|
1196
|
+
visit(def.parser, consumed);
|
|
1197
|
+
return;
|
|
1198
|
+
case "optional":
|
|
1199
|
+
visit(def.parser, consumed);
|
|
1200
|
+
return;
|
|
1201
|
+
case "sequence":
|
|
1202
|
+
mark(def, consumed);
|
|
1203
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1204
|
+
return;
|
|
1205
|
+
case "choice":
|
|
1206
|
+
for (const p of def.parsers) visit(p, consumed);
|
|
1207
|
+
return;
|
|
1208
|
+
case "node":
|
|
1209
|
+
visit(def.parser, false);
|
|
1210
|
+
return;
|
|
1211
|
+
case "transform":
|
|
1212
|
+
visit(def.parser, true);
|
|
1213
|
+
return;
|
|
1214
|
+
case "skip":
|
|
1215
|
+
visit(def.main, consumed);
|
|
1216
|
+
visit(def.skipped, false);
|
|
1217
|
+
return;
|
|
1218
|
+
case "sepBy":
|
|
1219
|
+
visit(def.parser, true);
|
|
1220
|
+
visit(def.separator, false);
|
|
1221
|
+
return;
|
|
1222
|
+
case "not":
|
|
1223
|
+
visit(def.parser, false);
|
|
1224
|
+
return;
|
|
1225
|
+
case "grammar":
|
|
1226
|
+
visit(def.parser, consumed);
|
|
1227
|
+
if (def.triviaParser) visit(def.triviaParser, false);
|
|
1228
|
+
return;
|
|
1229
|
+
case "recover":
|
|
1230
|
+
visit(def.parser, consumed);
|
|
1231
|
+
visit(def.sentinel, false);
|
|
1232
|
+
return;
|
|
1233
|
+
case "label":
|
|
1234
|
+
case "trivia":
|
|
1235
|
+
case "withCtx":
|
|
1236
|
+
case "expect":
|
|
1237
|
+
visit(def.parser, consumed);
|
|
1238
|
+
return;
|
|
1239
|
+
case "lazy":
|
|
1240
|
+
return;
|
|
1241
|
+
default:
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
const resolveRoot = (c) => {
|
|
1246
|
+
const guard2 = /* @__PURE__ */ new Set();
|
|
1247
|
+
let cur = c;
|
|
1248
|
+
while (!guard2.has(cur)) {
|
|
1249
|
+
const d = cur._def;
|
|
1250
|
+
if (d.tag !== "lazy") return cur;
|
|
1251
|
+
guard2.add(cur);
|
|
1252
|
+
try {
|
|
1253
|
+
cur = d.thunk();
|
|
1254
|
+
} catch {
|
|
1255
|
+
return c;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return cur;
|
|
1259
|
+
};
|
|
1260
|
+
visit(resolveRoot(root), true);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1116
1263
|
// src/combinators/not.ts
|
|
1117
1264
|
function not(combinator) {
|
|
1118
1265
|
const meta = {
|
|
@@ -3251,6 +3398,7 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
3251
3398
|
}
|
|
3252
3399
|
function emitSeq(def, ctx, pos) {
|
|
3253
3400
|
const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
|
|
3401
|
+
if (def.valueUnused) return { stmts, valueVar: "undefined", endVar };
|
|
3254
3402
|
const arrV = v(ctx, "_arr");
|
|
3255
3403
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
3256
3404
|
return { stmts, valueVar: arrV, endVar };
|
|
@@ -3457,7 +3605,8 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3457
3605
|
const autoNot = def.autoNot[i];
|
|
3458
3606
|
const atStart = failsAtStart(p);
|
|
3459
3607
|
const staticFx = armStaticExpected(ctx, p);
|
|
3460
|
-
const
|
|
3608
|
+
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
3609
|
+
const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
3461
3610
|
let gateCond = null;
|
|
3462
3611
|
if (gate) {
|
|
3463
3612
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -3548,19 +3697,18 @@ function emitLiteralCondition(litVal, pos) {
|
|
|
3548
3697
|
return checks.join(" && ");
|
|
3549
3698
|
}
|
|
3550
3699
|
function emitMany(def, ctx, pos) {
|
|
3700
|
+
const wantValue = !def.valueUnused;
|
|
3551
3701
|
const arrV = v(ctx, "_arr");
|
|
3552
3702
|
const curV = v(ctx, "_cur");
|
|
3553
3703
|
const stmts = [
|
|
3554
|
-
`${ind(ctx)}const ${arrV} = []
|
|
3704
|
+
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
3555
3705
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
3556
3706
|
];
|
|
3557
3707
|
if (def.min === 1) {
|
|
3558
3708
|
const firstR = emit(def.parser, ctx, curV);
|
|
3559
3709
|
stmts.push(...firstR.stmts);
|
|
3560
|
-
stmts.push(
|
|
3561
|
-
|
|
3562
|
-
`${ind(ctx)}${curV} = ${firstR.endVar}`
|
|
3563
|
-
);
|
|
3710
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${firstR.valueVar})`);
|
|
3711
|
+
stmts.push(`${ind(ctx)}${curV} = ${firstR.endVar}`);
|
|
3564
3712
|
}
|
|
3565
3713
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
3566
3714
|
ctx.indent++;
|
|
@@ -3591,13 +3739,13 @@ function emitMany(def, ctx, pos) {
|
|
|
3591
3739
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
3592
3740
|
stmts.push(
|
|
3593
3741
|
...iterStmts,
|
|
3594
|
-
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }
|
|
3595
|
-
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
3596
|
-
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
3742
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
3597
3743
|
);
|
|
3744
|
+
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
3745
|
+
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
3598
3746
|
ctx.indent--;
|
|
3599
3747
|
stmts.push(`${ind(ctx)}}`);
|
|
3600
|
-
return { stmts, valueVar: arrV, endVar: curV };
|
|
3748
|
+
return { stmts, valueVar: wantValue ? arrV : "undefined", endVar: curV };
|
|
3601
3749
|
}
|
|
3602
3750
|
function emitOptional(def, ctx, pos) {
|
|
3603
3751
|
const valV = v(ctx, "_opt");
|
|
@@ -4207,6 +4355,7 @@ function ruleDependencies(ruleMap) {
|
|
|
4207
4355
|
return deps;
|
|
4208
4356
|
}
|
|
4209
4357
|
function compile(combinator, mapFnSources) {
|
|
4358
|
+
markUnusedValues(combinator);
|
|
4210
4359
|
const ctx = {
|
|
4211
4360
|
vars: 0,
|
|
4212
4361
|
indent: 1,
|
|
@@ -4289,7 +4438,16 @@ function compile(combinator, mapFnSources) {
|
|
|
4289
4438
|
}
|
|
4290
4439
|
};
|
|
4291
4440
|
}
|
|
4441
|
+
var RULE_NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
4442
|
+
function assertRuleName(name) {
|
|
4443
|
+
if (!RULE_NAME_RE.test(name)) {
|
|
4444
|
+
throw new Error(
|
|
4445
|
+
`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).`
|
|
4446
|
+
);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4292
4449
|
function compileRuleMap(ruleMap) {
|
|
4450
|
+
for (const [, rule] of ruleMap) markUnusedValues(rule);
|
|
4293
4451
|
const ctx = {
|
|
4294
4452
|
vars: 0,
|
|
4295
4453
|
indent: 1,
|
|
@@ -4314,7 +4472,8 @@ function compileRuleMap(ruleMap) {
|
|
|
4314
4472
|
const seenNames = /* @__PURE__ */ new Map();
|
|
4315
4473
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4316
4474
|
for (const [key, rule] of ruleMap) {
|
|
4317
|
-
|
|
4475
|
+
assertRuleName(key);
|
|
4476
|
+
const base = `_r_${key}`;
|
|
4318
4477
|
const n = seenNames.get(base) ?? 0;
|
|
4319
4478
|
seenNames.set(base, n + 1);
|
|
4320
4479
|
ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
|
|
@@ -4365,6 +4524,7 @@ function compileRuleMap(ruleMap) {
|
|
|
4365
4524
|
}
|
|
4366
4525
|
function compileLinkable(ruleMapArg, ns) {
|
|
4367
4526
|
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4527
|
+
for (const [, rule] of ruleMapArg) markUnusedValues(rule);
|
|
4368
4528
|
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4369
4529
|
const d = val._def;
|
|
4370
4530
|
if (d.tag !== "lazy") return true;
|
|
@@ -4395,13 +4555,15 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4395
4555
|
namedFnDecls: [],
|
|
4396
4556
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4397
4557
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4398
|
-
ns
|
|
4558
|
+
ns,
|
|
4559
|
+
deferFirstSetRefs: true
|
|
4399
4560
|
};
|
|
4400
4561
|
const seen = /* @__PURE__ */ new Map();
|
|
4401
4562
|
const ruleNames = /* @__PURE__ */ new Map();
|
|
4402
4563
|
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4403
4564
|
for (const [key, rule] of ruleMap) {
|
|
4404
|
-
|
|
4565
|
+
assertRuleName(key);
|
|
4566
|
+
const base = `_r_${key}`;
|
|
4405
4567
|
const n = seen.get(base) ?? 0;
|
|
4406
4568
|
seen.set(base, n + 1);
|
|
4407
4569
|
const fn = n === 0 ? base : `${base}$${n}`;
|
|
@@ -4425,7 +4587,8 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4425
4587
|
if (resolved === void 0) {
|
|
4426
4588
|
const name = p._ruleName;
|
|
4427
4589
|
if (name && !ctx.namedParsers.has(p)) {
|
|
4428
|
-
|
|
4590
|
+
assertRuleName(name);
|
|
4591
|
+
const fn = `_r_${name}`;
|
|
4429
4592
|
ruleNames.set(p, fn);
|
|
4430
4593
|
ctx.namedParsers.set(p, fn);
|
|
4431
4594
|
}
|
|
@@ -4494,12 +4657,26 @@ function compileLinkable(ruleMapArg, ns) {
|
|
|
4494
4657
|
`}`
|
|
4495
4658
|
].join("\n"));
|
|
4496
4659
|
}
|
|
4660
|
+
const firstSets = /* @__PURE__ */ new Map();
|
|
4661
|
+
for (const [key, rule] of ruleMap) {
|
|
4662
|
+
let resolved = rule;
|
|
4663
|
+
const d = rule._def;
|
|
4664
|
+
if (d.tag === "lazy") {
|
|
4665
|
+
try {
|
|
4666
|
+
resolved = d.thunk();
|
|
4667
|
+
} catch {
|
|
4668
|
+
resolved = rule;
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
|
|
4672
|
+
}
|
|
4497
4673
|
return {
|
|
4498
4674
|
ns,
|
|
4499
4675
|
keys: perEntry.map((e) => e.key),
|
|
4500
4676
|
prelude,
|
|
4501
4677
|
ruleFns,
|
|
4502
4678
|
wrappers,
|
|
4679
|
+
firstSets,
|
|
4503
4680
|
deps: ruleDependencies(ruleMap),
|
|
4504
4681
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4505
4682
|
needsCollator: ctx.needsCollator,
|
|
@@ -4569,7 +4746,23 @@ function fusedBody(pieces) {
|
|
|
4569
4746
|
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4570
4747
|
];
|
|
4571
4748
|
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4572
|
-
const
|
|
4749
|
+
const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4750
|
+
const finalFS = /* @__PURE__ */ new Map();
|
|
4751
|
+
for (const [k, p] of winner) {
|
|
4752
|
+
const fs2 = p.firstSets?.get(k);
|
|
4753
|
+
if (fs2) finalFS.set(k, fs2);
|
|
4754
|
+
}
|
|
4755
|
+
const body = rawBody.replace(
|
|
4756
|
+
// Rule name + code-point var are both JS identifiers (rule names are validated
|
|
4757
|
+
// at compile time — see assertRuleName in codegen), so an identifier class
|
|
4758
|
+
// matches every well-formed placeholder.
|
|
4759
|
+
/\/\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\*\/true/g,
|
|
4760
|
+
(_m, name, codevar) => {
|
|
4761
|
+
const fs2 = finalFS.get(name);
|
|
4762
|
+
if (!fs2 || fs2.kind === "any" || fs2.kind === "empty") return "true";
|
|
4763
|
+
return `(${firstSetCond(codevar, fs2)})`;
|
|
4764
|
+
}
|
|
4765
|
+
);
|
|
4573
4766
|
const env = {};
|
|
4574
4767
|
for (const p of contributing) {
|
|
4575
4768
|
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
@@ -5349,7 +5542,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
5349
5542
|
const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
5350
5543
|
const serializePieces = (p) => {
|
|
5351
5544
|
const mapLit = (m) => `new Map([${[...m].map(([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(v2)}]`).join(", ")}])`;
|
|
5352
|
-
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: [] }`;
|
|
5545
|
+
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: [] }`;
|
|
5353
5546
|
};
|
|
5354
5547
|
const serializeList = (list) => `[${list.map(serializePieces).join(", ")}]`;
|
|
5355
5548
|
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ (() => { const _g = ${grammarExpr}; Object.defineProperty(_g, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)} }); return _g })()`;
|