parseman 0.29.0 → 0.30.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/attempt.d.ts.map +1 -1
- package/dist/combinators/choice.d.ts +11 -0
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts +7 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +1 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/index.cjs +147 -15
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +147 -15
- package/dist/index.js.map +2 -2
- package/dist/plugin/index.cjs +147 -15
- package/dist/plugin/index.cjs.map +3 -3
- package/dist/plugin/index.js +147 -15
- package/dist/plugin/index.js.map +3 -3
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/plugin/index.cjs
CHANGED
|
@@ -61,6 +61,15 @@ function intersects(a, b) {
|
|
|
61
61
|
function fromChar(code) {
|
|
62
62
|
return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
|
|
63
63
|
}
|
|
64
|
+
function startsFirstSet(combinator, input, pos) {
|
|
65
|
+
const fs2 = combinator._meta.firstSet;
|
|
66
|
+
if (fs2.kind === "any") return true;
|
|
67
|
+
if (fs2.kind === "empty") return false;
|
|
68
|
+
const code = input.codePointAt(pos);
|
|
69
|
+
if (code === void 0) return false;
|
|
70
|
+
for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
64
73
|
function fromRange(lo, hi) {
|
|
65
74
|
return { kind: "ranges", ranges: [{ lo, hi }] };
|
|
66
75
|
}
|
|
@@ -1020,6 +1029,17 @@ function deriveExpected(c) {
|
|
|
1020
1029
|
return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
|
|
1021
1030
|
case "attempt":
|
|
1022
1031
|
return deriveExpected(def.parser);
|
|
1032
|
+
// Delegating wrappers whose first-token failure is their inner parser's — must
|
|
1033
|
+
// pass through so a start-failure (and the first-set-miss fast-path in attempt/
|
|
1034
|
+
// node, which reads this) names the real expected token, not the wrapper tag.
|
|
1035
|
+
case "field":
|
|
1036
|
+
case "withCtx":
|
|
1037
|
+
case "recover":
|
|
1038
|
+
return deriveExpected(def.parser);
|
|
1039
|
+
case "skip":
|
|
1040
|
+
return deriveExpected(def.main);
|
|
1041
|
+
case "expect":
|
|
1042
|
+
return def.expected;
|
|
1023
1043
|
case "node":
|
|
1024
1044
|
case "grammar":
|
|
1025
1045
|
case "trivia":
|
|
@@ -1080,7 +1100,7 @@ function choice(...args) {
|
|
|
1080
1100
|
disjoint
|
|
1081
1101
|
};
|
|
1082
1102
|
const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
|
|
1083
|
-
const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
|
|
1103
|
+
const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
|
|
1084
1104
|
let greedyLitMap = null;
|
|
1085
1105
|
let sortedParsers = null;
|
|
1086
1106
|
const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
|
|
@@ -1206,8 +1226,47 @@ function detectStrategy(parsers) {
|
|
|
1206
1226
|
);
|
|
1207
1227
|
return { tag: "literalsLongestFirst", sortedIndices };
|
|
1208
1228
|
}
|
|
1229
|
+
const shared = detectSharedPrefix(parsers);
|
|
1230
|
+
if (shared !== null) return shared;
|
|
1209
1231
|
return { tag: "firstMatch" };
|
|
1210
1232
|
}
|
|
1233
|
+
function bareLeadingTermKey(term) {
|
|
1234
|
+
const d = term._def;
|
|
1235
|
+
if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
|
|
1236
|
+
if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
function detectSharedPrefix(parsers) {
|
|
1240
|
+
if (parsers.length < 2) return null;
|
|
1241
|
+
let key = null;
|
|
1242
|
+
let prefix = null;
|
|
1243
|
+
const members = [];
|
|
1244
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
1245
|
+
const term0 = leadingTermOfArm(parsers[i]);
|
|
1246
|
+
if (term0 === null) return null;
|
|
1247
|
+
const k = bareLeadingTermKey(term0);
|
|
1248
|
+
if (k === null) return null;
|
|
1249
|
+
if (key === null) {
|
|
1250
|
+
key = k;
|
|
1251
|
+
prefix = term0;
|
|
1252
|
+
} else if (k !== key) return null;
|
|
1253
|
+
members.push(i);
|
|
1254
|
+
}
|
|
1255
|
+
if (prefix === null || members.length < 2) return null;
|
|
1256
|
+
return { tag: "sharedPrefix", prefix, members };
|
|
1257
|
+
}
|
|
1258
|
+
function leadingTermOfArm(arm) {
|
|
1259
|
+
let d = arm._def;
|
|
1260
|
+
for (; ; ) {
|
|
1261
|
+
if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
|
|
1262
|
+
d = d.parser._def;
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
if (d.tag !== "sequence" || d.parsers.length < 2) return null;
|
|
1268
|
+
return d.parsers[0];
|
|
1269
|
+
}
|
|
1211
1270
|
function computeAutoNot(parsers) {
|
|
1212
1271
|
return parsers.map((p, i) => {
|
|
1213
1272
|
const litVal = getCoreLiteralValue(p);
|
|
@@ -1801,11 +1860,20 @@ function sequence(...parsers) {
|
|
|
1801
1860
|
|
|
1802
1861
|
// src/combinators/attempt.ts
|
|
1803
1862
|
function attempt(parser2) {
|
|
1863
|
+
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
1864
|
+
let failExpected;
|
|
1804
1865
|
return {
|
|
1805
1866
|
_tag: "attempt",
|
|
1806
1867
|
_meta: parser2._meta,
|
|
1807
1868
|
_def: { tag: "attempt", parser: parser2 },
|
|
1808
1869
|
parse(input, pos, ctx) {
|
|
1870
|
+
if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
|
|
1871
|
+
if (failExpected === void 0) {
|
|
1872
|
+
const e = deriveExpected(parser2);
|
|
1873
|
+
failExpected = e.length > 0 ? e : [parser2._tag];
|
|
1874
|
+
}
|
|
1875
|
+
return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
|
|
1876
|
+
}
|
|
1809
1877
|
const mark = saveCstMark(ctx);
|
|
1810
1878
|
const log = ctx._triviaLog?.length;
|
|
1811
1879
|
const result = parser2.parse(input, pos, ctx);
|
|
@@ -1818,7 +1886,7 @@ function attempt(parser2) {
|
|
|
1818
1886
|
}
|
|
1819
1887
|
|
|
1820
1888
|
// src/combinators/repeat.ts
|
|
1821
|
-
function repItem(combinator, input, cur, ctx) {
|
|
1889
|
+
function repItem(combinator, input, cur, ctx, guardable) {
|
|
1822
1890
|
const mark = saveTriviaMark(ctx);
|
|
1823
1891
|
let pos = cur;
|
|
1824
1892
|
if (ctx.trivia) {
|
|
@@ -1834,6 +1902,10 @@ function repItem(combinator, input, cur, ctx) {
|
|
|
1834
1902
|
rollbackTrivia(ctx, mark);
|
|
1835
1903
|
return "stop";
|
|
1836
1904
|
}
|
|
1905
|
+
if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
|
|
1906
|
+
rollbackTrivia(ctx, mark);
|
|
1907
|
+
return "stop";
|
|
1908
|
+
}
|
|
1837
1909
|
const result = combinator.parse(input, pos, ctx);
|
|
1838
1910
|
if (!result.ok) {
|
|
1839
1911
|
rollbackTrivia(ctx, mark);
|
|
@@ -1853,6 +1925,7 @@ function many(combinator) {
|
|
|
1853
1925
|
};
|
|
1854
1926
|
const def = { tag: "many", parser: combinator, min: 0 };
|
|
1855
1927
|
let expected;
|
|
1928
|
+
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1856
1929
|
return {
|
|
1857
1930
|
_tag: "many",
|
|
1858
1931
|
_meta: meta,
|
|
@@ -1861,7 +1934,7 @@ function many(combinator) {
|
|
|
1861
1934
|
const values = def.valueUnused ? void 0 : [];
|
|
1862
1935
|
let cur = pos;
|
|
1863
1936
|
while (cur < input.length) {
|
|
1864
|
-
const item = repItem(combinator, input, cur, ctx);
|
|
1937
|
+
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1865
1938
|
if (item === "stop") break;
|
|
1866
1939
|
if ("fail" in item) {
|
|
1867
1940
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
@@ -1889,6 +1962,7 @@ function oneOrMore(combinator) {
|
|
|
1889
1962
|
};
|
|
1890
1963
|
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
1891
1964
|
let expected;
|
|
1965
|
+
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
1892
1966
|
return {
|
|
1893
1967
|
_tag: "oneOrMore",
|
|
1894
1968
|
_meta: meta,
|
|
@@ -1899,7 +1973,7 @@ function oneOrMore(combinator) {
|
|
|
1899
1973
|
const values = def.valueUnused ? void 0 : [first.value];
|
|
1900
1974
|
let cur = first.span.end;
|
|
1901
1975
|
while (cur < input.length) {
|
|
1902
|
-
const item = repItem(combinator, input, cur, ctx);
|
|
1976
|
+
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1903
1977
|
if (item === "stop") break;
|
|
1904
1978
|
if ("fail" in item) {
|
|
1905
1979
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
@@ -1942,15 +2016,6 @@ function optional(combinator) {
|
|
|
1942
2016
|
}
|
|
1943
2017
|
};
|
|
1944
2018
|
}
|
|
1945
|
-
function startsFirstSet(combinator, input, pos) {
|
|
1946
|
-
const fs2 = combinator._meta.firstSet;
|
|
1947
|
-
if (fs2.kind === "any") return true;
|
|
1948
|
-
if (fs2.kind === "empty") return false;
|
|
1949
|
-
const code = input.codePointAt(pos);
|
|
1950
|
-
if (code === void 0) return false;
|
|
1951
|
-
for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
1952
|
-
return false;
|
|
1953
|
-
}
|
|
1954
2019
|
function sepBy(combinator, separator) {
|
|
1955
2020
|
const meta = {
|
|
1956
2021
|
firstSet: combinator._meta.firstSet,
|
|
@@ -2447,11 +2512,20 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2447
2512
|
const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
|
|
2448
2513
|
const clonesState = build ? buildReadsState(def) : true;
|
|
2449
2514
|
const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
|
|
2515
|
+
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
2516
|
+
let failExpected;
|
|
2450
2517
|
return {
|
|
2451
2518
|
_tag: "node",
|
|
2452
2519
|
_meta: meta,
|
|
2453
2520
|
_def: def,
|
|
2454
2521
|
parse(input, pos, ctx) {
|
|
2522
|
+
if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
|
|
2523
|
+
if (failExpected === void 0) {
|
|
2524
|
+
const e = deriveExpected(combinator);
|
|
2525
|
+
failExpected = e.length > 0 ? e : [combinator._tag];
|
|
2526
|
+
}
|
|
2527
|
+
return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
|
|
2528
|
+
}
|
|
2455
2529
|
const saved = beginCstNodeCapture(ctx);
|
|
2456
2530
|
const savedFields = ctx._fields;
|
|
2457
2531
|
ctx._fields = capturesFields ? [] : void 0;
|
|
@@ -4447,6 +4521,7 @@ var SWITCH_RANGE_LIMIT = 4;
|
|
|
4447
4521
|
var SWITCH_MAX_CASES = 48;
|
|
4448
4522
|
var SWITCH_MIN_CASES = 3;
|
|
4449
4523
|
var _forceDisjointIf = false;
|
|
4524
|
+
var _forceNoSharedPrefix = false;
|
|
4450
4525
|
function planDisjointDispatch(parsers) {
|
|
4451
4526
|
const cases = [];
|
|
4452
4527
|
let total = 0;
|
|
@@ -5082,7 +5157,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, cov
|
|
|
5082
5157
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
5083
5158
|
return { stmts, valueVar: valV, endVar: endV };
|
|
5084
5159
|
}
|
|
5085
|
-
function emitFirstMatch(def, ctx, pos, coverageBase) {
|
|
5160
|
+
function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
5086
5161
|
const resValV = v(ctx, "_crv");
|
|
5087
5162
|
const resEndV = v(ctx, "_cre");
|
|
5088
5163
|
const resOkV = v(ctx, "_crok");
|
|
@@ -5096,6 +5171,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
|
|
|
5096
5171
|
...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
|
|
5097
5172
|
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
5098
5173
|
];
|
|
5174
|
+
if (preStmts) stmts.push(...preStmts);
|
|
5099
5175
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
5100
5176
|
const p = def.parsers[i];
|
|
5101
5177
|
const gate = def.gates[i];
|
|
@@ -5103,11 +5179,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
|
|
|
5103
5179
|
const atStart = failsAtStart(p);
|
|
5104
5180
|
const staticFx = armStaticExpected(ctx, p);
|
|
5105
5181
|
const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
|
|
5106
|
-
const
|
|
5182
|
+
const baseFsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
|
|
5107
5183
|
if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
|
|
5108
5184
|
const deep = firstSetOf(p);
|
|
5109
5185
|
return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
|
|
5110
5186
|
})();
|
|
5187
|
+
const fsGuard = armGuardPrefix === void 0 ? baseFsGuard : baseFsGuard === null ? armGuardPrefix : `${armGuardPrefix} && (${baseFsGuard})`;
|
|
5111
5188
|
let gateCond = null;
|
|
5112
5189
|
if (gate) {
|
|
5113
5190
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -5176,11 +5253,64 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
|
|
|
5176
5253
|
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
5177
5254
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
5178
5255
|
}
|
|
5256
|
+
function emitsAsNamedFn(p, ctx) {
|
|
5257
|
+
if (p._def.tag === "lazy") return true;
|
|
5258
|
+
if (ctx.ruleNames?.has(p)) return true;
|
|
5259
|
+
if (ctx.namedParsers.has(p)) return true;
|
|
5260
|
+
const usage = ctx.lazyUsage;
|
|
5261
|
+
return !!usage && !ctx.noHoist && !ctx.capAsTrivia && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && (usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE;
|
|
5262
|
+
}
|
|
5263
|
+
function armReplayInScope(arm, ctx) {
|
|
5264
|
+
let p = arm;
|
|
5265
|
+
for (; ; ) {
|
|
5266
|
+
if (emitsAsNamedFn(p, ctx)) return false;
|
|
5267
|
+
const d = p._def;
|
|
5268
|
+
if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
|
|
5269
|
+
p = d.parser;
|
|
5270
|
+
continue;
|
|
5271
|
+
}
|
|
5272
|
+
return d.tag === "sequence";
|
|
5273
|
+
}
|
|
5274
|
+
}
|
|
5275
|
+
function emitSharedPrefix(def, strategy, ctx, pos) {
|
|
5276
|
+
const { prefix, members } = strategy;
|
|
5277
|
+
const savedCapturing = ctx.capturing;
|
|
5278
|
+
ctx.capturing = false;
|
|
5279
|
+
const pfx = emitFallible(prefix, ctx, pos, false);
|
|
5280
|
+
ctx.capturing = savedCapturing;
|
|
5281
|
+
const map = ctx.replayPrefix ??= /* @__PURE__ */ new Map();
|
|
5282
|
+
const keys = [];
|
|
5283
|
+
for (const i of members) {
|
|
5284
|
+
const term0 = leadingTermOfArm(def.parsers[i]);
|
|
5285
|
+
if (term0 === null) continue;
|
|
5286
|
+
map.set(term0, { valVar: pfx.valVar, endVar: pfx.endVar });
|
|
5287
|
+
keys.push(term0);
|
|
5288
|
+
}
|
|
5289
|
+
const r = emitFirstMatch(def, ctx, pos, void 0, pfx.stmts, pfx.okVar);
|
|
5290
|
+
for (const k of keys) map.delete(k);
|
|
5291
|
+
return r;
|
|
5292
|
+
}
|
|
5293
|
+
function emitReplayPrefixLeaf(ctx, pos, replay) {
|
|
5294
|
+
const vv = v(ctx);
|
|
5295
|
+
return {
|
|
5296
|
+
stmts: [
|
|
5297
|
+
`${ind(ctx)}const ${vv} = ${replay.valVar}`,
|
|
5298
|
+
...emitLeafCapture(ctx, vv, pos, replay.endVar)
|
|
5299
|
+
],
|
|
5300
|
+
valueVar: vv,
|
|
5301
|
+
endVar: replay.endVar
|
|
5302
|
+
};
|
|
5303
|
+
}
|
|
5179
5304
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
|
|
5180
5305
|
if (strategy.tag === "greedyClassify")
|
|
5181
5306
|
return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
|
|
5182
5307
|
if (strategy.tag === "literalsLongestFirst")
|
|
5183
5308
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
|
|
5309
|
+
if (strategy.tag === "sharedPrefix") {
|
|
5310
|
+
if (!_forceNoSharedPrefix && coverageBase === void 0 && !ctx.recovery && strategy.members.every((i) => armReplayInScope(def.parsers[i], ctx)))
|
|
5311
|
+
return emitSharedPrefix(def, strategy, ctx, pos);
|
|
5312
|
+
return emitFirstMatch(def, ctx, pos, coverageBase);
|
|
5313
|
+
}
|
|
5184
5314
|
return emitFirstMatch(def, ctx, pos, coverageBase);
|
|
5185
5315
|
}
|
|
5186
5316
|
function pushMapFn(ctx, fn, src) {
|
|
@@ -5894,6 +6024,8 @@ function emitExpect(def, ctx, pos) {
|
|
|
5894
6024
|
return { stmts, valueVar: valVar, endVar };
|
|
5895
6025
|
}
|
|
5896
6026
|
function emit(p, ctx, pos) {
|
|
6027
|
+
const replay = ctx.replayPrefix?.get(p);
|
|
6028
|
+
if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
|
|
5897
6029
|
const dispatch = (emissionPos = pos) => {
|
|
5898
6030
|
const savedRule = ctx.activeCoverageRuleId;
|
|
5899
6031
|
const rule = ctx.coverage?.plan.rules.get(p);
|