parseman 0.39.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/gating.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +36 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/compiler/codegen.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 +429 -42
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +429 -42
- package/dist/index.js.map +3 -3
- package/dist/plugin/evaluator.d.ts +6 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +446 -44
- package/dist/plugin/index.cjs.map +3 -3
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +446 -44
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.cjs +203 -17
- package/dist/spec/index.cjs.map +3 -3
- package/dist/spec/index.js +203 -17
- package/dist/spec/index.js.map +3 -3
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +20 -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.cjs
CHANGED
|
@@ -94,6 +94,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
|
|
|
94
94
|
return d.value.length === 0;
|
|
95
95
|
case "keywords":
|
|
96
96
|
return false;
|
|
97
|
+
case "routed":
|
|
98
|
+
return false;
|
|
97
99
|
case "regex":
|
|
98
100
|
try {
|
|
99
101
|
const m = new RegExp(d.source).exec("");
|
|
@@ -1186,6 +1188,8 @@ function derive(c, seen) {
|
|
|
1186
1188
|
case "not":
|
|
1187
1189
|
case "peek":
|
|
1188
1190
|
return deriveExpected2(def.parser);
|
|
1191
|
+
case "routed":
|
|
1192
|
+
return ["routed()"];
|
|
1189
1193
|
case "lazy": {
|
|
1190
1194
|
const name = c._ruleName;
|
|
1191
1195
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -2007,42 +2011,195 @@ function sequence(...parsers) {
|
|
|
2007
2011
|
}
|
|
2008
2012
|
|
|
2009
2013
|
// src/combinators/dispatch.ts
|
|
2010
|
-
function
|
|
2014
|
+
function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
2015
|
+
if (seen.has(parser2)) return false;
|
|
2016
|
+
seen.add(parser2);
|
|
2017
|
+
const def = parser2._def;
|
|
2018
|
+
switch (def.tag) {
|
|
2019
|
+
case "routed":
|
|
2020
|
+
return true;
|
|
2021
|
+
case "sequence":
|
|
2022
|
+
case "choice":
|
|
2023
|
+
return def.parsers.some((entry) => parserUsesRouted(entry, seen));
|
|
2024
|
+
case "dispatch":
|
|
2025
|
+
return false;
|
|
2026
|
+
case "sepBy":
|
|
2027
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.separator, seen);
|
|
2028
|
+
case "skip":
|
|
2029
|
+
return parserUsesRouted(def.main, seen) || parserUsesRouted(def.skipped, seen);
|
|
2030
|
+
case "grammar":
|
|
2031
|
+
return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
|
|
2032
|
+
case "scanTo":
|
|
2033
|
+
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
|
|
2034
|
+
case "recover":
|
|
2035
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
2036
|
+
case "lazy":
|
|
2037
|
+
try {
|
|
2038
|
+
return parserUsesRouted(def.thunk(), seen);
|
|
2039
|
+
} catch {
|
|
2040
|
+
return false;
|
|
2041
|
+
}
|
|
2042
|
+
case "many":
|
|
2043
|
+
case "oneOrMore":
|
|
2044
|
+
case "optional":
|
|
2045
|
+
case "attempt":
|
|
2046
|
+
case "transform":
|
|
2047
|
+
case "trivia":
|
|
2048
|
+
case "token":
|
|
2049
|
+
case "leaf":
|
|
2050
|
+
case "label":
|
|
2051
|
+
case "field":
|
|
2052
|
+
case "not":
|
|
2053
|
+
case "peek":
|
|
2054
|
+
case "node":
|
|
2055
|
+
case "withCtx":
|
|
2056
|
+
case "expect":
|
|
2057
|
+
return parserUsesRouted(def.parser, seen);
|
|
2058
|
+
default:
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function branchUsesRouted(branch) {
|
|
2063
|
+
return branch.usesRouted === true || parserUsesRouted(branch.parser);
|
|
2064
|
+
}
|
|
2065
|
+
function asciiFoldKey(key) {
|
|
2066
|
+
let out = "";
|
|
2067
|
+
for (let i = 0; i < key.length; i++) {
|
|
2068
|
+
const c = key.charCodeAt(i);
|
|
2069
|
+
out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
2070
|
+
}
|
|
2071
|
+
return out;
|
|
2072
|
+
}
|
|
2073
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2074
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2075
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2076
|
+
switch (matcher.kind) {
|
|
2077
|
+
case "startsWith":
|
|
2078
|
+
return candidate.startsWith(expected);
|
|
2079
|
+
case "endsWith":
|
|
2080
|
+
return candidate.endsWith(expected);
|
|
2081
|
+
case "matches": {
|
|
2082
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2083
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
function isStringMatcher(value) {
|
|
2088
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2089
|
+
const kind = value.kind;
|
|
2090
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2091
|
+
}
|
|
2092
|
+
function fixedMatcher(kind, value) {
|
|
2093
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2094
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2095
|
+
return { kind, value };
|
|
2096
|
+
}
|
|
2097
|
+
function startsWith(prefix) {
|
|
2098
|
+
return fixedMatcher("startsWith", prefix);
|
|
2099
|
+
}
|
|
2100
|
+
function endsWith(suffix) {
|
|
2101
|
+
return fixedMatcher("endsWith", suffix);
|
|
2102
|
+
}
|
|
2103
|
+
function matches(pattern) {
|
|
2104
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2105
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2106
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2107
|
+
}
|
|
2108
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2109
|
+
}
|
|
2110
|
+
function when(key, parser2, opts = {}) {
|
|
2111
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2112
|
+
if (isStringMatcher(key)) {
|
|
2113
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2114
|
+
}
|
|
2011
2115
|
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2012
2116
|
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2013
2117
|
for (const item of keys) {
|
|
2014
2118
|
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2015
2119
|
}
|
|
2016
|
-
return { kind: "when", keys, parser: parser2 };
|
|
2120
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2017
2121
|
}
|
|
2018
2122
|
function otherwise(parser2) {
|
|
2019
|
-
return { kind: "otherwise", parser: parser2 };
|
|
2123
|
+
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2124
|
+
}
|
|
2125
|
+
function routed() {
|
|
2126
|
+
return {
|
|
2127
|
+
_tag: "routed",
|
|
2128
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
2129
|
+
_def: { tag: "routed" },
|
|
2130
|
+
parse(_input, pos, ctx) {
|
|
2131
|
+
const item = ctx._routed;
|
|
2132
|
+
if (item === void 0) {
|
|
2133
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2134
|
+
}
|
|
2135
|
+
if (pos !== item.span.start) {
|
|
2136
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2137
|
+
}
|
|
2138
|
+
if (cstCaptureActive(ctx)) {
|
|
2139
|
+
pushCstLeaf(ctx, { _tag: "leaf", value: item.value, span: item.span });
|
|
2140
|
+
}
|
|
2141
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2020
2144
|
}
|
|
2021
2145
|
function dispatch(selector, ...arms) {
|
|
2146
|
+
if (parserUsesRouted(selector)) {
|
|
2147
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2148
|
+
}
|
|
2022
2149
|
let fallback;
|
|
2150
|
+
let fallbackUsesRouted = false;
|
|
2151
|
+
const matchers = [];
|
|
2023
2152
|
const cases = [];
|
|
2024
|
-
const seen =
|
|
2153
|
+
const seen = [];
|
|
2025
2154
|
for (let i = 0; i < arms.length; i++) {
|
|
2026
2155
|
const arm = arms[i];
|
|
2027
2156
|
if (arm.kind === "otherwise") {
|
|
2028
2157
|
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2029
2158
|
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2030
2159
|
fallback = arm.parser;
|
|
2160
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
2031
2161
|
continue;
|
|
2032
2162
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2163
|
+
if (arm.kind === "whenMatcher") {
|
|
2164
|
+
const matcher = arm.matcher;
|
|
2165
|
+
matchers.push({
|
|
2166
|
+
kind: matcher.kind,
|
|
2167
|
+
value: matcher.value,
|
|
2168
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2169
|
+
parser: arm.parser,
|
|
2170
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2171
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2172
|
+
});
|
|
2173
|
+
continue;
|
|
2036
2174
|
}
|
|
2037
|
-
|
|
2175
|
+
for (const key of arm.keys) {
|
|
2176
|
+
const folded = asciiFoldKey(key);
|
|
2177
|
+
for (const prior of seen) {
|
|
2178
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2179
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2180
|
+
}
|
|
2181
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2182
|
+
}
|
|
2183
|
+
cases.push({
|
|
2184
|
+
keys: arm.keys,
|
|
2185
|
+
parser: arm.parser,
|
|
2186
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2187
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2188
|
+
});
|
|
2038
2189
|
}
|
|
2039
2190
|
const meta = {
|
|
2040
2191
|
firstSet: selector._meta.firstSet,
|
|
2041
|
-
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2192
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2042
2193
|
isTrivia: false
|
|
2043
2194
|
};
|
|
2044
2195
|
const byKey = /* @__PURE__ */ new Map();
|
|
2045
|
-
|
|
2196
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2197
|
+
for (const entry of cases) {
|
|
2198
|
+
for (const key of entry.keys) {
|
|
2199
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2200
|
+
else byKey.set(key, entry);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2046
2203
|
return {
|
|
2047
2204
|
_tag: "dispatch",
|
|
2048
2205
|
_meta: meta,
|
|
@@ -2050,18 +2207,35 @@ function dispatch(selector, ...arms) {
|
|
|
2050
2207
|
tag: "dispatch",
|
|
2051
2208
|
selector,
|
|
2052
2209
|
cases,
|
|
2053
|
-
...
|
|
2210
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2211
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
2054
2212
|
},
|
|
2055
2213
|
parse(input, pos, ctx) {
|
|
2214
|
+
if (parserUsesRouted(selector)) {
|
|
2215
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2216
|
+
}
|
|
2217
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
2056
2218
|
const selected = selector.parse(input, pos, ctx);
|
|
2057
2219
|
if (!selected.ok) return selected;
|
|
2058
|
-
const
|
|
2059
|
-
if (
|
|
2220
|
+
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 });
|
|
2221
|
+
if (branch === void 0) {
|
|
2060
2222
|
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2061
2223
|
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2062
2224
|
}
|
|
2063
|
-
const
|
|
2064
|
-
const
|
|
2225
|
+
const savedRouted = ctx._routed;
|
|
2226
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2227
|
+
let mark = saveTriviaMark(ctx);
|
|
2228
|
+
if (usesRouted) {
|
|
2229
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2230
|
+
mark = saveTriviaMark(ctx);
|
|
2231
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2232
|
+
}
|
|
2233
|
+
let result;
|
|
2234
|
+
try {
|
|
2235
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2236
|
+
} finally {
|
|
2237
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2238
|
+
}
|
|
2065
2239
|
if (!result.ok) {
|
|
2066
2240
|
rollbackTrivia(ctx, mark);
|
|
2067
2241
|
return { ...result, committed: true };
|
|
@@ -2411,6 +2585,7 @@ function markUnusedValues(root) {
|
|
|
2411
2585
|
case "dispatch":
|
|
2412
2586
|
visit(def.selector, consumed);
|
|
2413
2587
|
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
2588
|
+
if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
|
|
2414
2589
|
if (def.otherwise) visit(def.otherwise, consumed);
|
|
2415
2590
|
return;
|
|
2416
2591
|
case "node":
|
|
@@ -2689,7 +2864,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2689
2864
|
case "choice":
|
|
2690
2865
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2691
2866
|
case "dispatch":
|
|
2692
|
-
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2867
|
+
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);
|
|
2693
2868
|
case "sepBy":
|
|
2694
2869
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2695
2870
|
case "skip":
|
|
@@ -2758,7 +2933,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2758
2933
|
case "choice":
|
|
2759
2934
|
return d.parsers.some((x) => parserHasTriviaSite(x, seen));
|
|
2760
2935
|
case "dispatch":
|
|
2761
|
-
return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
|
|
2936
|
+
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);
|
|
2762
2937
|
case "grammar":
|
|
2763
2938
|
return parserHasTriviaSite(d.parser, seen);
|
|
2764
2939
|
case "lazy": {
|
|
@@ -2785,7 +2960,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2785
2960
|
case "choice":
|
|
2786
2961
|
return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
|
|
2787
2962
|
case "dispatch":
|
|
2788
|
-
return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
|
|
2963
|
+
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);
|
|
2789
2964
|
case "sepBy":
|
|
2790
2965
|
return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
|
|
2791
2966
|
case "skip":
|
|
@@ -3160,7 +3335,7 @@ function leaf(root, fn) {
|
|
|
3160
3335
|
}
|
|
3161
3336
|
|
|
3162
3337
|
// src/version.ts
|
|
3163
|
-
var PARSEMAN_VERSION = "0.39.
|
|
3338
|
+
var PARSEMAN_VERSION = "0.39.1";
|
|
3164
3339
|
|
|
3165
3340
|
// src/cst/host-mode.ts
|
|
3166
3341
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3420,6 +3595,7 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3420
3595
|
if (d.tag === "dispatch") {
|
|
3421
3596
|
kids.push(d.selector);
|
|
3422
3597
|
for (const c of d.cases) kids.push(c.parser);
|
|
3598
|
+
if (d.matchers) for (const c of d.matchers) kids.push(c.parser);
|
|
3423
3599
|
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3424
3600
|
}
|
|
3425
3601
|
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
@@ -4943,13 +5119,25 @@ function escapeClassChar(ch2) {
|
|
|
4943
5119
|
}
|
|
4944
5120
|
|
|
4945
5121
|
// src/compiler/grammar-coverage-ids.ts
|
|
5122
|
+
function idPart(value) {
|
|
5123
|
+
return encodeURIComponent(value);
|
|
5124
|
+
}
|
|
5125
|
+
function dispatchArmIds(def, path2) {
|
|
5126
|
+
const ids = [];
|
|
5127
|
+
for (const entry of def.cases) ids.push(`dispatch:${path2}/when:${entry.keys.map(idPart).join("|")}`);
|
|
5128
|
+
for (const entry of def.matchers ?? []) {
|
|
5129
|
+
ids.push(`dispatch:${path2}/matcher:${entry.kind}:${idPart(entry.value)}${entry.kind === "matches" && entry.flags ? `:${idPart(entry.flags)}` : ""}`);
|
|
5130
|
+
}
|
|
5131
|
+
if (def.otherwise) ids.push(`dispatch:${path2}/otherwise`);
|
|
5132
|
+
return ids;
|
|
5133
|
+
}
|
|
4946
5134
|
function children(def, winners) {
|
|
4947
5135
|
switch (def.tag) {
|
|
4948
5136
|
case "sequence":
|
|
4949
5137
|
case "choice":
|
|
4950
5138
|
return def.parsers;
|
|
4951
5139
|
case "dispatch":
|
|
4952
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
5140
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
4953
5141
|
case "many":
|
|
4954
5142
|
case "oneOrMore":
|
|
4955
5143
|
case "optional":
|
|
@@ -4992,6 +5180,7 @@ function children(def, winners) {
|
|
|
4992
5180
|
function buildGrammarPlan(entry, winners) {
|
|
4993
5181
|
const definitions = /* @__PURE__ */ new Map();
|
|
4994
5182
|
const choices = /* @__PURE__ */ new WeakMap();
|
|
5183
|
+
const dispatches = /* @__PURE__ */ new WeakMap();
|
|
4995
5184
|
const attempts = /* @__PURE__ */ new WeakMap();
|
|
4996
5185
|
const labels = /* @__PURE__ */ new WeakMap();
|
|
4997
5186
|
const rules2 = /* @__PURE__ */ new WeakMap();
|
|
@@ -5022,6 +5211,11 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5022
5211
|
choices.set(parser2, ids);
|
|
5023
5212
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
5024
5213
|
}
|
|
5214
|
+
if (parser2._def.tag === "dispatch") {
|
|
5215
|
+
const ids = dispatchArmIds(parser2._def, path2);
|
|
5216
|
+
dispatches.set(parser2, ids);
|
|
5217
|
+
ids.forEach((id) => definitions.set(id, { id, kind: "dispatch-arm" }));
|
|
5218
|
+
}
|
|
5025
5219
|
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path2}`);
|
|
5026
5220
|
if (parser2._def.tag === "label") {
|
|
5027
5221
|
const id = `label:${path2}`;
|
|
@@ -5034,7 +5228,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5034
5228
|
for (const root of roots) {
|
|
5035
5229
|
visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
|
|
5036
5230
|
}
|
|
5037
|
-
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
|
|
5231
|
+
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
|
|
5038
5232
|
}
|
|
5039
5233
|
|
|
5040
5234
|
// src/compiler/scannable-run.ts
|
|
@@ -6408,6 +6602,7 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6408
6602
|
case "literal":
|
|
6409
6603
|
case "regex":
|
|
6410
6604
|
case "keywords":
|
|
6605
|
+
case "routed":
|
|
6411
6606
|
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
6412
6607
|
case "choice": {
|
|
6413
6608
|
const alts = [];
|
|
@@ -6766,7 +6961,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6766
6961
|
case "choice":
|
|
6767
6962
|
return false;
|
|
6768
6963
|
case "dispatch":
|
|
6769
|
-
return capturesLeaf(d.selector, seen) || d.cases.some((x) => mayLeavePartialCapture(x.parser, seen) || capturesLeaf(x.parser, seen)) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, seen) || capturesLeaf(d.otherwise, seen) : false);
|
|
6964
|
+
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);
|
|
6770
6965
|
case "attempt":
|
|
6771
6966
|
return false;
|
|
6772
6967
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
@@ -6865,6 +7060,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6865
7060
|
case "keywords":
|
|
6866
7061
|
case "node":
|
|
6867
7062
|
case "token":
|
|
7063
|
+
case "routed":
|
|
6868
7064
|
case "leaf":
|
|
6869
7065
|
return true;
|
|
6870
7066
|
case "not":
|
|
@@ -6877,7 +7073,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6877
7073
|
case "choice":
|
|
6878
7074
|
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
6879
7075
|
case "dispatch":
|
|
6880
|
-
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
7076
|
+
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);
|
|
6881
7077
|
case "sepBy":
|
|
6882
7078
|
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6883
7079
|
case "many":
|
|
@@ -7355,6 +7551,7 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7355
7551
|
for (const item of d.cases) {
|
|
7356
7552
|
if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
|
|
7357
7553
|
}
|
|
7554
|
+
if (d.matchers?.some((item) => mayRecordRecoveryError(item.parser, recovery, seen))) return true;
|
|
7358
7555
|
return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
|
|
7359
7556
|
}
|
|
7360
7557
|
case "attempt":
|
|
@@ -7389,53 +7586,120 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7389
7586
|
return false;
|
|
7390
7587
|
}
|
|
7391
7588
|
}
|
|
7392
|
-
function emitDispatchCombinator(def, ctx, pos) {
|
|
7589
|
+
function emitDispatchCombinator(parser2, def, ctx, pos) {
|
|
7590
|
+
if (parserUsesRouted2(def.selector)) {
|
|
7591
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
7592
|
+
}
|
|
7593
|
+
const coverageIds = ctx.coverage?.plan.dispatches.get(parser2) ?? [];
|
|
7594
|
+
const branchUsesRouted2 = (entry) => entry.usesRouted === true || parserUsesRouted2(entry.parser);
|
|
7595
|
+
const hasRoutedBranch = def.cases.some(branchUsesRouted2) || (def.matchers ? def.matchers.some(branchUsesRouted2) : false) || (def.otherwise ? def.otherwiseUsesRouted === true || parserUsesRouted2(def.otherwise) : false);
|
|
7596
|
+
const selectorLeafMark = hasRoutedBranch ? v(ctx, "_dsl") : null;
|
|
7597
|
+
const selectorRawMark = hasRoutedBranch ? v(ctx, "_dsr") : null;
|
|
7598
|
+
const selectorTriviaMark = hasRoutedBranch ? v(ctx, "_dst") : null;
|
|
7599
|
+
const selectorLogMark = hasRoutedBranch ? v(ctx, "_dsg") : null;
|
|
7600
|
+
const selectorFieldMark = hasRoutedBranch ? v(ctx, "_dsf") : null;
|
|
7601
|
+
const selectorErrorMark = hasRoutedBranch ? v(ctx, "_dse") : null;
|
|
7393
7602
|
const selector = emit(def.selector, ctx, pos);
|
|
7394
7603
|
const outV = v(ctx, "_dval");
|
|
7395
7604
|
const outE = v(ctx, "_dend");
|
|
7396
7605
|
const keyV = v(ctx, "_dkey");
|
|
7397
7606
|
const stmts = [
|
|
7607
|
+
...selectorLeafMark ? [
|
|
7608
|
+
`${ind(ctx)}const ${selectorLeafMark} = _ctx._cstLeaves?.length ?? 0`,
|
|
7609
|
+
`${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
|
|
7610
|
+
`${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
7611
|
+
`${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
|
|
7612
|
+
`${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
|
|
7613
|
+
`${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
|
|
7614
|
+
] : [],
|
|
7398
7615
|
...selector.stmts,
|
|
7399
7616
|
`${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
|
|
7400
7617
|
`${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
|
|
7401
7618
|
];
|
|
7402
7619
|
let wroteBranch = false;
|
|
7403
|
-
const emitSelectedTail = (
|
|
7620
|
+
const emitSelectedTail = (entry, keyword, condition, coverageId) => {
|
|
7621
|
+
const parser3 = entry.parser;
|
|
7404
7622
|
const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
|
|
7405
7623
|
stmts.push(head);
|
|
7406
7624
|
ctx.indent++;
|
|
7407
|
-
const
|
|
7625
|
+
const usesRouted = branchUsesRouted2(entry);
|
|
7626
|
+
const routedV = usesRouted ? v(ctx, "_drt") : null;
|
|
7627
|
+
const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
|
|
7408
7628
|
const errMark = mayError ? v(ctx, "_derr") : null;
|
|
7409
7629
|
const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
|
|
7630
|
+
if (routedV) {
|
|
7631
|
+
stmts.push(
|
|
7632
|
+
`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`,
|
|
7633
|
+
...selectorLeafMark ? [
|
|
7634
|
+
`${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}`
|
|
7635
|
+
] : []
|
|
7636
|
+
);
|
|
7637
|
+
}
|
|
7410
7638
|
if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
|
|
7411
7639
|
if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
|
|
7412
|
-
const tail = emitTailFallible(
|
|
7640
|
+
const tail = emitTailFallible(parser3, ctx, usesRouted ? pos : selector.endVar);
|
|
7641
|
+
stmts.push(...coverageAttempt(ctx, coverageId, pos));
|
|
7413
7642
|
const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
|
|
7414
7643
|
const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
|
|
7644
|
+
const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
|
|
7645
|
+
const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
|
|
7415
7646
|
stmts.push(
|
|
7416
7647
|
...tail.stmts,
|
|
7417
|
-
`${ind(ctx)}if (!${tail.okVar}) { ${errorRollback}${logRollback}${committedFailBody(ctx)} }`,
|
|
7648
|
+
`${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
|
|
7649
|
+
...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
|
|
7418
7650
|
`${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
|
|
7419
|
-
`${ind(ctx)}${outE} = ${tail.endVar}
|
|
7651
|
+
`${ind(ctx)}${outE} = ${tail.endVar}`,
|
|
7652
|
+
...coverageHit(ctx, coverageId, pos, tail.endVar)
|
|
7420
7653
|
);
|
|
7421
7654
|
ctx.indent--;
|
|
7422
7655
|
stmts.push(`${ind(ctx)}}`);
|
|
7423
7656
|
};
|
|
7657
|
+
let coverageIndex = 0;
|
|
7424
7658
|
for (const entry of def.cases) {
|
|
7425
|
-
const condition = entry.keys.map((key) =>
|
|
7426
|
-
emitSelectedTail(entry
|
|
7659
|
+
const condition = entry.keys.map((key) => emitDispatchKeyCondition(keyV, key, entry.caseInsensitive)).join(" || ");
|
|
7660
|
+
emitSelectedTail(entry, wroteBranch ? "else if" : "if", condition, coverageIds[coverageIndex++]);
|
|
7427
7661
|
wroteBranch = true;
|
|
7428
7662
|
}
|
|
7663
|
+
const emitMatcherTails = () => {
|
|
7664
|
+
for (const matcher of def.matchers ?? []) {
|
|
7665
|
+
emitSelectedTail(matcher, wroteBranch ? "else if" : "if", emitDispatchMatcherCondition(keyV, matcher), coverageIds[coverageIndex++]);
|
|
7666
|
+
wroteBranch = true;
|
|
7667
|
+
}
|
|
7668
|
+
};
|
|
7429
7669
|
if (def.otherwise) {
|
|
7430
|
-
|
|
7431
|
-
|
|
7670
|
+
emitMatcherTails();
|
|
7671
|
+
const otherwiseEntry = { parser: def.otherwise, usesRouted: def.otherwiseUsesRouted };
|
|
7672
|
+
if (wroteBranch) emitSelectedTail(otherwiseEntry, "else", void 0, coverageIds[coverageIndex++]);
|
|
7673
|
+
else emitSelectedTail(otherwiseEntry, "if", "true", coverageIds[coverageIndex++]);
|
|
7432
7674
|
} else {
|
|
7675
|
+
emitMatcherTails();
|
|
7433
7676
|
const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
|
|
7434
7677
|
const fail = failArrBody(ctx, expected, selector.endVar);
|
|
7435
7678
|
stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
|
|
7436
7679
|
}
|
|
7437
7680
|
return { stmts, valueVar: outV, endVar: outE };
|
|
7438
7681
|
}
|
|
7682
|
+
function emitDispatchKeyCondition(valueVar, key, caseInsensitive) {
|
|
7683
|
+
if (!caseInsensitive) return `${valueVar} === ${JSON.stringify(key)}`;
|
|
7684
|
+
const parts = [`${valueVar}.length === ${key.length}`];
|
|
7685
|
+
for (let i = 0; i < key.length; i++) {
|
|
7686
|
+
parts.push(foldEq(`${valueVar}.charCodeAt(${i})`, key.charCodeAt(i)));
|
|
7687
|
+
}
|
|
7688
|
+
return parts.join(" && ");
|
|
7689
|
+
}
|
|
7690
|
+
function emitDispatchMatcherCondition(valueVar, matcher) {
|
|
7691
|
+
if (matcher.kind === "matches") {
|
|
7692
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
7693
|
+
return `${new RegExp(matcher.value, flags).toString()}.test(${valueVar})`;
|
|
7694
|
+
}
|
|
7695
|
+
const text = matcher.value;
|
|
7696
|
+
const parts = [`${valueVar}.length >= ${text.length}`];
|
|
7697
|
+
const offset = matcher.kind === "endsWith" ? `${valueVar}.length - ${text.length}` : "0";
|
|
7698
|
+
for (let i = 0; i < text.length; i++) {
|
|
7699
|
+
parts.push(matcher.caseInsensitive ? foldEq(`${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`})`, text.charCodeAt(i)) : `${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`}) === ${text.charCodeAt(i)}`);
|
|
7700
|
+
}
|
|
7701
|
+
return parts.join(" && ");
|
|
7702
|
+
}
|
|
7439
7703
|
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
7440
7704
|
const superParser = def.parsers[superIndex];
|
|
7441
7705
|
const regexDef = getCoreRegexDef(superParser);
|
|
@@ -8090,6 +8354,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
8090
8354
|
endVar: pos
|
|
8091
8355
|
};
|
|
8092
8356
|
}
|
|
8357
|
+
function emitRouted(ctx, pos) {
|
|
8358
|
+
const item = v(ctx, "_rt");
|
|
8359
|
+
return {
|
|
8360
|
+
stmts: [
|
|
8361
|
+
`${ind(ctx)}const ${item} = _ctx._routed`,
|
|
8362
|
+
...emitIfFail(ctx, `${item} === undefined || ${item}.span.start !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
8363
|
+
...emitLeafCapture(ctx, `${item}.value`, `${item}.span.start`, `${item}.span.end`)
|
|
8364
|
+
],
|
|
8365
|
+
valueVar: `${item}.value`,
|
|
8366
|
+
endVar: `${item}.span.end`
|
|
8367
|
+
};
|
|
8368
|
+
}
|
|
8093
8369
|
function escapeTokenLiteral(value) {
|
|
8094
8370
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
8095
8371
|
}
|
|
@@ -8555,7 +8831,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8555
8831
|
case "choice":
|
|
8556
8832
|
return emitChoice(p, def, ctx, pos);
|
|
8557
8833
|
case "dispatch":
|
|
8558
|
-
return emitDispatchCombinator(def, ctx, pos);
|
|
8834
|
+
return emitDispatchCombinator(p, def, ctx, pos);
|
|
8559
8835
|
case "attempt":
|
|
8560
8836
|
return emitAttempt(p, def, ctx, pos);
|
|
8561
8837
|
case "many":
|
|
@@ -8680,6 +8956,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8680
8956
|
return emitNot(def, ctx, pos);
|
|
8681
8957
|
case "peek":
|
|
8682
8958
|
return emitPeek(def, ctx, pos);
|
|
8959
|
+
case "routed":
|
|
8960
|
+
return emitRouted(ctx, pos);
|
|
8683
8961
|
case "node":
|
|
8684
8962
|
return emitNode(def, ctx, pos);
|
|
8685
8963
|
case "scanTo":
|
|
@@ -8777,7 +9055,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8777
9055
|
case "choice":
|
|
8778
9056
|
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
8779
9057
|
case "dispatch":
|
|
8780
|
-
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
9058
|
+
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);
|
|
8781
9059
|
case "sepBy":
|
|
8782
9060
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
8783
9061
|
case "scanTo":
|
|
@@ -8792,6 +9070,26 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8792
9070
|
return false;
|
|
8793
9071
|
}
|
|
8794
9072
|
}
|
|
9073
|
+
function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
|
|
9074
|
+
if (seen.has(p)) return false;
|
|
9075
|
+
seen.add(p);
|
|
9076
|
+
const d = p._def;
|
|
9077
|
+
switch (d.tag) {
|
|
9078
|
+
case "routed":
|
|
9079
|
+
return true;
|
|
9080
|
+
case "lazy": {
|
|
9081
|
+
try {
|
|
9082
|
+
return parserUsesRouted2(d.thunk(), seen);
|
|
9083
|
+
} catch {
|
|
9084
|
+
return false;
|
|
9085
|
+
}
|
|
9086
|
+
}
|
|
9087
|
+
case "dispatch":
|
|
9088
|
+
return false;
|
|
9089
|
+
default:
|
|
9090
|
+
return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
8795
9093
|
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
8796
9094
|
if (seen.has(p)) return false;
|
|
8797
9095
|
seen.add(p);
|
|
@@ -8812,7 +9110,7 @@ function childrenOf2(def) {
|
|
|
8812
9110
|
case "choice":
|
|
8813
9111
|
return def.parsers;
|
|
8814
9112
|
case "dispatch":
|
|
8815
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9113
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
8816
9114
|
case "many":
|
|
8817
9115
|
case "attempt":
|
|
8818
9116
|
case "optional":
|
|
@@ -8849,6 +9147,7 @@ function childrenOf2(def) {
|
|
|
8849
9147
|
case "regex":
|
|
8850
9148
|
case "keywords":
|
|
8851
9149
|
case "guard":
|
|
9150
|
+
case "routed":
|
|
8852
9151
|
case "unknown":
|
|
8853
9152
|
return [];
|
|
8854
9153
|
}
|
|
@@ -9682,7 +9981,7 @@ function childrenOf3(def) {
|
|
|
9682
9981
|
case "choice":
|
|
9683
9982
|
return def.parsers;
|
|
9684
9983
|
case "dispatch":
|
|
9685
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9984
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
9686
9985
|
case "many":
|
|
9687
9986
|
case "oneOrMore":
|
|
9688
9987
|
case "optional":
|
|
@@ -9711,6 +10010,7 @@ function childrenOf3(def) {
|
|
|
9711
10010
|
case "literal":
|
|
9712
10011
|
case "regex":
|
|
9713
10012
|
case "keywords":
|
|
10013
|
+
case "routed":
|
|
9714
10014
|
case "guard":
|
|
9715
10015
|
case "recover":
|
|
9716
10016
|
case "unknown":
|
|
@@ -9735,6 +10035,16 @@ function repeatOpts(min, max, trailing) {
|
|
|
9735
10035
|
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
9736
10036
|
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
9737
10037
|
}
|
|
10038
|
+
function matcherExpr(entry) {
|
|
10039
|
+
switch (entry.kind) {
|
|
10040
|
+
case "startsWith":
|
|
10041
|
+
return `startsWith(${JSON.stringify(entry.value)})`;
|
|
10042
|
+
case "endsWith":
|
|
10043
|
+
return `endsWith(${JSON.stringify(entry.value)})`;
|
|
10044
|
+
case "matches":
|
|
10045
|
+
return `matches(${new RegExp(entry.value, entry.flags ?? "").toString()})`;
|
|
10046
|
+
}
|
|
10047
|
+
}
|
|
9738
10048
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
9739
10049
|
try {
|
|
9740
10050
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -9834,7 +10144,11 @@ function evalRuleMapIR(ir) {
|
|
|
9834
10144
|
"choice",
|
|
9835
10145
|
"dispatch",
|
|
9836
10146
|
"when",
|
|
10147
|
+
"startsWith",
|
|
10148
|
+
"endsWith",
|
|
10149
|
+
"matches",
|
|
9837
10150
|
"otherwise",
|
|
10151
|
+
"routed",
|
|
9838
10152
|
"attempt",
|
|
9839
10153
|
"many",
|
|
9840
10154
|
"oneOrMore",
|
|
@@ -9870,7 +10184,11 @@ function evalRuleMapIR(ir) {
|
|
|
9870
10184
|
choice,
|
|
9871
10185
|
dispatch,
|
|
9872
10186
|
when,
|
|
10187
|
+
startsWith,
|
|
10188
|
+
endsWith,
|
|
10189
|
+
matches,
|
|
9873
10190
|
otherwise,
|
|
10191
|
+
routed,
|
|
9874
10192
|
attempt,
|
|
9875
10193
|
many,
|
|
9876
10194
|
oneOrMore,
|
|
@@ -10058,8 +10376,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
10058
10376
|
}
|
|
10059
10377
|
case "dispatch": {
|
|
10060
10378
|
const arms = def.cases.map(
|
|
10061
|
-
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)})`
|
|
10379
|
+
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`
|
|
10062
10380
|
);
|
|
10381
|
+
if (def.matchers) {
|
|
10382
|
+
for (const entry of def.matchers) {
|
|
10383
|
+
arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`);
|
|
10384
|
+
}
|
|
10385
|
+
}
|
|
10063
10386
|
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
|
|
10064
10387
|
return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
|
|
10065
10388
|
}
|
|
@@ -10079,6 +10402,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
10079
10402
|
return `not(${kid(def.parser)})`;
|
|
10080
10403
|
case "peek":
|
|
10081
10404
|
return `peek(${kid(def.parser)})`;
|
|
10405
|
+
case "routed":
|
|
10406
|
+
return "routed()";
|
|
10082
10407
|
case "trivia":
|
|
10083
10408
|
return `trivia(${kid(def.parser)})`;
|
|
10084
10409
|
case "token":
|
|
@@ -10548,6 +10873,9 @@ function stripTsFromSource(node2, code) {
|
|
|
10548
10873
|
function isWordFactory(v2) {
|
|
10549
10874
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
10550
10875
|
}
|
|
10876
|
+
function isWhenFactory(v2) {
|
|
10877
|
+
return !!v2 && typeof v2 === "object" && v2.tag === "whenFactory";
|
|
10878
|
+
}
|
|
10551
10879
|
function isStaticValueEntry(v2) {
|
|
10552
10880
|
return !!v2 && typeof v2 === "object" && "value" in v2 && "mfSrcs" in v2;
|
|
10553
10881
|
}
|
|
@@ -10570,6 +10898,23 @@ function wordFactoryFromArgs(args, scope, code, mfs) {
|
|
|
10570
10898
|
if (typeof caseInsensitive !== "boolean") return null;
|
|
10571
10899
|
return { tag: "wordFactory", boundary: "_0-9A-Za-z", caseInsensitive };
|
|
10572
10900
|
}
|
|
10901
|
+
function dispatchWhenOptions(v2) {
|
|
10902
|
+
if (v2 === void 0) return {};
|
|
10903
|
+
if (typeof v2 !== "object" || v2 === null || Array.isArray(v2)) return null;
|
|
10904
|
+
for (const key of Object.keys(v2)) {
|
|
10905
|
+
if (key !== "caseInsensitive") return null;
|
|
10906
|
+
}
|
|
10907
|
+
const caseInsensitive = "caseInsensitive" in v2 ? v2.caseInsensitive : false;
|
|
10908
|
+
if (typeof caseInsensitive !== "boolean") return null;
|
|
10909
|
+
return { caseInsensitive };
|
|
10910
|
+
}
|
|
10911
|
+
function whenFactoryFromArgs(args, scope, code, mfs) {
|
|
10912
|
+
const [optsArg, extra] = args;
|
|
10913
|
+
if (optsArg?.type === "SpreadElement" || extra !== void 0) return null;
|
|
10914
|
+
const rawOpts = optsArg === void 0 ? void 0 : anyValue(optsArg, scope, code, mfs);
|
|
10915
|
+
const opts = dispatchWhenOptions(rawOpts);
|
|
10916
|
+
return opts === null ? null : { tag: "whenFactory", caseInsensitive: opts.caseInsensitive ?? false };
|
|
10917
|
+
}
|
|
10573
10918
|
var SUPPORTED = {
|
|
10574
10919
|
literal: (...a) => literal(a[0], a[1]),
|
|
10575
10920
|
regex: (...a) => regex(a[0], a[1]),
|
|
@@ -10585,6 +10930,7 @@ var SUPPORTED = {
|
|
|
10585
10930
|
field: (...a) => field(a[0], a[1]),
|
|
10586
10931
|
noTrivia: (...a) => noTrivia(a[0]),
|
|
10587
10932
|
token: (...a) => token(a[0]),
|
|
10933
|
+
routed: () => routed(),
|
|
10588
10934
|
leaf: (...a) => leaf(a[0], a[1]),
|
|
10589
10935
|
expect: (...a) => expect(a[0], a[1])
|
|
10590
10936
|
};
|
|
@@ -10628,6 +10974,18 @@ function dispatchArmValue(node2, scope, code, mfs) {
|
|
|
10628
10974
|
if (node2.type !== "CallExpression") return null;
|
|
10629
10975
|
const callee = node2.callee;
|
|
10630
10976
|
if (callee.type !== "Identifier") return null;
|
|
10977
|
+
const factory = scope.get(callee.name);
|
|
10978
|
+
if (isWhenFactory(factory)) {
|
|
10979
|
+
const [keyArg, parserArg, extraArg] = node2.arguments;
|
|
10980
|
+
if (!keyArg || !parserArg || extraArg !== void 0 || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
|
|
10981
|
+
const key = anyValue(keyArg, scope, code, mfs);
|
|
10982
|
+
const parserValue = anyValue(parserArg, scope, code, mfs);
|
|
10983
|
+
if (!isCombinator(parserValue)) return null;
|
|
10984
|
+
const opts = { caseInsensitive: factory.caseInsensitive };
|
|
10985
|
+
if (typeof key === "string") return when(key, parserValue, opts);
|
|
10986
|
+
if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
|
|
10987
|
+
return null;
|
|
10988
|
+
}
|
|
10631
10989
|
if (callee.name === "otherwise") {
|
|
10632
10990
|
const [parserArg] = node2.arguments;
|
|
10633
10991
|
if (!parserArg || parserArg.type === "SpreadElement") return null;
|
|
@@ -10636,13 +10994,16 @@ function dispatchArmValue(node2, scope, code, mfs) {
|
|
|
10636
10994
|
return otherwise(parserValue);
|
|
10637
10995
|
}
|
|
10638
10996
|
if (callee.name === "when") {
|
|
10639
|
-
const [keyArg, parserArg] = node2.arguments;
|
|
10640
|
-
if (!keyArg || !parserArg || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
|
|
10997
|
+
const [keyArg, parserArg, optsArg] = node2.arguments;
|
|
10998
|
+
if (!keyArg || !parserArg || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || optsArg?.type === "SpreadElement") return null;
|
|
10641
10999
|
const key = anyValue(keyArg, scope, code, mfs);
|
|
10642
11000
|
const parserValue = anyValue(parserArg, scope, code, mfs);
|
|
11001
|
+
const opts = dispatchWhenOptions(optsArg === void 0 ? void 0 : anyValue(optsArg, scope, code, mfs));
|
|
11002
|
+
if (opts === null) return null;
|
|
10643
11003
|
if (!isCombinator(parserValue)) return null;
|
|
10644
|
-
if (typeof key === "string") return when(key, parserValue);
|
|
10645
|
-
if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue);
|
|
11004
|
+
if (typeof key === "string") return when(key, parserValue, opts);
|
|
11005
|
+
if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
|
|
11006
|
+
if (isDispatchMatcher(key)) return when(key, parserValue, opts);
|
|
10646
11007
|
}
|
|
10647
11008
|
return null;
|
|
10648
11009
|
}
|
|
@@ -10653,7 +11014,16 @@ function isDispatchArm(v2) {
|
|
|
10653
11014
|
if (!v2 || typeof v2 !== "object") return false;
|
|
10654
11015
|
const rec = v2;
|
|
10655
11016
|
if (rec.kind === "otherwise") return isCombinator(rec.parser);
|
|
10656
|
-
|
|
11017
|
+
if (rec.kind === "whenMatcher") {
|
|
11018
|
+
return isDispatchMatcher(rec.matcher) && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
|
|
11019
|
+
}
|
|
11020
|
+
return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
|
|
11021
|
+
}
|
|
11022
|
+
function isDispatchMatcher(v2) {
|
|
11023
|
+
if (!v2 || typeof v2 !== "object") return false;
|
|
11024
|
+
const rec = v2;
|
|
11025
|
+
if ((rec.kind === "startsWith" || rec.kind === "endsWith") && typeof rec.value === "string") return true;
|
|
11026
|
+
return rec.kind === "matches" && typeof rec.value === "string" && typeof rec.flags === "string";
|
|
10657
11027
|
}
|
|
10658
11028
|
function scopeGet(scope, name, mfs) {
|
|
10659
11029
|
const entry = scope.get(name);
|
|
@@ -11039,9 +11409,27 @@ function anyValue(node2, scope, code, mfs) {
|
|
|
11039
11409
|
if (callee.type === "Identifier" && callee.name === "makeWord") {
|
|
11040
11410
|
return wordFactoryFromArgs(node2.arguments, scope, code, mfs);
|
|
11041
11411
|
}
|
|
11412
|
+
if (callee.type === "Identifier" && callee.name === "makeWhen") {
|
|
11413
|
+
return whenFactoryFromArgs(node2.arguments, scope, code, mfs);
|
|
11414
|
+
}
|
|
11415
|
+
if (callee.type === "Identifier" && (callee.name === "startsWith" || callee.name === "endsWith")) {
|
|
11416
|
+
if (node2.arguments.length !== 1 || node2.arguments[0]?.type === "SpreadElement") return null;
|
|
11417
|
+
const value = anyValue(node2.arguments[0], scope, code, mfs);
|
|
11418
|
+
if (typeof value !== "string") return null;
|
|
11419
|
+
return callee.name === "startsWith" ? startsWith(value) : endsWith(value);
|
|
11420
|
+
}
|
|
11421
|
+
if (callee.type === "Identifier" && callee.name === "matches") {
|
|
11422
|
+
if (node2.arguments.length !== 1 || node2.arguments[0]?.type === "SpreadElement") return null;
|
|
11423
|
+
const value = anyValue(node2.arguments[0], scope, code, mfs);
|
|
11424
|
+
if (!(value instanceof RegExp)) return null;
|
|
11425
|
+
return matches(value);
|
|
11426
|
+
}
|
|
11042
11427
|
if (callee.type === "Identifier" && (callee.name === "when" || callee.name === "otherwise")) {
|
|
11043
11428
|
return dispatchArmValue(node2, scope, code, mfs);
|
|
11044
11429
|
}
|
|
11430
|
+
if (callee.type === "Identifier" && isWhenFactory(scope.get(callee.name))) {
|
|
11431
|
+
return dispatchArmValue(node2, scope, code, mfs);
|
|
11432
|
+
}
|
|
11045
11433
|
return exprToCombi(node2, scope, code, mfs);
|
|
11046
11434
|
}
|
|
11047
11435
|
return null;
|
|
@@ -11052,6 +11440,12 @@ function evaluateWordFactory(node2, scope, code) {
|
|
|
11052
11440
|
if (callee.type !== "Identifier" || callee.name !== "makeWord") return null;
|
|
11053
11441
|
return wordFactoryFromArgs(node2.arguments, scope, code);
|
|
11054
11442
|
}
|
|
11443
|
+
function evaluateWhenFactory(node2, scope, code) {
|
|
11444
|
+
if (node2.type !== "CallExpression") return null;
|
|
11445
|
+
const callee = node2.callee;
|
|
11446
|
+
if (callee.type !== "Identifier" || callee.name !== "makeWhen") return null;
|
|
11447
|
+
return whenFactoryFromArgs(node2.arguments, scope, code);
|
|
11448
|
+
}
|
|
11055
11449
|
function evaluateExpr(node2, scope, code, mapFnSources) {
|
|
11056
11450
|
return exprToCombi(node2, scope, code, mapFnSources);
|
|
11057
11451
|
}
|
|
@@ -11632,7 +12026,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
11632
12026
|
for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
|
|
11633
12027
|
return [...ids].sort().map((id2) => ({
|
|
11634
12028
|
id: id2,
|
|
11635
|
-
kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : "choice-arm"
|
|
12029
|
+
kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : id2.startsWith("dispatch:") ? "dispatch-arm" : "choice-arm"
|
|
11636
12030
|
}));
|
|
11637
12031
|
};
|
|
11638
12032
|
const localComposedCarried = /* @__PURE__ */ new Map();
|
|
@@ -12078,6 +12472,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12078
12472
|
if (wordFactory) {
|
|
12079
12473
|
;
|
|
12080
12474
|
scope.set(varName, wordFactory);
|
|
12475
|
+
replacements.push({ start: init.start, end: init.end, replacement: "undefined" });
|
|
12476
|
+
continue;
|
|
12477
|
+
}
|
|
12478
|
+
const whenFactory = evaluateWhenFactory(init, scope, code);
|
|
12479
|
+
if (whenFactory) {
|
|
12480
|
+
;
|
|
12481
|
+
scope.set(varName, whenFactory);
|
|
12482
|
+
replacements.push({ start: init.start, end: init.end, replacement: "undefined" });
|
|
12081
12483
|
continue;
|
|
12082
12484
|
}
|
|
12083
12485
|
const combiArray = evaluateCombinatorArray(init, scope, code);
|