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.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("");
|
|
@@ -1151,6 +1153,8 @@ function derive(c, seen) {
|
|
|
1151
1153
|
case "not":
|
|
1152
1154
|
case "peek":
|
|
1153
1155
|
return deriveExpected2(def.parser);
|
|
1156
|
+
case "routed":
|
|
1157
|
+
return ["routed()"];
|
|
1154
1158
|
case "lazy": {
|
|
1155
1159
|
const name = c._ruleName;
|
|
1156
1160
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -1972,42 +1976,195 @@ function sequence(...parsers) {
|
|
|
1972
1976
|
}
|
|
1973
1977
|
|
|
1974
1978
|
// src/combinators/dispatch.ts
|
|
1975
|
-
function
|
|
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
|
+
}
|
|
1976
2080
|
const keys = Array.isArray(key) ? [...key] : [key];
|
|
1977
2081
|
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
1978
2082
|
for (const item of keys) {
|
|
1979
2083
|
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
1980
2084
|
}
|
|
1981
|
-
return { kind: "when", keys, parser: parser2 };
|
|
2085
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
1982
2086
|
}
|
|
1983
2087
|
function otherwise(parser2) {
|
|
1984
|
-
return { kind: "otherwise", parser: 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
|
+
};
|
|
1985
2109
|
}
|
|
1986
2110
|
function dispatch(selector, ...arms) {
|
|
2111
|
+
if (parserUsesRouted(selector)) {
|
|
2112
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2113
|
+
}
|
|
1987
2114
|
let fallback;
|
|
2115
|
+
let fallbackUsesRouted = false;
|
|
2116
|
+
const matchers = [];
|
|
1988
2117
|
const cases = [];
|
|
1989
|
-
const seen =
|
|
2118
|
+
const seen = [];
|
|
1990
2119
|
for (let i = 0; i < arms.length; i++) {
|
|
1991
2120
|
const arm = arms[i];
|
|
1992
2121
|
if (arm.kind === "otherwise") {
|
|
1993
2122
|
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
1994
2123
|
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
1995
2124
|
fallback = arm.parser;
|
|
2125
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
1996
2126
|
continue;
|
|
1997
2127
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
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;
|
|
2001
2139
|
}
|
|
2002
|
-
|
|
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
|
+
});
|
|
2003
2154
|
}
|
|
2004
2155
|
const meta = {
|
|
2005
2156
|
firstSet: selector._meta.firstSet,
|
|
2006
|
-
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2157
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2007
2158
|
isTrivia: false
|
|
2008
2159
|
};
|
|
2009
2160
|
const byKey = /* @__PURE__ */ new Map();
|
|
2010
|
-
|
|
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
|
+
}
|
|
2011
2168
|
return {
|
|
2012
2169
|
_tag: "dispatch",
|
|
2013
2170
|
_meta: meta,
|
|
@@ -2015,18 +2172,35 @@ function dispatch(selector, ...arms) {
|
|
|
2015
2172
|
tag: "dispatch",
|
|
2016
2173
|
selector,
|
|
2017
2174
|
cases,
|
|
2018
|
-
...
|
|
2175
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2176
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
2019
2177
|
},
|
|
2020
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);
|
|
2021
2183
|
const selected = selector.parse(input, pos, ctx);
|
|
2022
2184
|
if (!selected.ok) return selected;
|
|
2023
|
-
const
|
|
2024
|
-
if (
|
|
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) {
|
|
2025
2187
|
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2026
2188
|
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2027
2189
|
}
|
|
2028
|
-
const
|
|
2029
|
-
const
|
|
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
|
+
}
|
|
2030
2204
|
if (!result.ok) {
|
|
2031
2205
|
rollbackTrivia(ctx, mark);
|
|
2032
2206
|
return { ...result, committed: true };
|
|
@@ -2376,6 +2550,7 @@ function markUnusedValues(root) {
|
|
|
2376
2550
|
case "dispatch":
|
|
2377
2551
|
visit(def.selector, consumed);
|
|
2378
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);
|
|
2379
2554
|
if (def.otherwise) visit(def.otherwise, consumed);
|
|
2380
2555
|
return;
|
|
2381
2556
|
case "node":
|
|
@@ -2654,7 +2829,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2654
2829
|
case "choice":
|
|
2655
2830
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2656
2831
|
case "dispatch":
|
|
2657
|
-
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
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);
|
|
2658
2833
|
case "sepBy":
|
|
2659
2834
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2660
2835
|
case "skip":
|
|
@@ -2723,7 +2898,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2723
2898
|
case "choice":
|
|
2724
2899
|
return d.parsers.some((x) => parserHasTriviaSite(x, seen));
|
|
2725
2900
|
case "dispatch":
|
|
2726
|
-
return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
|
|
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);
|
|
2727
2902
|
case "grammar":
|
|
2728
2903
|
return parserHasTriviaSite(d.parser, seen);
|
|
2729
2904
|
case "lazy": {
|
|
@@ -2750,7 +2925,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2750
2925
|
case "choice":
|
|
2751
2926
|
return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
|
|
2752
2927
|
case "dispatch":
|
|
2753
|
-
return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
|
|
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);
|
|
2754
2929
|
case "sepBy":
|
|
2755
2930
|
return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
|
|
2756
2931
|
case "skip":
|
|
@@ -3125,7 +3300,7 @@ function leaf(root, fn) {
|
|
|
3125
3300
|
}
|
|
3126
3301
|
|
|
3127
3302
|
// src/version.ts
|
|
3128
|
-
var PARSEMAN_VERSION = "0.39.
|
|
3303
|
+
var PARSEMAN_VERSION = "0.39.1";
|
|
3129
3304
|
|
|
3130
3305
|
// src/cst/host-mode.ts
|
|
3131
3306
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3385,6 +3560,7 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3385
3560
|
if (d.tag === "dispatch") {
|
|
3386
3561
|
kids.push(d.selector);
|
|
3387
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);
|
|
3388
3564
|
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3389
3565
|
}
|
|
3390
3566
|
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
@@ -4908,13 +5084,25 @@ function escapeClassChar(ch2) {
|
|
|
4908
5084
|
}
|
|
4909
5085
|
|
|
4910
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
|
+
}
|
|
4911
5099
|
function children(def, winners) {
|
|
4912
5100
|
switch (def.tag) {
|
|
4913
5101
|
case "sequence":
|
|
4914
5102
|
case "choice":
|
|
4915
5103
|
return def.parsers;
|
|
4916
5104
|
case "dispatch":
|
|
4917
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
5105
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
4918
5106
|
case "many":
|
|
4919
5107
|
case "oneOrMore":
|
|
4920
5108
|
case "optional":
|
|
@@ -4957,6 +5145,7 @@ function children(def, winners) {
|
|
|
4957
5145
|
function buildGrammarPlan(entry, winners) {
|
|
4958
5146
|
const definitions = /* @__PURE__ */ new Map();
|
|
4959
5147
|
const choices = /* @__PURE__ */ new WeakMap();
|
|
5148
|
+
const dispatches = /* @__PURE__ */ new WeakMap();
|
|
4960
5149
|
const attempts = /* @__PURE__ */ new WeakMap();
|
|
4961
5150
|
const labels = /* @__PURE__ */ new WeakMap();
|
|
4962
5151
|
const rules2 = /* @__PURE__ */ new WeakMap();
|
|
@@ -4987,6 +5176,11 @@ function buildGrammarPlan(entry, winners) {
|
|
|
4987
5176
|
choices.set(parser2, ids);
|
|
4988
5177
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
4989
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
|
+
}
|
|
4990
5184
|
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path2}`);
|
|
4991
5185
|
if (parser2._def.tag === "label") {
|
|
4992
5186
|
const id = `label:${path2}`;
|
|
@@ -4999,7 +5193,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
4999
5193
|
for (const root of roots) {
|
|
5000
5194
|
visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
|
|
5001
5195
|
}
|
|
5002
|
-
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 };
|
|
5003
5197
|
}
|
|
5004
5198
|
|
|
5005
5199
|
// src/compiler/scannable-run.ts
|
|
@@ -6373,6 +6567,7 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6373
6567
|
case "literal":
|
|
6374
6568
|
case "regex":
|
|
6375
6569
|
case "keywords":
|
|
6570
|
+
case "routed":
|
|
6376
6571
|
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
6377
6572
|
case "choice": {
|
|
6378
6573
|
const alts = [];
|
|
@@ -6731,7 +6926,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6731
6926
|
case "choice":
|
|
6732
6927
|
return false;
|
|
6733
6928
|
case "dispatch":
|
|
6734
|
-
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);
|
|
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);
|
|
6735
6930
|
case "attempt":
|
|
6736
6931
|
return false;
|
|
6737
6932
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
@@ -6830,6 +7025,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6830
7025
|
case "keywords":
|
|
6831
7026
|
case "node":
|
|
6832
7027
|
case "token":
|
|
7028
|
+
case "routed":
|
|
6833
7029
|
case "leaf":
|
|
6834
7030
|
return true;
|
|
6835
7031
|
case "not":
|
|
@@ -6842,7 +7038,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6842
7038
|
case "choice":
|
|
6843
7039
|
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
6844
7040
|
case "dispatch":
|
|
6845
|
-
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
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);
|
|
6846
7042
|
case "sepBy":
|
|
6847
7043
|
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6848
7044
|
case "many":
|
|
@@ -7320,6 +7516,7 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7320
7516
|
for (const item of d.cases) {
|
|
7321
7517
|
if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
|
|
7322
7518
|
}
|
|
7519
|
+
if (d.matchers?.some((item) => mayRecordRecoveryError(item.parser, recovery, seen))) return true;
|
|
7323
7520
|
return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
|
|
7324
7521
|
}
|
|
7325
7522
|
case "attempt":
|
|
@@ -7354,53 +7551,120 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7354
7551
|
return false;
|
|
7355
7552
|
}
|
|
7356
7553
|
}
|
|
7357
|
-
function emitDispatchCombinator(def, ctx, pos) {
|
|
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;
|
|
7358
7567
|
const selector = emit(def.selector, ctx, pos);
|
|
7359
7568
|
const outV = v(ctx, "_dval");
|
|
7360
7569
|
const outE = v(ctx, "_dend");
|
|
7361
7570
|
const keyV = v(ctx, "_dkey");
|
|
7362
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
|
+
] : [],
|
|
7363
7580
|
...selector.stmts,
|
|
7364
7581
|
`${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
|
|
7365
7582
|
`${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
|
|
7366
7583
|
];
|
|
7367
7584
|
let wroteBranch = false;
|
|
7368
|
-
const emitSelectedTail = (
|
|
7585
|
+
const emitSelectedTail = (entry, keyword, condition, coverageId) => {
|
|
7586
|
+
const parser3 = entry.parser;
|
|
7369
7587
|
const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
|
|
7370
7588
|
stmts.push(head);
|
|
7371
7589
|
ctx.indent++;
|
|
7372
|
-
const
|
|
7590
|
+
const usesRouted = branchUsesRouted2(entry);
|
|
7591
|
+
const routedV = usesRouted ? v(ctx, "_drt") : null;
|
|
7592
|
+
const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
|
|
7373
7593
|
const errMark = mayError ? v(ctx, "_derr") : null;
|
|
7374
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
|
+
}
|
|
7375
7603
|
if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
|
|
7376
7604
|
if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
|
|
7377
|
-
const tail = emitTailFallible(
|
|
7605
|
+
const tail = emitTailFallible(parser3, ctx, usesRouted ? pos : selector.endVar);
|
|
7606
|
+
stmts.push(...coverageAttempt(ctx, coverageId, pos));
|
|
7378
7607
|
const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
|
|
7379
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 }); `;
|
|
7380
7611
|
stmts.push(
|
|
7381
7612
|
...tail.stmts,
|
|
7382
|
-
`${ind(ctx)}if (!${tail.okVar}) { ${errorRollback}${logRollback}${committedFailBody(ctx)} }`,
|
|
7613
|
+
`${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
|
|
7614
|
+
...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
|
|
7383
7615
|
`${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
|
|
7384
|
-
`${ind(ctx)}${outE} = ${tail.endVar}
|
|
7616
|
+
`${ind(ctx)}${outE} = ${tail.endVar}`,
|
|
7617
|
+
...coverageHit(ctx, coverageId, pos, tail.endVar)
|
|
7385
7618
|
);
|
|
7386
7619
|
ctx.indent--;
|
|
7387
7620
|
stmts.push(`${ind(ctx)}}`);
|
|
7388
7621
|
};
|
|
7622
|
+
let coverageIndex = 0;
|
|
7389
7623
|
for (const entry of def.cases) {
|
|
7390
|
-
const condition = entry.keys.map((key) =>
|
|
7391
|
-
emitSelectedTail(entry
|
|
7624
|
+
const condition = entry.keys.map((key) => emitDispatchKeyCondition(keyV, key, entry.caseInsensitive)).join(" || ");
|
|
7625
|
+
emitSelectedTail(entry, wroteBranch ? "else if" : "if", condition, coverageIds[coverageIndex++]);
|
|
7392
7626
|
wroteBranch = true;
|
|
7393
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
|
+
};
|
|
7394
7634
|
if (def.otherwise) {
|
|
7395
|
-
|
|
7396
|
-
|
|
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++]);
|
|
7397
7639
|
} else {
|
|
7640
|
+
emitMatcherTails();
|
|
7398
7641
|
const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
|
|
7399
7642
|
const fail = failArrBody(ctx, expected, selector.endVar);
|
|
7400
7643
|
stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
|
|
7401
7644
|
}
|
|
7402
7645
|
return { stmts, valueVar: outV, endVar: outE };
|
|
7403
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
|
+
}
|
|
7404
7668
|
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
7405
7669
|
const superParser = def.parsers[superIndex];
|
|
7406
7670
|
const regexDef = getCoreRegexDef(superParser);
|
|
@@ -8055,6 +8319,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
8055
8319
|
endVar: pos
|
|
8056
8320
|
};
|
|
8057
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
|
+
}
|
|
8058
8334
|
function escapeTokenLiteral(value) {
|
|
8059
8335
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
8060
8336
|
}
|
|
@@ -8520,7 +8796,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8520
8796
|
case "choice":
|
|
8521
8797
|
return emitChoice(p, def, ctx, pos);
|
|
8522
8798
|
case "dispatch":
|
|
8523
|
-
return emitDispatchCombinator(def, ctx, pos);
|
|
8799
|
+
return emitDispatchCombinator(p, def, ctx, pos);
|
|
8524
8800
|
case "attempt":
|
|
8525
8801
|
return emitAttempt(p, def, ctx, pos);
|
|
8526
8802
|
case "many":
|
|
@@ -8645,6 +8921,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8645
8921
|
return emitNot(def, ctx, pos);
|
|
8646
8922
|
case "peek":
|
|
8647
8923
|
return emitPeek(def, ctx, pos);
|
|
8924
|
+
case "routed":
|
|
8925
|
+
return emitRouted(ctx, pos);
|
|
8648
8926
|
case "node":
|
|
8649
8927
|
return emitNode(def, ctx, pos);
|
|
8650
8928
|
case "scanTo":
|
|
@@ -8742,7 +9020,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8742
9020
|
case "choice":
|
|
8743
9021
|
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
8744
9022
|
case "dispatch":
|
|
8745
|
-
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
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);
|
|
8746
9024
|
case "sepBy":
|
|
8747
9025
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
8748
9026
|
case "scanTo":
|
|
@@ -8757,6 +9035,26 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8757
9035
|
return false;
|
|
8758
9036
|
}
|
|
8759
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
|
+
}
|
|
8760
9058
|
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
8761
9059
|
if (seen.has(p)) return false;
|
|
8762
9060
|
seen.add(p);
|
|
@@ -8777,7 +9075,7 @@ function childrenOf2(def) {
|
|
|
8777
9075
|
case "choice":
|
|
8778
9076
|
return def.parsers;
|
|
8779
9077
|
case "dispatch":
|
|
8780
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9078
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
8781
9079
|
case "many":
|
|
8782
9080
|
case "attempt":
|
|
8783
9081
|
case "optional":
|
|
@@ -8814,6 +9112,7 @@ function childrenOf2(def) {
|
|
|
8814
9112
|
case "regex":
|
|
8815
9113
|
case "keywords":
|
|
8816
9114
|
case "guard":
|
|
9115
|
+
case "routed":
|
|
8817
9116
|
case "unknown":
|
|
8818
9117
|
return [];
|
|
8819
9118
|
}
|
|
@@ -9647,7 +9946,7 @@ function childrenOf3(def) {
|
|
|
9647
9946
|
case "choice":
|
|
9648
9947
|
return def.parsers;
|
|
9649
9948
|
case "dispatch":
|
|
9650
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9949
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
9651
9950
|
case "many":
|
|
9652
9951
|
case "oneOrMore":
|
|
9653
9952
|
case "optional":
|
|
@@ -9676,6 +9975,7 @@ function childrenOf3(def) {
|
|
|
9676
9975
|
case "literal":
|
|
9677
9976
|
case "regex":
|
|
9678
9977
|
case "keywords":
|
|
9978
|
+
case "routed":
|
|
9679
9979
|
case "guard":
|
|
9680
9980
|
case "recover":
|
|
9681
9981
|
case "unknown":
|
|
@@ -9700,6 +10000,16 @@ function repeatOpts(min, max, trailing) {
|
|
|
9700
10000
|
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
9701
10001
|
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
9702
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
|
+
}
|
|
9703
10013
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
9704
10014
|
try {
|
|
9705
10015
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -9799,7 +10109,11 @@ function evalRuleMapIR(ir) {
|
|
|
9799
10109
|
"choice",
|
|
9800
10110
|
"dispatch",
|
|
9801
10111
|
"when",
|
|
10112
|
+
"startsWith",
|
|
10113
|
+
"endsWith",
|
|
10114
|
+
"matches",
|
|
9802
10115
|
"otherwise",
|
|
10116
|
+
"routed",
|
|
9803
10117
|
"attempt",
|
|
9804
10118
|
"many",
|
|
9805
10119
|
"oneOrMore",
|
|
@@ -9835,7 +10149,11 @@ function evalRuleMapIR(ir) {
|
|
|
9835
10149
|
choice,
|
|
9836
10150
|
dispatch,
|
|
9837
10151
|
when,
|
|
10152
|
+
startsWith,
|
|
10153
|
+
endsWith,
|
|
10154
|
+
matches,
|
|
9838
10155
|
otherwise,
|
|
10156
|
+
routed,
|
|
9839
10157
|
attempt,
|
|
9840
10158
|
many,
|
|
9841
10159
|
oneOrMore,
|
|
@@ -10023,8 +10341,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
10023
10341
|
}
|
|
10024
10342
|
case "dispatch": {
|
|
10025
10343
|
const arms = def.cases.map(
|
|
10026
|
-
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)})`
|
|
10344
|
+
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`
|
|
10027
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
|
+
}
|
|
10028
10351
|
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
|
|
10029
10352
|
return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
|
|
10030
10353
|
}
|
|
@@ -10044,6 +10367,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
10044
10367
|
return `not(${kid(def.parser)})`;
|
|
10045
10368
|
case "peek":
|
|
10046
10369
|
return `peek(${kid(def.parser)})`;
|
|
10370
|
+
case "routed":
|
|
10371
|
+
return "routed()";
|
|
10047
10372
|
case "trivia":
|
|
10048
10373
|
return `trivia(${kid(def.parser)})`;
|
|
10049
10374
|
case "token":
|
|
@@ -10513,6 +10838,9 @@ function stripTsFromSource(node2, code) {
|
|
|
10513
10838
|
function isWordFactory(v2) {
|
|
10514
10839
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
10515
10840
|
}
|
|
10841
|
+
function isWhenFactory(v2) {
|
|
10842
|
+
return !!v2 && typeof v2 === "object" && v2.tag === "whenFactory";
|
|
10843
|
+
}
|
|
10516
10844
|
function isStaticValueEntry(v2) {
|
|
10517
10845
|
return !!v2 && typeof v2 === "object" && "value" in v2 && "mfSrcs" in v2;
|
|
10518
10846
|
}
|
|
@@ -10535,6 +10863,23 @@ function wordFactoryFromArgs(args, scope, code, mfs) {
|
|
|
10535
10863
|
if (typeof caseInsensitive !== "boolean") return null;
|
|
10536
10864
|
return { tag: "wordFactory", boundary: "_0-9A-Za-z", caseInsensitive };
|
|
10537
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
|
+
}
|
|
10538
10883
|
var SUPPORTED = {
|
|
10539
10884
|
literal: (...a) => literal(a[0], a[1]),
|
|
10540
10885
|
regex: (...a) => regex(a[0], a[1]),
|
|
@@ -10550,6 +10895,7 @@ var SUPPORTED = {
|
|
|
10550
10895
|
field: (...a) => field(a[0], a[1]),
|
|
10551
10896
|
noTrivia: (...a) => noTrivia(a[0]),
|
|
10552
10897
|
token: (...a) => token(a[0]),
|
|
10898
|
+
routed: () => routed(),
|
|
10553
10899
|
leaf: (...a) => leaf(a[0], a[1]),
|
|
10554
10900
|
expect: (...a) => expect(a[0], a[1])
|
|
10555
10901
|
};
|
|
@@ -10593,6 +10939,18 @@ function dispatchArmValue(node2, scope, code, mfs) {
|
|
|
10593
10939
|
if (node2.type !== "CallExpression") return null;
|
|
10594
10940
|
const callee = node2.callee;
|
|
10595
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
|
+
}
|
|
10596
10954
|
if (callee.name === "otherwise") {
|
|
10597
10955
|
const [parserArg] = node2.arguments;
|
|
10598
10956
|
if (!parserArg || parserArg.type === "SpreadElement") return null;
|
|
@@ -10601,13 +10959,16 @@ function dispatchArmValue(node2, scope, code, mfs) {
|
|
|
10601
10959
|
return otherwise(parserValue);
|
|
10602
10960
|
}
|
|
10603
10961
|
if (callee.name === "when") {
|
|
10604
|
-
const [keyArg, parserArg] = node2.arguments;
|
|
10605
|
-
if (!keyArg || !parserArg || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
|
|
10962
|
+
const [keyArg, parserArg, optsArg] = node2.arguments;
|
|
10963
|
+
if (!keyArg || !parserArg || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || optsArg?.type === "SpreadElement") return null;
|
|
10606
10964
|
const key = anyValue(keyArg, scope, code, mfs);
|
|
10607
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;
|
|
10608
10968
|
if (!isCombinator(parserValue)) return null;
|
|
10609
|
-
if (typeof key === "string") return when(key, parserValue);
|
|
10610
|
-
if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue);
|
|
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);
|
|
10611
10972
|
}
|
|
10612
10973
|
return null;
|
|
10613
10974
|
}
|
|
@@ -10618,7 +10979,16 @@ function isDispatchArm(v2) {
|
|
|
10618
10979
|
if (!v2 || typeof v2 !== "object") return false;
|
|
10619
10980
|
const rec = v2;
|
|
10620
10981
|
if (rec.kind === "otherwise") return isCombinator(rec.parser);
|
|
10621
|
-
|
|
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";
|
|
10622
10992
|
}
|
|
10623
10993
|
function scopeGet(scope, name, mfs) {
|
|
10624
10994
|
const entry = scope.get(name);
|
|
@@ -11004,9 +11374,27 @@ function anyValue(node2, scope, code, mfs) {
|
|
|
11004
11374
|
if (callee.type === "Identifier" && callee.name === "makeWord") {
|
|
11005
11375
|
return wordFactoryFromArgs(node2.arguments, scope, code, mfs);
|
|
11006
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
|
+
}
|
|
11007
11392
|
if (callee.type === "Identifier" && (callee.name === "when" || callee.name === "otherwise")) {
|
|
11008
11393
|
return dispatchArmValue(node2, scope, code, mfs);
|
|
11009
11394
|
}
|
|
11395
|
+
if (callee.type === "Identifier" && isWhenFactory(scope.get(callee.name))) {
|
|
11396
|
+
return dispatchArmValue(node2, scope, code, mfs);
|
|
11397
|
+
}
|
|
11010
11398
|
return exprToCombi(node2, scope, code, mfs);
|
|
11011
11399
|
}
|
|
11012
11400
|
return null;
|
|
@@ -11017,6 +11405,12 @@ function evaluateWordFactory(node2, scope, code) {
|
|
|
11017
11405
|
if (callee.type !== "Identifier" || callee.name !== "makeWord") return null;
|
|
11018
11406
|
return wordFactoryFromArgs(node2.arguments, scope, code);
|
|
11019
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
|
+
}
|
|
11020
11414
|
function evaluateExpr(node2, scope, code, mapFnSources) {
|
|
11021
11415
|
return exprToCombi(node2, scope, code, mapFnSources);
|
|
11022
11416
|
}
|
|
@@ -11597,7 +11991,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
11597
11991
|
for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
|
|
11598
11992
|
return [...ids].sort().map((id2) => ({
|
|
11599
11993
|
id: id2,
|
|
11600
|
-
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"
|
|
11601
11995
|
}));
|
|
11602
11996
|
};
|
|
11603
11997
|
const localComposedCarried = /* @__PURE__ */ new Map();
|
|
@@ -12043,6 +12437,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
12043
12437
|
if (wordFactory) {
|
|
12044
12438
|
;
|
|
12045
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" });
|
|
12046
12448
|
continue;
|
|
12047
12449
|
}
|
|
12048
12450
|
const combiArray = evaluateCombinatorArray(init, scope, code);
|