parseman 0.38.0 → 0.39.1
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/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/combinators/attempt.d.ts.map +1 -1
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +53 -0
- package/dist/combinators/dispatch.d.ts.map +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts +2 -1
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/index.cjs +772 -33
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +772 -33
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs.map +2 -2
- package/dist/language-service/index.js.map +2 -2
- package/dist/plugin/evaluator.d.ts +6 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +840 -33
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +840 -33
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/index.cjs +294 -1
- package/dist/spec/index.cjs.map +4 -4
- package/dist/spec/index.js +294 -1
- package/dist/spec/index.js.map +4 -4
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/plugin/index.js
CHANGED
|
@@ -59,6 +59,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
59
59
|
return d.value.length === 0;
|
|
60
60
|
case "keywords":
|
|
61
61
|
return false;
|
|
62
|
+
case "routed":
|
|
63
|
+
return false;
|
|
62
64
|
case "regex":
|
|
63
65
|
try {
|
|
64
66
|
const m = new RegExp(d.source).exec("");
|
|
@@ -85,6 +87,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
85
87
|
return d.parsers.every(me);
|
|
86
88
|
case "choice":
|
|
87
89
|
return d.parsers.some(me);
|
|
90
|
+
case "dispatch":
|
|
91
|
+
return me(d.selector);
|
|
88
92
|
case "transform":
|
|
89
93
|
case "label":
|
|
90
94
|
case "trivia":
|
|
@@ -158,6 +162,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
158
162
|
case "keywords":
|
|
159
163
|
return p._meta.firstSet;
|
|
160
164
|
// terminals: no refs, cached set is exact
|
|
165
|
+
case "dispatch":
|
|
166
|
+
return fs2(d.selector);
|
|
161
167
|
case "lazy":
|
|
162
168
|
try {
|
|
163
169
|
return fs2(d.thunk());
|
|
@@ -1106,6 +1112,8 @@ function derive(c, seen) {
|
|
|
1106
1112
|
return [def.label];
|
|
1107
1113
|
case "choice":
|
|
1108
1114
|
return def.parsers.flatMap(deriveExpected2);
|
|
1115
|
+
case "dispatch":
|
|
1116
|
+
return deriveExpected2(def.selector);
|
|
1109
1117
|
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
1110
1118
|
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
1111
1119
|
// able to fail first — deriving from term 0 alone named a token the parse
|
|
@@ -1145,6 +1153,8 @@ function derive(c, seen) {
|
|
|
1145
1153
|
case "not":
|
|
1146
1154
|
case "peek":
|
|
1147
1155
|
return deriveExpected2(def.parser);
|
|
1156
|
+
case "routed":
|
|
1157
|
+
return ["routed()"];
|
|
1148
1158
|
case "lazy": {
|
|
1149
1159
|
const name = c._ruleName;
|
|
1150
1160
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -1244,6 +1254,7 @@ function choice(...args) {
|
|
|
1244
1254
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
1245
1255
|
if (result.ok) return result;
|
|
1246
1256
|
expected.push(...result.expected);
|
|
1257
|
+
if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
|
|
1247
1258
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
1248
1259
|
}
|
|
1249
1260
|
return {
|
|
@@ -1284,6 +1295,7 @@ function choice(...args) {
|
|
|
1284
1295
|
rollbackCstCapture(ctx, mark);
|
|
1285
1296
|
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1286
1297
|
expected.push(...result.expected);
|
|
1298
|
+
if (result.committed) return { ok: false, expected, span: result.span, committed: true };
|
|
1287
1299
|
continue;
|
|
1288
1300
|
}
|
|
1289
1301
|
const checks = autoNot[i];
|
|
@@ -1963,6 +1975,245 @@ function sequence(...parsers) {
|
|
|
1963
1975
|
};
|
|
1964
1976
|
}
|
|
1965
1977
|
|
|
1978
|
+
// src/combinators/dispatch.ts
|
|
1979
|
+
function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
1980
|
+
if (seen.has(parser2)) return false;
|
|
1981
|
+
seen.add(parser2);
|
|
1982
|
+
const def = parser2._def;
|
|
1983
|
+
switch (def.tag) {
|
|
1984
|
+
case "routed":
|
|
1985
|
+
return true;
|
|
1986
|
+
case "sequence":
|
|
1987
|
+
case "choice":
|
|
1988
|
+
return def.parsers.some((entry) => parserUsesRouted(entry, seen));
|
|
1989
|
+
case "dispatch":
|
|
1990
|
+
return false;
|
|
1991
|
+
case "sepBy":
|
|
1992
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.separator, seen);
|
|
1993
|
+
case "skip":
|
|
1994
|
+
return parserUsesRouted(def.main, seen) || parserUsesRouted(def.skipped, seen);
|
|
1995
|
+
case "grammar":
|
|
1996
|
+
return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
|
|
1997
|
+
case "scanTo":
|
|
1998
|
+
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
|
|
1999
|
+
case "recover":
|
|
2000
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
2001
|
+
case "lazy":
|
|
2002
|
+
try {
|
|
2003
|
+
return parserUsesRouted(def.thunk(), seen);
|
|
2004
|
+
} catch {
|
|
2005
|
+
return false;
|
|
2006
|
+
}
|
|
2007
|
+
case "many":
|
|
2008
|
+
case "oneOrMore":
|
|
2009
|
+
case "optional":
|
|
2010
|
+
case "attempt":
|
|
2011
|
+
case "transform":
|
|
2012
|
+
case "trivia":
|
|
2013
|
+
case "token":
|
|
2014
|
+
case "leaf":
|
|
2015
|
+
case "label":
|
|
2016
|
+
case "field":
|
|
2017
|
+
case "not":
|
|
2018
|
+
case "peek":
|
|
2019
|
+
case "node":
|
|
2020
|
+
case "withCtx":
|
|
2021
|
+
case "expect":
|
|
2022
|
+
return parserUsesRouted(def.parser, seen);
|
|
2023
|
+
default:
|
|
2024
|
+
return false;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
function branchUsesRouted(branch) {
|
|
2028
|
+
return branch.usesRouted === true || parserUsesRouted(branch.parser);
|
|
2029
|
+
}
|
|
2030
|
+
function asciiFoldKey(key) {
|
|
2031
|
+
let out = "";
|
|
2032
|
+
for (let i = 0; i < key.length; i++) {
|
|
2033
|
+
const c = key.charCodeAt(i);
|
|
2034
|
+
out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
2035
|
+
}
|
|
2036
|
+
return out;
|
|
2037
|
+
}
|
|
2038
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2039
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2040
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2041
|
+
switch (matcher.kind) {
|
|
2042
|
+
case "startsWith":
|
|
2043
|
+
return candidate.startsWith(expected);
|
|
2044
|
+
case "endsWith":
|
|
2045
|
+
return candidate.endsWith(expected);
|
|
2046
|
+
case "matches": {
|
|
2047
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2048
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
function isStringMatcher(value) {
|
|
2053
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2054
|
+
const kind = value.kind;
|
|
2055
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2056
|
+
}
|
|
2057
|
+
function fixedMatcher(kind, value) {
|
|
2058
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2059
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2060
|
+
return { kind, value };
|
|
2061
|
+
}
|
|
2062
|
+
function startsWith(prefix) {
|
|
2063
|
+
return fixedMatcher("startsWith", prefix);
|
|
2064
|
+
}
|
|
2065
|
+
function endsWith(suffix) {
|
|
2066
|
+
return fixedMatcher("endsWith", suffix);
|
|
2067
|
+
}
|
|
2068
|
+
function matches(pattern) {
|
|
2069
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2070
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2071
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2072
|
+
}
|
|
2073
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2074
|
+
}
|
|
2075
|
+
function when(key, parser2, opts = {}) {
|
|
2076
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2077
|
+
if (isStringMatcher(key)) {
|
|
2078
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2079
|
+
}
|
|
2080
|
+
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2081
|
+
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2082
|
+
for (const item of keys) {
|
|
2083
|
+
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2084
|
+
}
|
|
2085
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2086
|
+
}
|
|
2087
|
+
function otherwise(parser2) {
|
|
2088
|
+
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2089
|
+
}
|
|
2090
|
+
function routed() {
|
|
2091
|
+
return {
|
|
2092
|
+
_tag: "routed",
|
|
2093
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
2094
|
+
_def: { tag: "routed" },
|
|
2095
|
+
parse(_input, pos, ctx) {
|
|
2096
|
+
const item = ctx._routed;
|
|
2097
|
+
if (item === void 0) {
|
|
2098
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2099
|
+
}
|
|
2100
|
+
if (pos !== item.span.start) {
|
|
2101
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2102
|
+
}
|
|
2103
|
+
if (cstCaptureActive(ctx)) {
|
|
2104
|
+
pushCstLeaf(ctx, { _tag: "leaf", value: item.value, span: item.span });
|
|
2105
|
+
}
|
|
2106
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
function dispatch(selector, ...arms) {
|
|
2111
|
+
if (parserUsesRouted(selector)) {
|
|
2112
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2113
|
+
}
|
|
2114
|
+
let fallback;
|
|
2115
|
+
let fallbackUsesRouted = false;
|
|
2116
|
+
const matchers = [];
|
|
2117
|
+
const cases = [];
|
|
2118
|
+
const seen = [];
|
|
2119
|
+
for (let i = 0; i < arms.length; i++) {
|
|
2120
|
+
const arm = arms[i];
|
|
2121
|
+
if (arm.kind === "otherwise") {
|
|
2122
|
+
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2123
|
+
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2124
|
+
fallback = arm.parser;
|
|
2125
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
2126
|
+
continue;
|
|
2127
|
+
}
|
|
2128
|
+
if (arm.kind === "whenMatcher") {
|
|
2129
|
+
const matcher = arm.matcher;
|
|
2130
|
+
matchers.push({
|
|
2131
|
+
kind: matcher.kind,
|
|
2132
|
+
value: matcher.value,
|
|
2133
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2134
|
+
parser: arm.parser,
|
|
2135
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2136
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2137
|
+
});
|
|
2138
|
+
continue;
|
|
2139
|
+
}
|
|
2140
|
+
for (const key of arm.keys) {
|
|
2141
|
+
const folded = asciiFoldKey(key);
|
|
2142
|
+
for (const prior of seen) {
|
|
2143
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2144
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2145
|
+
}
|
|
2146
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2147
|
+
}
|
|
2148
|
+
cases.push({
|
|
2149
|
+
keys: arm.keys,
|
|
2150
|
+
parser: arm.parser,
|
|
2151
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2152
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
const meta = {
|
|
2156
|
+
firstSet: selector._meta.firstSet,
|
|
2157
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2158
|
+
isTrivia: false
|
|
2159
|
+
};
|
|
2160
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2161
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2162
|
+
for (const entry of cases) {
|
|
2163
|
+
for (const key of entry.keys) {
|
|
2164
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2165
|
+
else byKey.set(key, entry);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
return {
|
|
2169
|
+
_tag: "dispatch",
|
|
2170
|
+
_meta: meta,
|
|
2171
|
+
_def: {
|
|
2172
|
+
tag: "dispatch",
|
|
2173
|
+
selector,
|
|
2174
|
+
cases,
|
|
2175
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2176
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
2177
|
+
},
|
|
2178
|
+
parse(input, pos, ctx) {
|
|
2179
|
+
if (parserUsesRouted(selector)) {
|
|
2180
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2181
|
+
}
|
|
2182
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
2183
|
+
const selected = selector.parse(input, pos, ctx);
|
|
2184
|
+
if (!selected.ok) return selected;
|
|
2185
|
+
const branch = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher)) ?? (fallback === void 0 ? void 0 : { keys: [], parser: fallback, caseInsensitive: false, usesRouted: fallbackUsesRouted });
|
|
2186
|
+
if (branch === void 0) {
|
|
2187
|
+
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2188
|
+
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2189
|
+
}
|
|
2190
|
+
const savedRouted = ctx._routed;
|
|
2191
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2192
|
+
let mark = saveTriviaMark(ctx);
|
|
2193
|
+
if (usesRouted) {
|
|
2194
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2195
|
+
mark = saveTriviaMark(ctx);
|
|
2196
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2197
|
+
}
|
|
2198
|
+
let result;
|
|
2199
|
+
try {
|
|
2200
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2201
|
+
} finally {
|
|
2202
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2203
|
+
}
|
|
2204
|
+
if (!result.ok) {
|
|
2205
|
+
rollbackTrivia(ctx, mark);
|
|
2206
|
+
return { ...result, committed: true };
|
|
2207
|
+
}
|
|
2208
|
+
return {
|
|
2209
|
+
ok: true,
|
|
2210
|
+
value: [selected.value, result.value],
|
|
2211
|
+
span: { start: pos, end: result.span.end }
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
|
|
1966
2217
|
// src/combinators/attempt.ts
|
|
1967
2218
|
function attempt(parser2) {
|
|
1968
2219
|
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
@@ -1985,6 +2236,7 @@ function attempt(parser2) {
|
|
|
1985
2236
|
if (result.ok) return result;
|
|
1986
2237
|
rollbackCstCapture(ctx, mark);
|
|
1987
2238
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
2239
|
+
if (result.committed) return result;
|
|
1988
2240
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
1989
2241
|
}
|
|
1990
2242
|
};
|
|
@@ -2014,6 +2266,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
2014
2266
|
const result = combinator.parse(input, pos, ctx);
|
|
2015
2267
|
if (!result.ok) {
|
|
2016
2268
|
rollbackTrivia(ctx, mark);
|
|
2269
|
+
if (result.committed) return { fail: result, failPos: pos };
|
|
2017
2270
|
return { fail: result, failPos: pos };
|
|
2018
2271
|
}
|
|
2019
2272
|
if (result.span.end === pos) {
|
|
@@ -2057,6 +2310,7 @@ function many(combinator, opts = {}) {
|
|
|
2057
2310
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2058
2311
|
if (item === "stop") break;
|
|
2059
2312
|
if ("fail" in item) {
|
|
2313
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
2060
2314
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
2061
2315
|
if (sync === void 0) break;
|
|
2062
2316
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -2101,6 +2355,7 @@ function atLeast(combinator, min, max) {
|
|
|
2101
2355
|
while (count < min) {
|
|
2102
2356
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2103
2357
|
if (item === "stop" || "fail" in item) {
|
|
2358
|
+
if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
|
|
2104
2359
|
expected ??= deriveExpected(combinator);
|
|
2105
2360
|
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
2106
2361
|
}
|
|
@@ -2113,6 +2368,7 @@ function atLeast(combinator, min, max) {
|
|
|
2113
2368
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2114
2369
|
if (item === "stop") break;
|
|
2115
2370
|
if ("fail" in item) {
|
|
2371
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
2116
2372
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
2117
2373
|
if (sync === void 0) break;
|
|
2118
2374
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -2151,6 +2407,7 @@ function optional(combinator) {
|
|
|
2151
2407
|
const result = combinator.parse(input, pos, ctx);
|
|
2152
2408
|
if (result.ok) return result;
|
|
2153
2409
|
rollbackTrivia(ctx, mark);
|
|
2410
|
+
if (result.committed) return result;
|
|
2154
2411
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
2155
2412
|
}
|
|
2156
2413
|
};
|
|
@@ -2190,6 +2447,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2190
2447
|
values.push(first.value);
|
|
2191
2448
|
cur = first.span.end;
|
|
2192
2449
|
} else {
|
|
2450
|
+
if (first.committed) return first;
|
|
2193
2451
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2194
2452
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2195
2453
|
if (min >= 1) return failAt2(pos);
|
|
@@ -2217,6 +2475,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2217
2475
|
const sep2 = separator.parse(input, sepPos, ctx);
|
|
2218
2476
|
if (!sep2.ok) {
|
|
2219
2477
|
rollbackTrivia(ctx, loopMark);
|
|
2478
|
+
if (sep2.committed) return sep2;
|
|
2220
2479
|
break;
|
|
2221
2480
|
}
|
|
2222
2481
|
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
@@ -2232,6 +2491,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2232
2491
|
}
|
|
2233
2492
|
const next = combinator.parse(input, nextPos, ctx);
|
|
2234
2493
|
if (!next.ok) {
|
|
2494
|
+
if (next.committed) return next;
|
|
2235
2495
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2236
2496
|
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
2237
2497
|
expected ??= deriveExpected(combinator);
|
|
@@ -2287,6 +2547,12 @@ function markUnusedValues(root) {
|
|
|
2287
2547
|
case "choice":
|
|
2288
2548
|
for (const p of def.parsers) visit(p, consumed);
|
|
2289
2549
|
return;
|
|
2550
|
+
case "dispatch":
|
|
2551
|
+
visit(def.selector, consumed);
|
|
2552
|
+
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
2553
|
+
if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
|
|
2554
|
+
if (def.otherwise) visit(def.otherwise, consumed);
|
|
2555
|
+
return;
|
|
2290
2556
|
case "node":
|
|
2291
2557
|
visit(def.parser, false);
|
|
2292
2558
|
return;
|
|
@@ -2562,6 +2828,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2562
2828
|
case "sequence":
|
|
2563
2829
|
case "choice":
|
|
2564
2830
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2831
|
+
case "dispatch":
|
|
2832
|
+
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasOwnFields(entry.parser, seen)) : false) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2565
2833
|
case "sepBy":
|
|
2566
2834
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2567
2835
|
case "skip":
|
|
@@ -2629,6 +2897,8 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2629
2897
|
return parserHasTriviaSite(d.parser, seen);
|
|
2630
2898
|
case "choice":
|
|
2631
2899
|
return d.parsers.some((x) => parserHasTriviaSite(x, seen));
|
|
2900
|
+
case "dispatch":
|
|
2901
|
+
return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasTriviaSite(entry.parser, seen)) : false) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
|
|
2632
2902
|
case "grammar":
|
|
2633
2903
|
return parserHasTriviaSite(d.parser, seen);
|
|
2634
2904
|
case "lazy": {
|
|
@@ -2654,6 +2924,8 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2654
2924
|
case "sequence":
|
|
2655
2925
|
case "choice":
|
|
2656
2926
|
return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
|
|
2927
|
+
case "dispatch":
|
|
2928
|
+
return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserEnablesTriviaCapture(entry.parser, seen)) : false) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
|
|
2657
2929
|
case "sepBy":
|
|
2658
2930
|
return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
|
|
2659
2931
|
case "skip":
|
|
@@ -3028,7 +3300,7 @@ function leaf(root, fn) {
|
|
|
3028
3300
|
}
|
|
3029
3301
|
|
|
3030
3302
|
// src/version.ts
|
|
3031
|
-
var PARSEMAN_VERSION = "0.
|
|
3303
|
+
var PARSEMAN_VERSION = "0.39.1";
|
|
3032
3304
|
|
|
3033
3305
|
// src/cst/host-mode.ts
|
|
3034
3306
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3184,6 +3456,8 @@ function classifyBroadArm(arm, resolve2) {
|
|
|
3184
3456
|
}
|
|
3185
3457
|
return null;
|
|
3186
3458
|
}
|
|
3459
|
+
case "dispatch":
|
|
3460
|
+
return walk2(d.selector);
|
|
3187
3461
|
default:
|
|
3188
3462
|
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
3189
3463
|
}
|
|
@@ -3283,6 +3557,12 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3283
3557
|
if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
|
|
3284
3558
|
for (const k of ["parser", "main", "skipped", "separator", "sentinel"])
|
|
3285
3559
|
if (rec[k]) kids.push(rec[k]);
|
|
3560
|
+
if (d.tag === "dispatch") {
|
|
3561
|
+
kids.push(d.selector);
|
|
3562
|
+
for (const c of d.cases) kids.push(c.parser);
|
|
3563
|
+
if (d.matchers) for (const c of d.matchers) kids.push(c.parser);
|
|
3564
|
+
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3565
|
+
}
|
|
3286
3566
|
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
3287
3567
|
if (d.tag === "lazy") {
|
|
3288
3568
|
try {
|
|
@@ -3389,6 +3669,12 @@ function childrenOf(d) {
|
|
|
3389
3669
|
case "sequence":
|
|
3390
3670
|
case "choice":
|
|
3391
3671
|
return d.parsers;
|
|
3672
|
+
case "dispatch":
|
|
3673
|
+
return [
|
|
3674
|
+
d.selector,
|
|
3675
|
+
...d.cases.map((c) => c.parser),
|
|
3676
|
+
...d.otherwise === void 0 ? [] : [d.otherwise]
|
|
3677
|
+
];
|
|
3392
3678
|
case "skip":
|
|
3393
3679
|
return [d.main, d.skipped];
|
|
3394
3680
|
case "sepBy":
|
|
@@ -3445,6 +3731,8 @@ function payloadKey(p, d) {
|
|
|
3445
3731
|
return `${d.tag}\0${d.min}\0${d.max ?? ""}`;
|
|
3446
3732
|
case "sepBy":
|
|
3447
3733
|
return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
|
|
3734
|
+
case "dispatch":
|
|
3735
|
+
return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
|
|
3448
3736
|
case "expect":
|
|
3449
3737
|
return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
|
|
3450
3738
|
case "scanTo":
|
|
@@ -3516,6 +3804,11 @@ function render(p, depth = 3) {
|
|
|
3516
3804
|
return `sequence(${kids.join(", ")})`;
|
|
3517
3805
|
case "choice":
|
|
3518
3806
|
return `choice(${kids.join(", ")})`;
|
|
3807
|
+
case "dispatch": {
|
|
3808
|
+
const arms = d.cases.map((c, i) => `when([${c.keys.map((k) => JSON.stringify(k)).join(", ")}], ${kids[i + 1] ?? ""})`);
|
|
3809
|
+
if (d.otherwise !== void 0) arms.push(`otherwise(${kids[kids.length - 1] ?? ""})`);
|
|
3810
|
+
return `dispatch(${kids[0] ?? ""}, ${arms.join(", ")})`;
|
|
3811
|
+
}
|
|
3519
3812
|
case "node":
|
|
3520
3813
|
return `node(${d.type ? `'${d.type}', ` : ""}${kids[0] ?? ""})`;
|
|
3521
3814
|
case "label":
|
|
@@ -3539,6 +3832,10 @@ function segment(d, index) {
|
|
|
3539
3832
|
return `seq[${index}]`;
|
|
3540
3833
|
case "choice":
|
|
3541
3834
|
return `choice[${index}]`;
|
|
3835
|
+
case "dispatch":
|
|
3836
|
+
if (index === 0) return "dispatch.selector";
|
|
3837
|
+
if (index <= d.cases.length) return `dispatch.when[${index - 1}]`;
|
|
3838
|
+
return "dispatch.otherwise";
|
|
3542
3839
|
case "node":
|
|
3543
3840
|
return `node(${d.type ?? ""})`;
|
|
3544
3841
|
case "label":
|
|
@@ -4787,11 +5084,25 @@ function escapeClassChar(ch2) {
|
|
|
4787
5084
|
}
|
|
4788
5085
|
|
|
4789
5086
|
// src/compiler/grammar-coverage-ids.ts
|
|
5087
|
+
function idPart(value) {
|
|
5088
|
+
return encodeURIComponent(value);
|
|
5089
|
+
}
|
|
5090
|
+
function dispatchArmIds(def, path2) {
|
|
5091
|
+
const ids = [];
|
|
5092
|
+
for (const entry of def.cases) ids.push(`dispatch:${path2}/when:${entry.keys.map(idPart).join("|")}`);
|
|
5093
|
+
for (const entry of def.matchers ?? []) {
|
|
5094
|
+
ids.push(`dispatch:${path2}/matcher:${entry.kind}:${idPart(entry.value)}${entry.kind === "matches" && entry.flags ? `:${idPart(entry.flags)}` : ""}`);
|
|
5095
|
+
}
|
|
5096
|
+
if (def.otherwise) ids.push(`dispatch:${path2}/otherwise`);
|
|
5097
|
+
return ids;
|
|
5098
|
+
}
|
|
4790
5099
|
function children(def, winners) {
|
|
4791
5100
|
switch (def.tag) {
|
|
4792
5101
|
case "sequence":
|
|
4793
5102
|
case "choice":
|
|
4794
5103
|
return def.parsers;
|
|
5104
|
+
case "dispatch":
|
|
5105
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
4795
5106
|
case "many":
|
|
4796
5107
|
case "oneOrMore":
|
|
4797
5108
|
case "optional":
|
|
@@ -4834,6 +5145,7 @@ function children(def, winners) {
|
|
|
4834
5145
|
function buildGrammarPlan(entry, winners) {
|
|
4835
5146
|
const definitions = /* @__PURE__ */ new Map();
|
|
4836
5147
|
const choices = /* @__PURE__ */ new WeakMap();
|
|
5148
|
+
const dispatches = /* @__PURE__ */ new WeakMap();
|
|
4837
5149
|
const attempts = /* @__PURE__ */ new WeakMap();
|
|
4838
5150
|
const labels = /* @__PURE__ */ new WeakMap();
|
|
4839
5151
|
const rules2 = /* @__PURE__ */ new WeakMap();
|
|
@@ -4864,6 +5176,11 @@ function buildGrammarPlan(entry, winners) {
|
|
|
4864
5176
|
choices.set(parser2, ids);
|
|
4865
5177
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
4866
5178
|
}
|
|
5179
|
+
if (parser2._def.tag === "dispatch") {
|
|
5180
|
+
const ids = dispatchArmIds(parser2._def, path2);
|
|
5181
|
+
dispatches.set(parser2, ids);
|
|
5182
|
+
ids.forEach((id) => definitions.set(id, { id, kind: "dispatch-arm" }));
|
|
5183
|
+
}
|
|
4867
5184
|
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path2}`);
|
|
4868
5185
|
if (parser2._def.tag === "label") {
|
|
4869
5186
|
const id = `label:${path2}`;
|
|
@@ -4876,7 +5193,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
4876
5193
|
for (const root of roots) {
|
|
4877
5194
|
visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
|
|
4878
5195
|
}
|
|
4879
|
-
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
|
|
5196
|
+
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
|
|
4880
5197
|
}
|
|
4881
5198
|
|
|
4882
5199
|
// src/compiler/scannable-run.ts
|
|
@@ -6250,12 +6567,15 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6250
6567
|
case "literal":
|
|
6251
6568
|
case "regex":
|
|
6252
6569
|
case "keywords":
|
|
6570
|
+
case "routed":
|
|
6253
6571
|
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
6254
6572
|
case "choice": {
|
|
6255
6573
|
const alts = [];
|
|
6256
6574
|
for (const arm of d.parsers) alts.push(...rec(arm).alts);
|
|
6257
6575
|
return { alts };
|
|
6258
6576
|
}
|
|
6577
|
+
case "dispatch":
|
|
6578
|
+
return rec(d.selector);
|
|
6259
6579
|
case "sequence": {
|
|
6260
6580
|
const chain = [];
|
|
6261
6581
|
for (const term of d.parsers) {
|
|
@@ -6338,6 +6658,9 @@ function failReturn(expected, posExpr) {
|
|
|
6338
6658
|
function failReturnArr(expectedArr, posExpr) {
|
|
6339
6659
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
6340
6660
|
}
|
|
6661
|
+
function committedReturnArr(expectedArr, posExpr) {
|
|
6662
|
+
return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
|
|
6663
|
+
}
|
|
6341
6664
|
function hoistExpected(ctx, constArrSource) {
|
|
6342
6665
|
let name = ctx.expectedMap.get(constArrSource);
|
|
6343
6666
|
if (name === void 0) {
|
|
@@ -6374,15 +6697,22 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
|
6374
6697
|
return probe + trace + failReturnArr(expectedArr, posExpr);
|
|
6375
6698
|
}
|
|
6376
6699
|
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
6377
|
-
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
6700
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
|
|
6701
|
+
}
|
|
6702
|
+
function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
|
|
6703
|
+
if (ctx.failLabel) {
|
|
6704
|
+
if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
|
|
6705
|
+
return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
|
|
6706
|
+
}
|
|
6707
|
+
return committedReturnArr(expectedArr, posExpr);
|
|
6378
6708
|
}
|
|
6379
6709
|
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
6380
6710
|
if (srcCtx !== "_ctx") {
|
|
6381
6711
|
if (ctx.failLabel) {
|
|
6382
6712
|
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
6383
|
-
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
6713
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
|
|
6384
6714
|
}
|
|
6385
|
-
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
6715
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
|
|
6386
6716
|
}
|
|
6387
6717
|
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
6388
6718
|
return resultFromRecorded();
|
|
@@ -6595,6 +6925,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6595
6925
|
// nothing committed remains.
|
|
6596
6926
|
case "choice":
|
|
6597
6927
|
return false;
|
|
6928
|
+
case "dispatch":
|
|
6929
|
+
return capturesLeaf(d.selector, seen) || d.cases.some((x) => mayLeavePartialCapture(x.parser, seen) || capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, seen) || capturesLeaf(x.parser, seen)) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, seen) || capturesLeaf(d.otherwise, seen) : false);
|
|
6598
6930
|
case "attempt":
|
|
6599
6931
|
return false;
|
|
6600
6932
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
@@ -6639,6 +6971,50 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6639
6971
|
return true;
|
|
6640
6972
|
}
|
|
6641
6973
|
}
|
|
6974
|
+
function mayCommitFailure(p, seen = /* @__PURE__ */ new Set()) {
|
|
6975
|
+
if (seen.has(p)) return false;
|
|
6976
|
+
seen.add(p);
|
|
6977
|
+
const d = p._def;
|
|
6978
|
+
switch (d.tag) {
|
|
6979
|
+
case "dispatch":
|
|
6980
|
+
return true;
|
|
6981
|
+
case "choice":
|
|
6982
|
+
case "sequence":
|
|
6983
|
+
return d.parsers.some((x) => mayCommitFailure(x, seen));
|
|
6984
|
+
case "many":
|
|
6985
|
+
case "oneOrMore":
|
|
6986
|
+
case "optional":
|
|
6987
|
+
case "attempt":
|
|
6988
|
+
case "transform":
|
|
6989
|
+
case "label":
|
|
6990
|
+
case "field":
|
|
6991
|
+
case "grammar":
|
|
6992
|
+
case "node":
|
|
6993
|
+
return mayCommitFailure(d.parser, seen);
|
|
6994
|
+
case "sepBy":
|
|
6995
|
+
return mayCommitFailure(d.parser, seen) || mayCommitFailure(d.separator, seen);
|
|
6996
|
+
case "skip":
|
|
6997
|
+
return mayCommitFailure(d.main, seen) || mayCommitFailure(d.skipped, seen);
|
|
6998
|
+
case "token":
|
|
6999
|
+
case "leaf":
|
|
7000
|
+
return mayCommitFailure(d.parser, seen);
|
|
7001
|
+
case "withCtx":
|
|
7002
|
+
return mayCommitFailure(d.parser, seen);
|
|
7003
|
+
case "scanTo":
|
|
7004
|
+
return mayCommitFailure(d.sentinel, seen) || d.skip.some((x) => mayCommitFailure(x, seen));
|
|
7005
|
+
case "lazy": {
|
|
7006
|
+
try {
|
|
7007
|
+
return mayCommitFailure(d.thunk(), seen);
|
|
7008
|
+
} catch {
|
|
7009
|
+
return true;
|
|
7010
|
+
}
|
|
7011
|
+
}
|
|
7012
|
+
case "unknown":
|
|
7013
|
+
return true;
|
|
7014
|
+
default:
|
|
7015
|
+
return false;
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
6642
7018
|
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
6643
7019
|
if (seen.has(p)) return false;
|
|
6644
7020
|
seen.add(p);
|
|
@@ -6649,6 +7025,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6649
7025
|
case "keywords":
|
|
6650
7026
|
case "node":
|
|
6651
7027
|
case "token":
|
|
7028
|
+
case "routed":
|
|
6652
7029
|
case "leaf":
|
|
6653
7030
|
return true;
|
|
6654
7031
|
case "not":
|
|
@@ -6660,6 +7037,8 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6660
7037
|
case "sequence":
|
|
6661
7038
|
case "choice":
|
|
6662
7039
|
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
7040
|
+
case "dispatch":
|
|
7041
|
+
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
6663
7042
|
case "sepBy":
|
|
6664
7043
|
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6665
7044
|
case "many":
|
|
@@ -6706,6 +7085,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
|
|
|
6706
7085
|
ctx.recordFail = savedRecord;
|
|
6707
7086
|
const ind0 = ind(ctx);
|
|
6708
7087
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
7088
|
+
const mayCommit = mayCommitFailure(inner);
|
|
6709
7089
|
const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
|
|
6710
7090
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
6711
7091
|
const mR = needsRollback ? v(ctx, "_fcr") : null;
|
|
@@ -6719,6 +7099,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
|
|
|
6719
7099
|
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
6720
7100
|
...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
|
|
6721
7101
|
] : [],
|
|
7102
|
+
...mayCommit ? [`${ind0}_ctx._fc = false`] : [],
|
|
6722
7103
|
`${ind0}${lbl}: {`,
|
|
6723
7104
|
...r.stmts,
|
|
6724
7105
|
`${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
|
|
@@ -6727,7 +7108,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
|
|
|
6727
7108
|
`${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
|
|
6728
7109
|
] : []
|
|
6729
7110
|
];
|
|
6730
|
-
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
7111
|
+
return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
|
|
6731
7112
|
}
|
|
6732
7113
|
var CHARCODE_CHAIN_MAX = 16;
|
|
6733
7114
|
function emitLit(def, ctx, pos) {
|
|
@@ -7104,6 +7485,186 @@ function emitChoice(parser2, def, ctx, pos) {
|
|
|
7104
7485
|
}
|
|
7105
7486
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
|
|
7106
7487
|
}
|
|
7488
|
+
function emitTailFallible(parser2, ctx, pos) {
|
|
7489
|
+
const savedRecord = ctx.recordFail;
|
|
7490
|
+
ctx.recordFail = true;
|
|
7491
|
+
const out = emitFallible(parser2, ctx, pos);
|
|
7492
|
+
ctx.recordFail = savedRecord;
|
|
7493
|
+
return out;
|
|
7494
|
+
}
|
|
7495
|
+
function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
7496
|
+
if (seen.has(p)) return false;
|
|
7497
|
+
seen.add(p);
|
|
7498
|
+
const d = p._def;
|
|
7499
|
+
switch (d.tag) {
|
|
7500
|
+
case "expect":
|
|
7501
|
+
case "recover":
|
|
7502
|
+
return true;
|
|
7503
|
+
case "many":
|
|
7504
|
+
case "oneOrMore":
|
|
7505
|
+
case "sepBy":
|
|
7506
|
+
return recovery || mayRecordRecoveryError(d.parser, recovery, seen);
|
|
7507
|
+
case "choice":
|
|
7508
|
+
case "sequence": {
|
|
7509
|
+
for (const item of d.parsers) {
|
|
7510
|
+
if (mayRecordRecoveryError(item, recovery, seen)) return true;
|
|
7511
|
+
}
|
|
7512
|
+
return false;
|
|
7513
|
+
}
|
|
7514
|
+
case "dispatch": {
|
|
7515
|
+
if (mayRecordRecoveryError(d.selector, recovery, seen)) return true;
|
|
7516
|
+
for (const item of d.cases) {
|
|
7517
|
+
if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
|
|
7518
|
+
}
|
|
7519
|
+
if (d.matchers?.some((item) => mayRecordRecoveryError(item.parser, recovery, seen))) return true;
|
|
7520
|
+
return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
|
|
7521
|
+
}
|
|
7522
|
+
case "attempt":
|
|
7523
|
+
case "transform":
|
|
7524
|
+
case "label":
|
|
7525
|
+
case "field":
|
|
7526
|
+
case "grammar":
|
|
7527
|
+
case "node":
|
|
7528
|
+
case "token":
|
|
7529
|
+
case "leaf":
|
|
7530
|
+
case "withCtx":
|
|
7531
|
+
case "optional":
|
|
7532
|
+
return mayRecordRecoveryError(d.parser, recovery, seen);
|
|
7533
|
+
case "skip":
|
|
7534
|
+
return mayRecordRecoveryError(d.main, recovery, seen) || mayRecordRecoveryError(d.skipped, recovery, seen);
|
|
7535
|
+
case "scanTo":
|
|
7536
|
+
if (mayRecordRecoveryError(d.sentinel, recovery, seen)) return true;
|
|
7537
|
+
for (const item of d.skip) {
|
|
7538
|
+
if (mayRecordRecoveryError(item, recovery, seen)) return true;
|
|
7539
|
+
}
|
|
7540
|
+
return false;
|
|
7541
|
+
case "lazy": {
|
|
7542
|
+
try {
|
|
7543
|
+
return mayRecordRecoveryError(d.thunk(), recovery, seen);
|
|
7544
|
+
} catch {
|
|
7545
|
+
return true;
|
|
7546
|
+
}
|
|
7547
|
+
}
|
|
7548
|
+
case "unknown":
|
|
7549
|
+
return true;
|
|
7550
|
+
default:
|
|
7551
|
+
return false;
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
function emitDispatchCombinator(parser2, def, ctx, pos) {
|
|
7555
|
+
if (parserUsesRouted2(def.selector)) {
|
|
7556
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
7557
|
+
}
|
|
7558
|
+
const coverageIds = ctx.coverage?.plan.dispatches.get(parser2) ?? [];
|
|
7559
|
+
const branchUsesRouted2 = (entry) => entry.usesRouted === true || parserUsesRouted2(entry.parser);
|
|
7560
|
+
const hasRoutedBranch = def.cases.some(branchUsesRouted2) || (def.matchers ? def.matchers.some(branchUsesRouted2) : false) || (def.otherwise ? def.otherwiseUsesRouted === true || parserUsesRouted2(def.otherwise) : false);
|
|
7561
|
+
const selectorLeafMark = hasRoutedBranch ? v(ctx, "_dsl") : null;
|
|
7562
|
+
const selectorRawMark = hasRoutedBranch ? v(ctx, "_dsr") : null;
|
|
7563
|
+
const selectorTriviaMark = hasRoutedBranch ? v(ctx, "_dst") : null;
|
|
7564
|
+
const selectorLogMark = hasRoutedBranch ? v(ctx, "_dsg") : null;
|
|
7565
|
+
const selectorFieldMark = hasRoutedBranch ? v(ctx, "_dsf") : null;
|
|
7566
|
+
const selectorErrorMark = hasRoutedBranch ? v(ctx, "_dse") : null;
|
|
7567
|
+
const selector = emit(def.selector, ctx, pos);
|
|
7568
|
+
const outV = v(ctx, "_dval");
|
|
7569
|
+
const outE = v(ctx, "_dend");
|
|
7570
|
+
const keyV = v(ctx, "_dkey");
|
|
7571
|
+
const stmts = [
|
|
7572
|
+
...selectorLeafMark ? [
|
|
7573
|
+
`${ind(ctx)}const ${selectorLeafMark} = _ctx._cstLeaves?.length ?? 0`,
|
|
7574
|
+
`${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
|
|
7575
|
+
`${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
7576
|
+
`${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
|
|
7577
|
+
`${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
|
|
7578
|
+
`${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
|
|
7579
|
+
] : [],
|
|
7580
|
+
...selector.stmts,
|
|
7581
|
+
`${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
|
|
7582
|
+
`${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
|
|
7583
|
+
];
|
|
7584
|
+
let wroteBranch = false;
|
|
7585
|
+
const emitSelectedTail = (entry, keyword, condition, coverageId) => {
|
|
7586
|
+
const parser3 = entry.parser;
|
|
7587
|
+
const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
|
|
7588
|
+
stmts.push(head);
|
|
7589
|
+
ctx.indent++;
|
|
7590
|
+
const usesRouted = branchUsesRouted2(entry);
|
|
7591
|
+
const routedV = usesRouted ? v(ctx, "_drt") : null;
|
|
7592
|
+
const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
|
|
7593
|
+
const errMark = mayError ? v(ctx, "_derr") : null;
|
|
7594
|
+
const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
|
|
7595
|
+
if (routedV) {
|
|
7596
|
+
stmts.push(
|
|
7597
|
+
`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`,
|
|
7598
|
+
...selectorLeafMark ? [
|
|
7599
|
+
`${ind(ctx)}if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${selectorLeafMark}) _ctx._cstLeaves.length = ${selectorLeafMark}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${selectorRawMark}) _ctx._cstRawChildren.length = ${selectorRawMark}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${selectorTriviaMark}) _ctx._cstTriviaLog.length = ${selectorTriviaMark}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${selectorLogMark}) _ctx._triviaLog.length = ${selectorLogMark}; if (_ctx._fields && _ctx._fields.length !== ${selectorFieldMark}) _ctx._fields.length = ${selectorFieldMark}; if (_ctx._errors && _ctx._errors.length !== ${selectorErrorMark}) _ctx._errors.length = ${selectorErrorMark}`
|
|
7600
|
+
] : []
|
|
7601
|
+
);
|
|
7602
|
+
}
|
|
7603
|
+
if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
|
|
7604
|
+
if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
|
|
7605
|
+
const tail = emitTailFallible(parser3, ctx, usesRouted ? pos : selector.endVar);
|
|
7606
|
+
stmts.push(...coverageAttempt(ctx, coverageId, pos));
|
|
7607
|
+
const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
|
|
7608
|
+
const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
|
|
7609
|
+
const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
|
|
7610
|
+
const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
|
|
7611
|
+
stmts.push(
|
|
7612
|
+
...tail.stmts,
|
|
7613
|
+
`${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
|
|
7614
|
+
...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
|
|
7615
|
+
`${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
|
|
7616
|
+
`${ind(ctx)}${outE} = ${tail.endVar}`,
|
|
7617
|
+
...coverageHit(ctx, coverageId, pos, tail.endVar)
|
|
7618
|
+
);
|
|
7619
|
+
ctx.indent--;
|
|
7620
|
+
stmts.push(`${ind(ctx)}}`);
|
|
7621
|
+
};
|
|
7622
|
+
let coverageIndex = 0;
|
|
7623
|
+
for (const entry of def.cases) {
|
|
7624
|
+
const condition = entry.keys.map((key) => emitDispatchKeyCondition(keyV, key, entry.caseInsensitive)).join(" || ");
|
|
7625
|
+
emitSelectedTail(entry, wroteBranch ? "else if" : "if", condition, coverageIds[coverageIndex++]);
|
|
7626
|
+
wroteBranch = true;
|
|
7627
|
+
}
|
|
7628
|
+
const emitMatcherTails = () => {
|
|
7629
|
+
for (const matcher of def.matchers ?? []) {
|
|
7630
|
+
emitSelectedTail(matcher, wroteBranch ? "else if" : "if", emitDispatchMatcherCondition(keyV, matcher), coverageIds[coverageIndex++]);
|
|
7631
|
+
wroteBranch = true;
|
|
7632
|
+
}
|
|
7633
|
+
};
|
|
7634
|
+
if (def.otherwise) {
|
|
7635
|
+
emitMatcherTails();
|
|
7636
|
+
const otherwiseEntry = { parser: def.otherwise, usesRouted: def.otherwiseUsesRouted };
|
|
7637
|
+
if (wroteBranch) emitSelectedTail(otherwiseEntry, "else", void 0, coverageIds[coverageIndex++]);
|
|
7638
|
+
else emitSelectedTail(otherwiseEntry, "if", "true", coverageIds[coverageIndex++]);
|
|
7639
|
+
} else {
|
|
7640
|
+
emitMatcherTails();
|
|
7641
|
+
const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
|
|
7642
|
+
const fail = failArrBody(ctx, expected, selector.endVar);
|
|
7643
|
+
stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
|
|
7644
|
+
}
|
|
7645
|
+
return { stmts, valueVar: outV, endVar: outE };
|
|
7646
|
+
}
|
|
7647
|
+
function emitDispatchKeyCondition(valueVar, key, caseInsensitive) {
|
|
7648
|
+
if (!caseInsensitive) return `${valueVar} === ${JSON.stringify(key)}`;
|
|
7649
|
+
const parts = [`${valueVar}.length === ${key.length}`];
|
|
7650
|
+
for (let i = 0; i < key.length; i++) {
|
|
7651
|
+
parts.push(foldEq(`${valueVar}.charCodeAt(${i})`, key.charCodeAt(i)));
|
|
7652
|
+
}
|
|
7653
|
+
return parts.join(" && ");
|
|
7654
|
+
}
|
|
7655
|
+
function emitDispatchMatcherCondition(valueVar, matcher) {
|
|
7656
|
+
if (matcher.kind === "matches") {
|
|
7657
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
7658
|
+
return `${new RegExp(matcher.value, flags).toString()}.test(${valueVar})`;
|
|
7659
|
+
}
|
|
7660
|
+
const text = matcher.value;
|
|
7661
|
+
const parts = [`${valueVar}.length >= ${text.length}`];
|
|
7662
|
+
const offset = matcher.kind === "endsWith" ? `${valueVar}.length - ${text.length}` : "0";
|
|
7663
|
+
for (let i = 0; i < text.length; i++) {
|
|
7664
|
+
parts.push(matcher.caseInsensitive ? foldEq(`${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`})`, text.charCodeAt(i)) : `${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`}) === ${text.charCodeAt(i)}`);
|
|
7665
|
+
}
|
|
7666
|
+
return parts.join(" && ");
|
|
7667
|
+
}
|
|
7107
7668
|
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
7108
7669
|
const superParser = def.parsers[superIndex];
|
|
7109
7670
|
const regexDef = getCoreRegexDef(superParser);
|
|
@@ -7225,12 +7786,16 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
7225
7786
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
7226
7787
|
const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
|
|
7227
7788
|
const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
|
|
7789
|
+
const armMayRecordError = mayRecordRecoveryError(p, !!ctx.recovery);
|
|
7228
7790
|
const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
|
|
7229
7791
|
const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
|
|
7230
7792
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
7231
7793
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
7232
7794
|
const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
|
|
7233
|
-
const
|
|
7795
|
+
const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
|
|
7796
|
+
const captureRollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
|
|
7797
|
+
const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
|
|
7798
|
+
const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
|
|
7234
7799
|
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
7235
7800
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
7236
7801
|
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
@@ -7245,7 +7810,9 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
7245
7810
|
...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
|
|
7246
7811
|
);
|
|
7247
7812
|
}
|
|
7248
|
-
|
|
7813
|
+
if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
|
|
7814
|
+
const arm = emitFallible(p, ctx, pos, atStart);
|
|
7815
|
+
const { stmts: armStmts, okVar, valVar, endVar } = arm;
|
|
7249
7816
|
stmts.push(...armStmts);
|
|
7250
7817
|
if (autoNot && autoNot.length > 0) {
|
|
7251
7818
|
const anCode = v(ctx, "_anc");
|
|
@@ -7269,10 +7836,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
7269
7836
|
stmts.push(`${ind(ctx)}}`);
|
|
7270
7837
|
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}; ${coverageBase === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} });`} }`);
|
|
7271
7838
|
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
7839
|
+
if (arm.mayCommit) stmts.push(`${ind(ctx)}if (!${resOkV} && _ctx._fc) ${committedFailBody(ctx)}`);
|
|
7272
7840
|
} else {
|
|
7273
7841
|
const rejectedFlush = autoRejectedV === void 0 ? "" : `; for (const _rejectedId of ${autoRejectedV}) { _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'failure', offset: ${pos} }); _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'backtrack', offset: ${pos} }) }`;
|
|
7274
7842
|
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true${coverageBase === void 0 ? "" : `${rejectedFlush}; _ctx._grammarCoverage?.(${JSON.stringify(`${coverageBase}${i}`)}); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'selected', offset: ${pos}, end: ${endVar} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'success', offset: ${pos}, end: ${endVar} })`} }`);
|
|
7275
7843
|
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""}${coverageBase === void 0 ? "" : `; _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} })`} }`);
|
|
7844
|
+
if (arm.mayCommit) stmts.push(`${ind(ctx)}if (!${resOkV} && _ctx._fc) ${committedFailBody(ctx)}`);
|
|
7276
7845
|
}
|
|
7277
7846
|
ctx.indent -= fsGuard ? 2 : 1;
|
|
7278
7847
|
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
@@ -7429,7 +7998,8 @@ function emitMany(def, ctx, pos) {
|
|
|
7429
7998
|
`${ind(ctx)}if (!(${firstSetCond(lcV, def.parser._meta.firstSet)})) { ${rollback}break }`
|
|
7430
7999
|
);
|
|
7431
8000
|
}
|
|
7432
|
-
const
|
|
8001
|
+
const iter = emitFallible(def.parser, ctx, itemPos, true);
|
|
8002
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = iter;
|
|
7433
8003
|
stmts.push(...iterStmts);
|
|
7434
8004
|
if (ctx.recovery) {
|
|
7435
8005
|
const rrV = v(ctx, "_rr");
|
|
@@ -7448,7 +8018,8 @@ function emitMany(def, ctx, pos) {
|
|
|
7448
8018
|
`${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
7449
8019
|
);
|
|
7450
8020
|
} else {
|
|
7451
|
-
stmts.push(`${ind(ctx)}if (!${iterOk}
|
|
8021
|
+
stmts.push(`${ind(ctx)}if (!${iterOk}) { ${rollback}${iter.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
8022
|
+
stmts.push(`${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
7452
8023
|
}
|
|
7453
8024
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
7454
8025
|
else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
|
|
@@ -7460,10 +8031,12 @@ function emitMany(def, ctx, pos) {
|
|
|
7460
8031
|
function emitOptional(def, ctx, pos) {
|
|
7461
8032
|
const valV = v(ctx, "_opt");
|
|
7462
8033
|
const endV = v(ctx, "_opte");
|
|
7463
|
-
const
|
|
8034
|
+
const inner = emitFallible(def.parser, ctx, pos, true);
|
|
8035
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = inner;
|
|
7464
8036
|
const ind0 = ind(ctx);
|
|
7465
8037
|
const stmts = [
|
|
7466
8038
|
...lblStmts,
|
|
8039
|
+
...inner.mayCommit ? [`${ind0}if (!${okVar} && _ctx._fc) ${committedFailBody(ctx)}`] : [],
|
|
7467
8040
|
`${ind0}const ${valV} = ${okVar} ? ${valVar} : null`,
|
|
7468
8041
|
`${ind0}const ${endV} = ${okVar} ? ${endVar} : ${pos}`
|
|
7469
8042
|
];
|
|
@@ -7495,7 +8068,7 @@ function emitAttempt(p, def, ctx, pos) {
|
|
|
7495
8068
|
...preGuard,
|
|
7496
8069
|
`${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
|
|
7497
8070
|
...inner.stmts,
|
|
7498
|
-
...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
|
|
8071
|
+
...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
|
|
7499
8072
|
],
|
|
7500
8073
|
valueVar: inner.valVar,
|
|
7501
8074
|
endVar: inner.endVar
|
|
@@ -7509,18 +8082,21 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7509
8082
|
const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
|
|
7510
8083
|
const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
|
|
7511
8084
|
const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
|
|
7512
|
-
const
|
|
8085
|
+
const first = emitFallible(def.parser, ctx, pos, true);
|
|
8086
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = first;
|
|
7513
8087
|
const stmts = [
|
|
7514
8088
|
`${ind(ctx)}const ${arrV} = []`,
|
|
7515
8089
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
7516
8090
|
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
7517
8091
|
...firstStmts
|
|
7518
8092
|
];
|
|
7519
|
-
const failItem = (nextOkVar, itemPosVar, breakStmt) => {
|
|
7520
|
-
if (
|
|
8093
|
+
const failItem = (nextOkVar, nextMayCommit, itemPosVar, breakStmt) => {
|
|
8094
|
+
const commit = nextMayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : "";
|
|
8095
|
+
if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) { ${commit}${breakStmt} }`];
|
|
7521
8096
|
const rr = v(ctx, "_rr");
|
|
7522
8097
|
return [
|
|
7523
8098
|
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
8099
|
+
...nextMayCommit ? [`${ind(ctx)} if (_ctx._fc) ${committedFailBody(ctx)}`] : [],
|
|
7524
8100
|
`${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
|
|
7525
8101
|
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
7526
8102
|
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
@@ -7549,6 +8125,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7549
8125
|
stmts.push(
|
|
7550
8126
|
`${ind(ctx)}let ${didV} = false`,
|
|
7551
8127
|
`${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
|
|
8128
|
+
...first.mayCommit ? [`${ind(ctx)}else if (_ctx._fc) ${committedFailBody(ctx)}`] : [],
|
|
7552
8129
|
`${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
|
|
7553
8130
|
`${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
|
|
7554
8131
|
`${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
|
|
@@ -7559,6 +8136,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7559
8136
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
7560
8137
|
ctx.indent++;
|
|
7561
8138
|
} else {
|
|
8139
|
+
if (first.mayCommit) stmts.push(`${ind(ctx)}if (!${firstOk} && _ctx._fc) ${committedFailBody(ctx)}`);
|
|
7562
8140
|
stmts.push(`${ind(ctx)}if (${firstOk}) {`);
|
|
7563
8141
|
ctx.indent++;
|
|
7564
8142
|
stmts.push(
|
|
@@ -7587,16 +8165,18 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7587
8165
|
);
|
|
7588
8166
|
sepAtPos = spV;
|
|
7589
8167
|
const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
|
|
7590
|
-
const
|
|
7591
|
-
stmts
|
|
8168
|
+
const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
8169
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
|
|
8170
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7592
8171
|
const post = postSepMarks();
|
|
7593
8172
|
stmts.push(...post.decl);
|
|
7594
8173
|
const npV = v(ctx, "_np");
|
|
7595
8174
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
7596
|
-
const
|
|
8175
|
+
const next = emitFallible(def.parser, ctx, npV, true);
|
|
8176
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
|
|
7597
8177
|
stmts.push(
|
|
7598
8178
|
...nextStmts,
|
|
7599
|
-
...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
8179
|
+
...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
7600
8180
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
7601
8181
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
7602
8182
|
);
|
|
@@ -7605,14 +8185,16 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7605
8185
|
const spV = v(ctx, "_sp");
|
|
7606
8186
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
7607
8187
|
sepAtPos = spV;
|
|
7608
|
-
const
|
|
7609
|
-
stmts
|
|
8188
|
+
const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
8189
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
|
|
8190
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7610
8191
|
const npV = v(ctx, "_np");
|
|
7611
8192
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
7612
|
-
const
|
|
8193
|
+
const next = emitFallible(def.parser, ctx, npV, true);
|
|
8194
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
|
|
7613
8195
|
stmts.push(
|
|
7614
8196
|
...nextStmts,
|
|
7615
|
-
...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
|
|
8197
|
+
...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, "", "")),
|
|
7616
8198
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
7617
8199
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
7618
8200
|
);
|
|
@@ -7626,15 +8208,17 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7626
8208
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
7627
8209
|
);
|
|
7628
8210
|
}
|
|
7629
|
-
const
|
|
7630
|
-
stmts
|
|
8211
|
+
const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
8212
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
|
|
8213
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7631
8214
|
const nextRb = markLv ? `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markRw}) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
7632
8215
|
const post = postSepMarks();
|
|
7633
8216
|
stmts.push(...post.decl);
|
|
7634
|
-
const
|
|
8217
|
+
const next = emitFallible(def.parser, ctx, sepEnd, true);
|
|
8218
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
|
|
7635
8219
|
stmts.push(
|
|
7636
8220
|
...nextStmts,
|
|
7637
|
-
...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
8221
|
+
...failItem(nextOk, next.mayCommit, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
7638
8222
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
7639
8223
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
7640
8224
|
);
|
|
@@ -7735,6 +8319,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
7735
8319
|
endVar: pos
|
|
7736
8320
|
};
|
|
7737
8321
|
}
|
|
8322
|
+
function emitRouted(ctx, pos) {
|
|
8323
|
+
const item = v(ctx, "_rt");
|
|
8324
|
+
return {
|
|
8325
|
+
stmts: [
|
|
8326
|
+
`${ind(ctx)}const ${item} = _ctx._routed`,
|
|
8327
|
+
...emitIfFail(ctx, `${item} === undefined || ${item}.span.start !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
8328
|
+
...emitLeafCapture(ctx, `${item}.value`, `${item}.span.start`, `${item}.span.end`)
|
|
8329
|
+
],
|
|
8330
|
+
valueVar: `${item}.value`,
|
|
8331
|
+
endVar: `${item}.span.end`
|
|
8332
|
+
};
|
|
8333
|
+
}
|
|
7738
8334
|
function escapeTokenLiteral(value) {
|
|
7739
8335
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
7740
8336
|
}
|
|
@@ -8015,7 +8611,7 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
8015
8611
|
const rv = v(ctx, "_rt");
|
|
8016
8612
|
const vv = v(ctx, "_rtv");
|
|
8017
8613
|
const ev = v(ctx, "_rte");
|
|
8018
|
-
const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span }`;
|
|
8614
|
+
const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; _ctx._fc = ${rv}.committed === true; break ${ctx.failLabel} }` : `{ _ctx._fc = ${rv}.committed === true; break ${ctx.failLabel} }` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span, ...(${rv}.committed ? { committed: true } : {}) }`;
|
|
8019
8615
|
const stmts = [
|
|
8020
8616
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
8021
8617
|
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
@@ -8096,7 +8692,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
8096
8692
|
return { stmts, valueVar: valVar, endVar };
|
|
8097
8693
|
}
|
|
8098
8694
|
function emitExpect(def, ctx, pos) {
|
|
8099
|
-
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
8695
|
+
const { stmts: innerStmts, okVar, valVar, endVar, mayCommit } = emitFallible(def.parser, ctx, pos);
|
|
8100
8696
|
const ind0 = ind(ctx);
|
|
8101
8697
|
const errV = v(ctx, "_err");
|
|
8102
8698
|
const stmts = [
|
|
@@ -8108,6 +8704,7 @@ function emitExpect(def, ctx, pos) {
|
|
|
8108
8704
|
// interpreter (expect.ts) and the list-recovery emit sites — guarded on
|
|
8109
8705
|
// `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
|
|
8110
8706
|
`${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
|
|
8707
|
+
...mayCommit ? [`${ind0} _ctx._fc = false`] : [],
|
|
8111
8708
|
`${ind0} ${valVar} = ${errV}`,
|
|
8112
8709
|
`${ind0} ${endVar} = ${pos}`,
|
|
8113
8710
|
`${ind0} ${okVar} = true`,
|
|
@@ -8118,7 +8715,7 @@ function emitExpect(def, ctx, pos) {
|
|
|
8118
8715
|
function emit(p, ctx, pos) {
|
|
8119
8716
|
const replay = ctx.replayPrefix?.get(p);
|
|
8120
8717
|
if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
|
|
8121
|
-
const
|
|
8718
|
+
const dispatch2 = (emissionPos = pos) => {
|
|
8122
8719
|
const savedRule = ctx.activeCoverageRuleId;
|
|
8123
8720
|
const rule = ctx.coverage?.plan.rules.get(p);
|
|
8124
8721
|
if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
|
|
@@ -8158,14 +8755,14 @@ function emit(p, ctx, pos) {
|
|
|
8158
8755
|
ctx.indent = 1;
|
|
8159
8756
|
ctx.failLabel = "_pfail";
|
|
8160
8757
|
ctx.recordFail = true;
|
|
8161
|
-
const r =
|
|
8758
|
+
const r = dispatch2("_pos");
|
|
8162
8759
|
ctx.indent = savedIndent;
|
|
8163
8760
|
ctx.failLabel = savedFailLabel;
|
|
8164
8761
|
ctx.recordFail = savedRecord;
|
|
8165
8762
|
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
8166
8763
|
return instrument(emitNamedFnCall(ctx, fnName, pos));
|
|
8167
8764
|
}
|
|
8168
|
-
return instrument(
|
|
8765
|
+
return instrument(dispatch2());
|
|
8169
8766
|
}
|
|
8170
8767
|
function emitDispatch(p, ctx, pos) {
|
|
8171
8768
|
const bal = p._balancedAmbient;
|
|
@@ -8198,6 +8795,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8198
8795
|
return emitSeq(def, ctx, pos);
|
|
8199
8796
|
case "choice":
|
|
8200
8797
|
return emitChoice(p, def, ctx, pos);
|
|
8798
|
+
case "dispatch":
|
|
8799
|
+
return emitDispatchCombinator(p, def, ctx, pos);
|
|
8201
8800
|
case "attempt":
|
|
8202
8801
|
return emitAttempt(p, def, ctx, pos);
|
|
8203
8802
|
case "many":
|
|
@@ -8322,6 +8921,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8322
8921
|
return emitNot(def, ctx, pos);
|
|
8323
8922
|
case "peek":
|
|
8324
8923
|
return emitPeek(def, ctx, pos);
|
|
8924
|
+
case "routed":
|
|
8925
|
+
return emitRouted(ctx, pos);
|
|
8325
8926
|
case "node":
|
|
8326
8927
|
return emitNode(def, ctx, pos);
|
|
8327
8928
|
case "scanTo":
|
|
@@ -8418,6 +9019,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8418
9019
|
case "sequence":
|
|
8419
9020
|
case "choice":
|
|
8420
9021
|
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
9022
|
+
case "dispatch":
|
|
9023
|
+
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
8421
9024
|
case "sepBy":
|
|
8422
9025
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
8423
9026
|
case "scanTo":
|
|
@@ -8432,6 +9035,26 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8432
9035
|
return false;
|
|
8433
9036
|
}
|
|
8434
9037
|
}
|
|
9038
|
+
function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
|
|
9039
|
+
if (seen.has(p)) return false;
|
|
9040
|
+
seen.add(p);
|
|
9041
|
+
const d = p._def;
|
|
9042
|
+
switch (d.tag) {
|
|
9043
|
+
case "routed":
|
|
9044
|
+
return true;
|
|
9045
|
+
case "lazy": {
|
|
9046
|
+
try {
|
|
9047
|
+
return parserUsesRouted2(d.thunk(), seen);
|
|
9048
|
+
} catch {
|
|
9049
|
+
return false;
|
|
9050
|
+
}
|
|
9051
|
+
}
|
|
9052
|
+
case "dispatch":
|
|
9053
|
+
return false;
|
|
9054
|
+
default:
|
|
9055
|
+
return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
8435
9058
|
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
8436
9059
|
if (seen.has(p)) return false;
|
|
8437
9060
|
seen.add(p);
|
|
@@ -8451,6 +9074,8 @@ function childrenOf2(def) {
|
|
|
8451
9074
|
case "sequence":
|
|
8452
9075
|
case "choice":
|
|
8453
9076
|
return def.parsers;
|
|
9077
|
+
case "dispatch":
|
|
9078
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
8454
9079
|
case "many":
|
|
8455
9080
|
case "attempt":
|
|
8456
9081
|
case "optional":
|
|
@@ -8487,6 +9112,7 @@ function childrenOf2(def) {
|
|
|
8487
9112
|
case "regex":
|
|
8488
9113
|
case "keywords":
|
|
8489
9114
|
case "guard":
|
|
9115
|
+
case "routed":
|
|
8490
9116
|
case "unknown":
|
|
8491
9117
|
return [];
|
|
8492
9118
|
}
|
|
@@ -8494,6 +9120,7 @@ function childrenOf2(def) {
|
|
|
8494
9120
|
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
8495
9121
|
"sequence",
|
|
8496
9122
|
"choice",
|
|
9123
|
+
"dispatch",
|
|
8497
9124
|
"many",
|
|
8498
9125
|
"oneOrMore",
|
|
8499
9126
|
"optional",
|
|
@@ -9318,6 +9945,8 @@ function childrenOf3(def) {
|
|
|
9318
9945
|
case "sequence":
|
|
9319
9946
|
case "choice":
|
|
9320
9947
|
return def.parsers;
|
|
9948
|
+
case "dispatch":
|
|
9949
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
9321
9950
|
case "many":
|
|
9322
9951
|
case "oneOrMore":
|
|
9323
9952
|
case "optional":
|
|
@@ -9346,6 +9975,7 @@ function childrenOf3(def) {
|
|
|
9346
9975
|
case "literal":
|
|
9347
9976
|
case "regex":
|
|
9348
9977
|
case "keywords":
|
|
9978
|
+
case "routed":
|
|
9349
9979
|
case "guard":
|
|
9350
9980
|
case "recover":
|
|
9351
9981
|
case "unknown":
|
|
@@ -9370,6 +10000,16 @@ function repeatOpts(min, max, trailing) {
|
|
|
9370
10000
|
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
9371
10001
|
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
9372
10002
|
}
|
|
10003
|
+
function matcherExpr(entry) {
|
|
10004
|
+
switch (entry.kind) {
|
|
10005
|
+
case "startsWith":
|
|
10006
|
+
return `startsWith(${JSON.stringify(entry.value)})`;
|
|
10007
|
+
case "endsWith":
|
|
10008
|
+
return `endsWith(${JSON.stringify(entry.value)})`;
|
|
10009
|
+
case "matches":
|
|
10010
|
+
return `matches(${new RegExp(entry.value, entry.flags ?? "").toString()})`;
|
|
10011
|
+
}
|
|
10012
|
+
}
|
|
9373
10013
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
9374
10014
|
try {
|
|
9375
10015
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -9467,6 +10107,13 @@ function evalRuleMapIR(ir) {
|
|
|
9467
10107
|
"keywords",
|
|
9468
10108
|
"sequence",
|
|
9469
10109
|
"choice",
|
|
10110
|
+
"dispatch",
|
|
10111
|
+
"when",
|
|
10112
|
+
"startsWith",
|
|
10113
|
+
"endsWith",
|
|
10114
|
+
"matches",
|
|
10115
|
+
"otherwise",
|
|
10116
|
+
"routed",
|
|
9470
10117
|
"attempt",
|
|
9471
10118
|
"many",
|
|
9472
10119
|
"oneOrMore",
|
|
@@ -9500,6 +10147,13 @@ function evalRuleMapIR(ir) {
|
|
|
9500
10147
|
keywords,
|
|
9501
10148
|
sequence,
|
|
9502
10149
|
choice,
|
|
10150
|
+
dispatch,
|
|
10151
|
+
when,
|
|
10152
|
+
startsWith,
|
|
10153
|
+
endsWith,
|
|
10154
|
+
matches,
|
|
10155
|
+
otherwise,
|
|
10156
|
+
routed,
|
|
9503
10157
|
attempt,
|
|
9504
10158
|
many,
|
|
9505
10159
|
oneOrMore,
|
|
@@ -9685,6 +10339,18 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9685
10339
|
});
|
|
9686
10340
|
return `_gch([${items.join(", ")}])`;
|
|
9687
10341
|
}
|
|
10342
|
+
case "dispatch": {
|
|
10343
|
+
const arms = def.cases.map(
|
|
10344
|
+
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`
|
|
10345
|
+
);
|
|
10346
|
+
if (def.matchers) {
|
|
10347
|
+
for (const entry of def.matchers) {
|
|
10348
|
+
arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`);
|
|
10349
|
+
}
|
|
10350
|
+
}
|
|
10351
|
+
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
|
|
10352
|
+
return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
|
|
10353
|
+
}
|
|
9688
10354
|
case "many":
|
|
9689
10355
|
return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
9690
10356
|
// A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
|
|
@@ -9701,6 +10367,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9701
10367
|
return `not(${kid(def.parser)})`;
|
|
9702
10368
|
case "peek":
|
|
9703
10369
|
return `peek(${kid(def.parser)})`;
|
|
10370
|
+
case "routed":
|
|
10371
|
+
return "routed()";
|
|
9704
10372
|
case "trivia":
|
|
9705
10373
|
return `trivia(${kid(def.parser)})`;
|
|
9706
10374
|
case "token":
|
|
@@ -10170,6 +10838,12 @@ function stripTsFromSource(node2, code) {
|
|
|
10170
10838
|
function isWordFactory(v2) {
|
|
10171
10839
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
10172
10840
|
}
|
|
10841
|
+
function isWhenFactory(v2) {
|
|
10842
|
+
return !!v2 && typeof v2 === "object" && v2.tag === "whenFactory";
|
|
10843
|
+
}
|
|
10844
|
+
function isStaticValueEntry(v2) {
|
|
10845
|
+
return !!v2 && typeof v2 === "object" && "value" in v2 && "mfSrcs" in v2;
|
|
10846
|
+
}
|
|
10173
10847
|
function wordFactoryFromArgs(args, scope, code, mfs) {
|
|
10174
10848
|
const [boundaryOrOptsArg, optsArg] = args;
|
|
10175
10849
|
if (boundaryOrOptsArg?.type === "SpreadElement" || optsArg?.type === "SpreadElement") return null;
|
|
@@ -10189,6 +10863,23 @@ function wordFactoryFromArgs(args, scope, code, mfs) {
|
|
|
10189
10863
|
if (typeof caseInsensitive !== "boolean") return null;
|
|
10190
10864
|
return { tag: "wordFactory", boundary: "_0-9A-Za-z", caseInsensitive };
|
|
10191
10865
|
}
|
|
10866
|
+
function dispatchWhenOptions(v2) {
|
|
10867
|
+
if (v2 === void 0) return {};
|
|
10868
|
+
if (typeof v2 !== "object" || v2 === null || Array.isArray(v2)) return null;
|
|
10869
|
+
for (const key of Object.keys(v2)) {
|
|
10870
|
+
if (key !== "caseInsensitive") return null;
|
|
10871
|
+
}
|
|
10872
|
+
const caseInsensitive = "caseInsensitive" in v2 ? v2.caseInsensitive : false;
|
|
10873
|
+
if (typeof caseInsensitive !== "boolean") return null;
|
|
10874
|
+
return { caseInsensitive };
|
|
10875
|
+
}
|
|
10876
|
+
function whenFactoryFromArgs(args, scope, code, mfs) {
|
|
10877
|
+
const [optsArg, extra] = args;
|
|
10878
|
+
if (optsArg?.type === "SpreadElement" || extra !== void 0) return null;
|
|
10879
|
+
const rawOpts = optsArg === void 0 ? void 0 : anyValue(optsArg, scope, code, mfs);
|
|
10880
|
+
const opts = dispatchWhenOptions(rawOpts);
|
|
10881
|
+
return opts === null ? null : { tag: "whenFactory", caseInsensitive: opts.caseInsensitive ?? false };
|
|
10882
|
+
}
|
|
10192
10883
|
var SUPPORTED = {
|
|
10193
10884
|
literal: (...a) => literal(a[0], a[1]),
|
|
10194
10885
|
regex: (...a) => regex(a[0], a[1]),
|
|
@@ -10204,6 +10895,7 @@ var SUPPORTED = {
|
|
|
10204
10895
|
field: (...a) => field(a[0], a[1]),
|
|
10205
10896
|
noTrivia: (...a) => noTrivia(a[0]),
|
|
10206
10897
|
token: (...a) => token(a[0]),
|
|
10898
|
+
routed: () => routed(),
|
|
10207
10899
|
leaf: (...a) => leaf(a[0], a[1]),
|
|
10208
10900
|
expect: (...a) => expect(a[0], a[1])
|
|
10209
10901
|
};
|
|
@@ -10239,9 +10931,65 @@ function gatedArmParts(e) {
|
|
|
10239
10931
|
}
|
|
10240
10932
|
return gate2 && combinator ? { gate: gate2, combinator } : null;
|
|
10241
10933
|
}
|
|
10934
|
+
function dispatchArmValue(node2, scope, code, mfs) {
|
|
10935
|
+
if (node2.type === "Identifier") {
|
|
10936
|
+
const value = anyValue(node2, scope, code, mfs);
|
|
10937
|
+
return isDispatchArm(value) ? value : null;
|
|
10938
|
+
}
|
|
10939
|
+
if (node2.type !== "CallExpression") return null;
|
|
10940
|
+
const callee = node2.callee;
|
|
10941
|
+
if (callee.type !== "Identifier") return null;
|
|
10942
|
+
const factory = scope.get(callee.name);
|
|
10943
|
+
if (isWhenFactory(factory)) {
|
|
10944
|
+
const [keyArg, parserArg, extraArg] = node2.arguments;
|
|
10945
|
+
if (!keyArg || !parserArg || extraArg !== void 0 || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
|
|
10946
|
+
const key = anyValue(keyArg, scope, code, mfs);
|
|
10947
|
+
const parserValue = anyValue(parserArg, scope, code, mfs);
|
|
10948
|
+
if (!isCombinator(parserValue)) return null;
|
|
10949
|
+
const opts = { caseInsensitive: factory.caseInsensitive };
|
|
10950
|
+
if (typeof key === "string") return when(key, parserValue, opts);
|
|
10951
|
+
if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
|
|
10952
|
+
return null;
|
|
10953
|
+
}
|
|
10954
|
+
if (callee.name === "otherwise") {
|
|
10955
|
+
const [parserArg] = node2.arguments;
|
|
10956
|
+
if (!parserArg || parserArg.type === "SpreadElement") return null;
|
|
10957
|
+
const parserValue = anyValue(parserArg, scope, code, mfs);
|
|
10958
|
+
if (!isCombinator(parserValue)) return null;
|
|
10959
|
+
return otherwise(parserValue);
|
|
10960
|
+
}
|
|
10961
|
+
if (callee.name === "when") {
|
|
10962
|
+
const [keyArg, parserArg, optsArg] = node2.arguments;
|
|
10963
|
+
if (!keyArg || !parserArg || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || optsArg?.type === "SpreadElement") return null;
|
|
10964
|
+
const key = anyValue(keyArg, scope, code, mfs);
|
|
10965
|
+
const parserValue = anyValue(parserArg, scope, code, mfs);
|
|
10966
|
+
const opts = dispatchWhenOptions(optsArg === void 0 ? void 0 : anyValue(optsArg, scope, code, mfs));
|
|
10967
|
+
if (opts === null) return null;
|
|
10968
|
+
if (!isCombinator(parserValue)) return null;
|
|
10969
|
+
if (typeof key === "string") return when(key, parserValue, opts);
|
|
10970
|
+
if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
|
|
10971
|
+
if (isDispatchMatcher(key)) return when(key, parserValue, opts);
|
|
10972
|
+
}
|
|
10973
|
+
return null;
|
|
10974
|
+
}
|
|
10242
10975
|
function isCombinator(v2) {
|
|
10243
10976
|
return !!v2 && typeof v2 === "object" && "_def" in v2;
|
|
10244
10977
|
}
|
|
10978
|
+
function isDispatchArm(v2) {
|
|
10979
|
+
if (!v2 || typeof v2 !== "object") return false;
|
|
10980
|
+
const rec = v2;
|
|
10981
|
+
if (rec.kind === "otherwise") return isCombinator(rec.parser);
|
|
10982
|
+
if (rec.kind === "whenMatcher") {
|
|
10983
|
+
return isDispatchMatcher(rec.matcher) && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
|
|
10984
|
+
}
|
|
10985
|
+
return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
|
|
10986
|
+
}
|
|
10987
|
+
function isDispatchMatcher(v2) {
|
|
10988
|
+
if (!v2 || typeof v2 !== "object") return false;
|
|
10989
|
+
const rec = v2;
|
|
10990
|
+
if ((rec.kind === "startsWith" || rec.kind === "endsWith") && typeof rec.value === "string") return true;
|
|
10991
|
+
return rec.kind === "matches" && typeof rec.value === "string" && typeof rec.flags === "string";
|
|
10992
|
+
}
|
|
10245
10993
|
function scopeGet(scope, name, mfs) {
|
|
10246
10994
|
const entry = scope.get(name);
|
|
10247
10995
|
if (!entry) return null;
|
|
@@ -10498,6 +11246,25 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
10498
11246
|
return null;
|
|
10499
11247
|
}
|
|
10500
11248
|
}
|
|
11249
|
+
if (callee.name === "dispatch") {
|
|
11250
|
+
const [selectorArg, ...armArgs] = node2.arguments;
|
|
11251
|
+
if (!selectorArg || selectorArg.type === "SpreadElement") return null;
|
|
11252
|
+
const selector = anyValue(selectorArg, scope, code, mfs);
|
|
11253
|
+
if (!isCombinator(selector)) return null;
|
|
11254
|
+
const arms = [];
|
|
11255
|
+
for (const arg of armArgs) {
|
|
11256
|
+
if (arg.type === "SpreadElement") return null;
|
|
11257
|
+
const arm = dispatchArmValue(arg, scope, code, mfs);
|
|
11258
|
+
if (!isDispatchArm(arm)) return null;
|
|
11259
|
+
arms.push(arm);
|
|
11260
|
+
}
|
|
11261
|
+
try {
|
|
11262
|
+
const unsafeDispatch = dispatch;
|
|
11263
|
+
return unsafeDispatch(selector, ...arms);
|
|
11264
|
+
} catch {
|
|
11265
|
+
return null;
|
|
11266
|
+
}
|
|
11267
|
+
}
|
|
10501
11268
|
if (callee.name === "choice" && node2.arguments.some((a) => a.type !== "SpreadElement" && isGatedArmExpr(a))) {
|
|
10502
11269
|
if (code === void 0 || mfs === void 0) return null;
|
|
10503
11270
|
const arms = [];
|
|
@@ -10583,6 +11350,10 @@ function anyValue(node2, scope, code, mfs) {
|
|
|
10583
11350
|
if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
|
|
10584
11351
|
return entry.combi;
|
|
10585
11352
|
}
|
|
11353
|
+
if (isStaticValueEntry(entry)) {
|
|
11354
|
+
if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
|
|
11355
|
+
return entry.value;
|
|
11356
|
+
}
|
|
10586
11357
|
return entry;
|
|
10587
11358
|
}
|
|
10588
11359
|
if (node2.type === "MemberExpression") {
|
|
@@ -10603,6 +11374,27 @@ function anyValue(node2, scope, code, mfs) {
|
|
|
10603
11374
|
if (callee.type === "Identifier" && callee.name === "makeWord") {
|
|
10604
11375
|
return wordFactoryFromArgs(node2.arguments, scope, code, mfs);
|
|
10605
11376
|
}
|
|
11377
|
+
if (callee.type === "Identifier" && callee.name === "makeWhen") {
|
|
11378
|
+
return whenFactoryFromArgs(node2.arguments, scope, code, mfs);
|
|
11379
|
+
}
|
|
11380
|
+
if (callee.type === "Identifier" && (callee.name === "startsWith" || callee.name === "endsWith")) {
|
|
11381
|
+
if (node2.arguments.length !== 1 || node2.arguments[0]?.type === "SpreadElement") return null;
|
|
11382
|
+
const value = anyValue(node2.arguments[0], scope, code, mfs);
|
|
11383
|
+
if (typeof value !== "string") return null;
|
|
11384
|
+
return callee.name === "startsWith" ? startsWith(value) : endsWith(value);
|
|
11385
|
+
}
|
|
11386
|
+
if (callee.type === "Identifier" && callee.name === "matches") {
|
|
11387
|
+
if (node2.arguments.length !== 1 || node2.arguments[0]?.type === "SpreadElement") return null;
|
|
11388
|
+
const value = anyValue(node2.arguments[0], scope, code, mfs);
|
|
11389
|
+
if (!(value instanceof RegExp)) return null;
|
|
11390
|
+
return matches(value);
|
|
11391
|
+
}
|
|
11392
|
+
if (callee.type === "Identifier" && (callee.name === "when" || callee.name === "otherwise")) {
|
|
11393
|
+
return dispatchArmValue(node2, scope, code, mfs);
|
|
11394
|
+
}
|
|
11395
|
+
if (callee.type === "Identifier" && isWhenFactory(scope.get(callee.name))) {
|
|
11396
|
+
return dispatchArmValue(node2, scope, code, mfs);
|
|
11397
|
+
}
|
|
10606
11398
|
return exprToCombi(node2, scope, code, mfs);
|
|
10607
11399
|
}
|
|
10608
11400
|
return null;
|
|
@@ -10613,6 +11405,12 @@ function evaluateWordFactory(node2, scope, code) {
|
|
|
10613
11405
|
if (callee.type !== "Identifier" || callee.name !== "makeWord") return null;
|
|
10614
11406
|
return wordFactoryFromArgs(node2.arguments, scope, code);
|
|
10615
11407
|
}
|
|
11408
|
+
function evaluateWhenFactory(node2, scope, code) {
|
|
11409
|
+
if (node2.type !== "CallExpression") return null;
|
|
11410
|
+
const callee = node2.callee;
|
|
11411
|
+
if (callee.type !== "Identifier" || callee.name !== "makeWhen") return null;
|
|
11412
|
+
return whenFactoryFromArgs(node2.arguments, scope, code);
|
|
11413
|
+
}
|
|
10616
11414
|
function evaluateExpr(node2, scope, code, mapFnSources) {
|
|
10617
11415
|
return exprToCombi(node2, scope, code, mapFnSources);
|
|
10618
11416
|
}
|
|
@@ -10661,6 +11459,7 @@ function evalBodyStatements(statements, scope, code) {
|
|
|
10661
11459
|
if (val === null) return false;
|
|
10662
11460
|
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
10663
11461
|
if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
11462
|
+
else if (isDispatchArm(val)) scope.set(name, { value: val, mfSrcs: thisDeclMfSrcs });
|
|
10664
11463
|
else scope.set(name, val);
|
|
10665
11464
|
}
|
|
10666
11465
|
}
|
|
@@ -11192,7 +11991,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
11192
11991
|
for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
|
|
11193
11992
|
return [...ids].sort().map((id2) => ({
|
|
11194
11993
|
id: id2,
|
|
11195
|
-
kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : "choice-arm"
|
|
11994
|
+
kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : id2.startsWith("dispatch:") ? "dispatch-arm" : "choice-arm"
|
|
11196
11995
|
}));
|
|
11197
11996
|
};
|
|
11198
11997
|
const localComposedCarried = /* @__PURE__ */ new Map();
|
|
@@ -11638,6 +12437,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
11638
12437
|
if (wordFactory) {
|
|
11639
12438
|
;
|
|
11640
12439
|
scope.set(varName, wordFactory);
|
|
12440
|
+
replacements.push({ start: init.start, end: init.end, replacement: "undefined" });
|
|
12441
|
+
continue;
|
|
12442
|
+
}
|
|
12443
|
+
const whenFactory = evaluateWhenFactory(init, scope, code);
|
|
12444
|
+
if (whenFactory) {
|
|
12445
|
+
;
|
|
12446
|
+
scope.set(varName, whenFactory);
|
|
12447
|
+
replacements.push({ start: init.start, end: init.end, replacement: "undefined" });
|
|
11641
12448
|
continue;
|
|
11642
12449
|
}
|
|
11643
12450
|
const combiArray = evaluateCombinatorArray(init, scope, code);
|