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/index.js
CHANGED
|
@@ -51,6 +51,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
51
51
|
return d.value.length === 0;
|
|
52
52
|
case "keywords":
|
|
53
53
|
return false;
|
|
54
|
+
case "routed":
|
|
55
|
+
return false;
|
|
54
56
|
case "regex":
|
|
55
57
|
try {
|
|
56
58
|
const m = new RegExp(d.source).exec("");
|
|
@@ -1112,6 +1114,8 @@ function derive(c, seen) {
|
|
|
1112
1114
|
case "not":
|
|
1113
1115
|
case "peek":
|
|
1114
1116
|
return deriveExpected2(def.parser);
|
|
1117
|
+
case "routed":
|
|
1118
|
+
return ["routed()"];
|
|
1115
1119
|
case "lazy": {
|
|
1116
1120
|
const name = c._ruleName;
|
|
1117
1121
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -1936,42 +1940,198 @@ function sequence(...parsers) {
|
|
|
1936
1940
|
}
|
|
1937
1941
|
|
|
1938
1942
|
// src/combinators/dispatch.ts
|
|
1939
|
-
function
|
|
1943
|
+
function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
1944
|
+
if (seen.has(parser2)) return false;
|
|
1945
|
+
seen.add(parser2);
|
|
1946
|
+
const def = parser2._def;
|
|
1947
|
+
switch (def.tag) {
|
|
1948
|
+
case "routed":
|
|
1949
|
+
return true;
|
|
1950
|
+
case "sequence":
|
|
1951
|
+
case "choice":
|
|
1952
|
+
return def.parsers.some((entry) => parserUsesRouted(entry, seen));
|
|
1953
|
+
case "dispatch":
|
|
1954
|
+
return false;
|
|
1955
|
+
case "sepBy":
|
|
1956
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.separator, seen);
|
|
1957
|
+
case "skip":
|
|
1958
|
+
return parserUsesRouted(def.main, seen) || parserUsesRouted(def.skipped, seen);
|
|
1959
|
+
case "grammar":
|
|
1960
|
+
return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
|
|
1961
|
+
case "scanTo":
|
|
1962
|
+
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
|
|
1963
|
+
case "recover":
|
|
1964
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
1965
|
+
case "lazy":
|
|
1966
|
+
try {
|
|
1967
|
+
return parserUsesRouted(def.thunk(), seen);
|
|
1968
|
+
} catch {
|
|
1969
|
+
return false;
|
|
1970
|
+
}
|
|
1971
|
+
case "many":
|
|
1972
|
+
case "oneOrMore":
|
|
1973
|
+
case "optional":
|
|
1974
|
+
case "attempt":
|
|
1975
|
+
case "transform":
|
|
1976
|
+
case "trivia":
|
|
1977
|
+
case "token":
|
|
1978
|
+
case "leaf":
|
|
1979
|
+
case "label":
|
|
1980
|
+
case "field":
|
|
1981
|
+
case "not":
|
|
1982
|
+
case "peek":
|
|
1983
|
+
case "node":
|
|
1984
|
+
case "withCtx":
|
|
1985
|
+
case "expect":
|
|
1986
|
+
return parserUsesRouted(def.parser, seen);
|
|
1987
|
+
default:
|
|
1988
|
+
return false;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function branchUsesRouted(branch) {
|
|
1992
|
+
return branch.usesRouted === true || parserUsesRouted(branch.parser);
|
|
1993
|
+
}
|
|
1994
|
+
function asciiFoldKey(key) {
|
|
1995
|
+
let out = "";
|
|
1996
|
+
for (let i = 0; i < key.length; i++) {
|
|
1997
|
+
const c = key.charCodeAt(i);
|
|
1998
|
+
out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
1999
|
+
}
|
|
2000
|
+
return out;
|
|
2001
|
+
}
|
|
2002
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2003
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2004
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2005
|
+
switch (matcher.kind) {
|
|
2006
|
+
case "startsWith":
|
|
2007
|
+
return candidate.startsWith(expected);
|
|
2008
|
+
case "endsWith":
|
|
2009
|
+
return candidate.endsWith(expected);
|
|
2010
|
+
case "matches": {
|
|
2011
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2012
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
function isStringMatcher(value) {
|
|
2017
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2018
|
+
const kind = value.kind;
|
|
2019
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2020
|
+
}
|
|
2021
|
+
function fixedMatcher(kind, value) {
|
|
2022
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2023
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2024
|
+
return { kind, value };
|
|
2025
|
+
}
|
|
2026
|
+
function startsWith(prefix) {
|
|
2027
|
+
return fixedMatcher("startsWith", prefix);
|
|
2028
|
+
}
|
|
2029
|
+
function endsWith(suffix) {
|
|
2030
|
+
return fixedMatcher("endsWith", suffix);
|
|
2031
|
+
}
|
|
2032
|
+
function matches(pattern) {
|
|
2033
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2034
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2035
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2036
|
+
}
|
|
2037
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2038
|
+
}
|
|
2039
|
+
function when(key, parser2, opts = {}) {
|
|
2040
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2041
|
+
if (isStringMatcher(key)) {
|
|
2042
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2043
|
+
}
|
|
1940
2044
|
const keys = Array.isArray(key) ? [...key] : [key];
|
|
1941
2045
|
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
1942
2046
|
for (const item of keys) {
|
|
1943
2047
|
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
1944
2048
|
}
|
|
1945
|
-
return { kind: "when", keys, parser: parser2 };
|
|
2049
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2050
|
+
}
|
|
2051
|
+
function makeWhen(opts = {}) {
|
|
2052
|
+
return (key, parser2) => when(key, parser2, opts);
|
|
1946
2053
|
}
|
|
1947
2054
|
function otherwise(parser2) {
|
|
1948
|
-
return { kind: "otherwise", parser: parser2 };
|
|
2055
|
+
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2056
|
+
}
|
|
2057
|
+
function routed() {
|
|
2058
|
+
return {
|
|
2059
|
+
_tag: "routed",
|
|
2060
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
2061
|
+
_def: { tag: "routed" },
|
|
2062
|
+
parse(_input, pos, ctx) {
|
|
2063
|
+
const item = ctx._routed;
|
|
2064
|
+
if (item === void 0) {
|
|
2065
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2066
|
+
}
|
|
2067
|
+
if (pos !== item.span.start) {
|
|
2068
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2069
|
+
}
|
|
2070
|
+
if (cstCaptureActive(ctx)) {
|
|
2071
|
+
pushCstLeaf(ctx, { _tag: "leaf", value: item.value, span: item.span });
|
|
2072
|
+
}
|
|
2073
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
1949
2076
|
}
|
|
1950
2077
|
function dispatch(selector, ...arms) {
|
|
2078
|
+
if (parserUsesRouted(selector)) {
|
|
2079
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2080
|
+
}
|
|
1951
2081
|
let fallback;
|
|
2082
|
+
let fallbackUsesRouted = false;
|
|
2083
|
+
const matchers = [];
|
|
1952
2084
|
const cases = [];
|
|
1953
|
-
const seen =
|
|
2085
|
+
const seen = [];
|
|
1954
2086
|
for (let i = 0; i < arms.length; i++) {
|
|
1955
2087
|
const arm = arms[i];
|
|
1956
2088
|
if (arm.kind === "otherwise") {
|
|
1957
2089
|
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
1958
2090
|
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
1959
2091
|
fallback = arm.parser;
|
|
2092
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
1960
2093
|
continue;
|
|
1961
2094
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2095
|
+
if (arm.kind === "whenMatcher") {
|
|
2096
|
+
const matcher = arm.matcher;
|
|
2097
|
+
matchers.push({
|
|
2098
|
+
kind: matcher.kind,
|
|
2099
|
+
value: matcher.value,
|
|
2100
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2101
|
+
parser: arm.parser,
|
|
2102
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2103
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2104
|
+
});
|
|
2105
|
+
continue;
|
|
1965
2106
|
}
|
|
1966
|
-
|
|
2107
|
+
for (const key of arm.keys) {
|
|
2108
|
+
const folded = asciiFoldKey(key);
|
|
2109
|
+
for (const prior of seen) {
|
|
2110
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2111
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2112
|
+
}
|
|
2113
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2114
|
+
}
|
|
2115
|
+
cases.push({
|
|
2116
|
+
keys: arm.keys,
|
|
2117
|
+
parser: arm.parser,
|
|
2118
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2119
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2120
|
+
});
|
|
1967
2121
|
}
|
|
1968
2122
|
const meta = {
|
|
1969
2123
|
firstSet: selector._meta.firstSet,
|
|
1970
|
-
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2124
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
1971
2125
|
isTrivia: false
|
|
1972
2126
|
};
|
|
1973
2127
|
const byKey = /* @__PURE__ */ new Map();
|
|
1974
|
-
|
|
2128
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2129
|
+
for (const entry of cases) {
|
|
2130
|
+
for (const key of entry.keys) {
|
|
2131
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2132
|
+
else byKey.set(key, entry);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
1975
2135
|
return {
|
|
1976
2136
|
_tag: "dispatch",
|
|
1977
2137
|
_meta: meta,
|
|
@@ -1979,18 +2139,35 @@ function dispatch(selector, ...arms) {
|
|
|
1979
2139
|
tag: "dispatch",
|
|
1980
2140
|
selector,
|
|
1981
2141
|
cases,
|
|
1982
|
-
...
|
|
2142
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2143
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
1983
2144
|
},
|
|
1984
2145
|
parse(input, pos, ctx) {
|
|
2146
|
+
if (parserUsesRouted(selector)) {
|
|
2147
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2148
|
+
}
|
|
2149
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
1985
2150
|
const selected = selector.parse(input, pos, ctx);
|
|
1986
2151
|
if (!selected.ok) return selected;
|
|
1987
|
-
const
|
|
1988
|
-
if (
|
|
2152
|
+
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 });
|
|
2153
|
+
if (branch === void 0) {
|
|
1989
2154
|
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
1990
2155
|
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
1991
2156
|
}
|
|
1992
|
-
const
|
|
1993
|
-
const
|
|
2157
|
+
const savedRouted = ctx._routed;
|
|
2158
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2159
|
+
let mark = saveTriviaMark(ctx);
|
|
2160
|
+
if (usesRouted) {
|
|
2161
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2162
|
+
mark = saveTriviaMark(ctx);
|
|
2163
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2164
|
+
}
|
|
2165
|
+
let result;
|
|
2166
|
+
try {
|
|
2167
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2168
|
+
} finally {
|
|
2169
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2170
|
+
}
|
|
1994
2171
|
if (!result.ok) {
|
|
1995
2172
|
rollbackTrivia(ctx, mark);
|
|
1996
2173
|
return { ...result, committed: true };
|
|
@@ -2377,6 +2554,7 @@ function markUnusedValues(root) {
|
|
|
2377
2554
|
case "dispatch":
|
|
2378
2555
|
visit(def.selector, consumed);
|
|
2379
2556
|
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
2557
|
+
if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
|
|
2380
2558
|
if (def.otherwise) visit(def.otherwise, consumed);
|
|
2381
2559
|
return;
|
|
2382
2560
|
case "node":
|
|
@@ -2655,7 +2833,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2655
2833
|
case "choice":
|
|
2656
2834
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2657
2835
|
case "dispatch":
|
|
2658
|
-
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2836
|
+
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);
|
|
2659
2837
|
case "sepBy":
|
|
2660
2838
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2661
2839
|
case "skip":
|
|
@@ -2724,7 +2902,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2724
2902
|
case "choice":
|
|
2725
2903
|
return d.parsers.some((x) => parserHasTriviaSite(x, seen));
|
|
2726
2904
|
case "dispatch":
|
|
2727
|
-
return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
|
|
2905
|
+
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);
|
|
2728
2906
|
case "grammar":
|
|
2729
2907
|
return parserHasTriviaSite(d.parser, seen);
|
|
2730
2908
|
case "lazy": {
|
|
@@ -2751,7 +2929,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2751
2929
|
case "choice":
|
|
2752
2930
|
return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
|
|
2753
2931
|
case "dispatch":
|
|
2754
|
-
return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
|
|
2932
|
+
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);
|
|
2755
2933
|
case "sepBy":
|
|
2756
2934
|
return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
|
|
2757
2935
|
case "skip":
|
|
@@ -3153,7 +3331,7 @@ function leaf(root, fn) {
|
|
|
3153
3331
|
}
|
|
3154
3332
|
|
|
3155
3333
|
// src/version.ts
|
|
3156
|
-
var PARSEMAN_VERSION = "0.39.
|
|
3334
|
+
var PARSEMAN_VERSION = "0.39.1";
|
|
3157
3335
|
|
|
3158
3336
|
// src/cst/host-mode.ts
|
|
3159
3337
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3413,6 +3591,7 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3413
3591
|
if (d.tag === "dispatch") {
|
|
3414
3592
|
kids.push(d.selector);
|
|
3415
3593
|
for (const c of d.cases) kids.push(c.parser);
|
|
3594
|
+
if (d.matchers) for (const c of d.matchers) kids.push(c.parser);
|
|
3416
3595
|
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3417
3596
|
}
|
|
3418
3597
|
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
@@ -4936,13 +5115,25 @@ function escapeClassChar(ch2) {
|
|
|
4936
5115
|
}
|
|
4937
5116
|
|
|
4938
5117
|
// src/compiler/grammar-coverage-ids.ts
|
|
5118
|
+
function idPart(value) {
|
|
5119
|
+
return encodeURIComponent(value);
|
|
5120
|
+
}
|
|
5121
|
+
function dispatchArmIds(def, path) {
|
|
5122
|
+
const ids = [];
|
|
5123
|
+
for (const entry of def.cases) ids.push(`dispatch:${path}/when:${entry.keys.map(idPart).join("|")}`);
|
|
5124
|
+
for (const entry of def.matchers ?? []) {
|
|
5125
|
+
ids.push(`dispatch:${path}/matcher:${entry.kind}:${idPart(entry.value)}${entry.kind === "matches" && entry.flags ? `:${idPart(entry.flags)}` : ""}`);
|
|
5126
|
+
}
|
|
5127
|
+
if (def.otherwise) ids.push(`dispatch:${path}/otherwise`);
|
|
5128
|
+
return ids;
|
|
5129
|
+
}
|
|
4939
5130
|
function children(def, winners) {
|
|
4940
5131
|
switch (def.tag) {
|
|
4941
5132
|
case "sequence":
|
|
4942
5133
|
case "choice":
|
|
4943
5134
|
return def.parsers;
|
|
4944
5135
|
case "dispatch":
|
|
4945
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
5136
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
4946
5137
|
case "many":
|
|
4947
5138
|
case "oneOrMore":
|
|
4948
5139
|
case "optional":
|
|
@@ -4985,6 +5176,7 @@ function children(def, winners) {
|
|
|
4985
5176
|
function buildGrammarPlan(entry, winners) {
|
|
4986
5177
|
const definitions = /* @__PURE__ */ new Map();
|
|
4987
5178
|
const choices = /* @__PURE__ */ new WeakMap();
|
|
5179
|
+
const dispatches = /* @__PURE__ */ new WeakMap();
|
|
4988
5180
|
const attempts = /* @__PURE__ */ new WeakMap();
|
|
4989
5181
|
const labels = /* @__PURE__ */ new WeakMap();
|
|
4990
5182
|
const rules2 = /* @__PURE__ */ new WeakMap();
|
|
@@ -5015,6 +5207,11 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5015
5207
|
choices.set(parser2, ids);
|
|
5016
5208
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
5017
5209
|
}
|
|
5210
|
+
if (parser2._def.tag === "dispatch") {
|
|
5211
|
+
const ids = dispatchArmIds(parser2._def, path);
|
|
5212
|
+
dispatches.set(parser2, ids);
|
|
5213
|
+
ids.forEach((id) => definitions.set(id, { id, kind: "dispatch-arm" }));
|
|
5214
|
+
}
|
|
5018
5215
|
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path}`);
|
|
5019
5216
|
if (parser2._def.tag === "label") {
|
|
5020
5217
|
const id = `label:${path}`;
|
|
@@ -5027,7 +5224,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5027
5224
|
for (const root of roots) {
|
|
5028
5225
|
visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
|
|
5029
5226
|
}
|
|
5030
|
-
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
|
|
5227
|
+
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
|
|
5031
5228
|
}
|
|
5032
5229
|
|
|
5033
5230
|
// src/compiler/scannable-run.ts
|
|
@@ -6401,6 +6598,7 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6401
6598
|
case "literal":
|
|
6402
6599
|
case "regex":
|
|
6403
6600
|
case "keywords":
|
|
6601
|
+
case "routed":
|
|
6404
6602
|
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
6405
6603
|
case "choice": {
|
|
6406
6604
|
const alts = [];
|
|
@@ -6751,7 +6949,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6751
6949
|
case "choice":
|
|
6752
6950
|
return false;
|
|
6753
6951
|
case "dispatch":
|
|
6754
|
-
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);
|
|
6952
|
+
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);
|
|
6755
6953
|
case "attempt":
|
|
6756
6954
|
return false;
|
|
6757
6955
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
@@ -6850,6 +7048,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6850
7048
|
case "keywords":
|
|
6851
7049
|
case "node":
|
|
6852
7050
|
case "token":
|
|
7051
|
+
case "routed":
|
|
6853
7052
|
case "leaf":
|
|
6854
7053
|
return true;
|
|
6855
7054
|
case "not":
|
|
@@ -6862,7 +7061,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6862
7061
|
case "choice":
|
|
6863
7062
|
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
6864
7063
|
case "dispatch":
|
|
6865
|
-
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
7064
|
+
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);
|
|
6866
7065
|
case "sepBy":
|
|
6867
7066
|
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6868
7067
|
case "many":
|
|
@@ -7340,6 +7539,7 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7340
7539
|
for (const item of d.cases) {
|
|
7341
7540
|
if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
|
|
7342
7541
|
}
|
|
7542
|
+
if (d.matchers?.some((item) => mayRecordRecoveryError(item.parser, recovery, seen))) return true;
|
|
7343
7543
|
return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
|
|
7344
7544
|
}
|
|
7345
7545
|
case "attempt":
|
|
@@ -7374,53 +7574,120 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7374
7574
|
return false;
|
|
7375
7575
|
}
|
|
7376
7576
|
}
|
|
7377
|
-
function emitDispatchCombinator(def, ctx, pos) {
|
|
7577
|
+
function emitDispatchCombinator(parser2, def, ctx, pos) {
|
|
7578
|
+
if (parserUsesRouted2(def.selector)) {
|
|
7579
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
7580
|
+
}
|
|
7581
|
+
const coverageIds = ctx.coverage?.plan.dispatches.get(parser2) ?? [];
|
|
7582
|
+
const branchUsesRouted2 = (entry) => entry.usesRouted === true || parserUsesRouted2(entry.parser);
|
|
7583
|
+
const hasRoutedBranch = def.cases.some(branchUsesRouted2) || (def.matchers ? def.matchers.some(branchUsesRouted2) : false) || (def.otherwise ? def.otherwiseUsesRouted === true || parserUsesRouted2(def.otherwise) : false);
|
|
7584
|
+
const selectorLeafMark = hasRoutedBranch ? v(ctx, "_dsl") : null;
|
|
7585
|
+
const selectorRawMark = hasRoutedBranch ? v(ctx, "_dsr") : null;
|
|
7586
|
+
const selectorTriviaMark = hasRoutedBranch ? v(ctx, "_dst") : null;
|
|
7587
|
+
const selectorLogMark = hasRoutedBranch ? v(ctx, "_dsg") : null;
|
|
7588
|
+
const selectorFieldMark = hasRoutedBranch ? v(ctx, "_dsf") : null;
|
|
7589
|
+
const selectorErrorMark = hasRoutedBranch ? v(ctx, "_dse") : null;
|
|
7378
7590
|
const selector = emit(def.selector, ctx, pos);
|
|
7379
7591
|
const outV = v(ctx, "_dval");
|
|
7380
7592
|
const outE = v(ctx, "_dend");
|
|
7381
7593
|
const keyV = v(ctx, "_dkey");
|
|
7382
7594
|
const stmts = [
|
|
7595
|
+
...selectorLeafMark ? [
|
|
7596
|
+
`${ind(ctx)}const ${selectorLeafMark} = _ctx._cstLeaves?.length ?? 0`,
|
|
7597
|
+
`${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
|
|
7598
|
+
`${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
7599
|
+
`${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
|
|
7600
|
+
`${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
|
|
7601
|
+
`${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
|
|
7602
|
+
] : [],
|
|
7383
7603
|
...selector.stmts,
|
|
7384
7604
|
`${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
|
|
7385
7605
|
`${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
|
|
7386
7606
|
];
|
|
7387
7607
|
let wroteBranch = false;
|
|
7388
|
-
const emitSelectedTail = (
|
|
7608
|
+
const emitSelectedTail = (entry, keyword, condition, coverageId) => {
|
|
7609
|
+
const parser3 = entry.parser;
|
|
7389
7610
|
const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
|
|
7390
7611
|
stmts.push(head);
|
|
7391
7612
|
ctx.indent++;
|
|
7392
|
-
const
|
|
7613
|
+
const usesRouted = branchUsesRouted2(entry);
|
|
7614
|
+
const routedV = usesRouted ? v(ctx, "_drt") : null;
|
|
7615
|
+
const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
|
|
7393
7616
|
const errMark = mayError ? v(ctx, "_derr") : null;
|
|
7394
7617
|
const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
|
|
7618
|
+
if (routedV) {
|
|
7619
|
+
stmts.push(
|
|
7620
|
+
`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`,
|
|
7621
|
+
...selectorLeafMark ? [
|
|
7622
|
+
`${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}`
|
|
7623
|
+
] : []
|
|
7624
|
+
);
|
|
7625
|
+
}
|
|
7395
7626
|
if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
|
|
7396
7627
|
if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
|
|
7397
|
-
const tail = emitTailFallible(
|
|
7628
|
+
const tail = emitTailFallible(parser3, ctx, usesRouted ? pos : selector.endVar);
|
|
7629
|
+
stmts.push(...coverageAttempt(ctx, coverageId, pos));
|
|
7398
7630
|
const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
|
|
7399
7631
|
const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
|
|
7632
|
+
const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
|
|
7633
|
+
const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
|
|
7400
7634
|
stmts.push(
|
|
7401
7635
|
...tail.stmts,
|
|
7402
|
-
`${ind(ctx)}if (!${tail.okVar}) { ${errorRollback}${logRollback}${committedFailBody(ctx)} }`,
|
|
7636
|
+
`${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
|
|
7637
|
+
...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
|
|
7403
7638
|
`${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
|
|
7404
|
-
`${ind(ctx)}${outE} = ${tail.endVar}
|
|
7639
|
+
`${ind(ctx)}${outE} = ${tail.endVar}`,
|
|
7640
|
+
...coverageHit(ctx, coverageId, pos, tail.endVar)
|
|
7405
7641
|
);
|
|
7406
7642
|
ctx.indent--;
|
|
7407
7643
|
stmts.push(`${ind(ctx)}}`);
|
|
7408
7644
|
};
|
|
7645
|
+
let coverageIndex = 0;
|
|
7409
7646
|
for (const entry of def.cases) {
|
|
7410
|
-
const condition = entry.keys.map((key) =>
|
|
7411
|
-
emitSelectedTail(entry
|
|
7647
|
+
const condition = entry.keys.map((key) => emitDispatchKeyCondition(keyV, key, entry.caseInsensitive)).join(" || ");
|
|
7648
|
+
emitSelectedTail(entry, wroteBranch ? "else if" : "if", condition, coverageIds[coverageIndex++]);
|
|
7412
7649
|
wroteBranch = true;
|
|
7413
7650
|
}
|
|
7651
|
+
const emitMatcherTails = () => {
|
|
7652
|
+
for (const matcher of def.matchers ?? []) {
|
|
7653
|
+
emitSelectedTail(matcher, wroteBranch ? "else if" : "if", emitDispatchMatcherCondition(keyV, matcher), coverageIds[coverageIndex++]);
|
|
7654
|
+
wroteBranch = true;
|
|
7655
|
+
}
|
|
7656
|
+
};
|
|
7414
7657
|
if (def.otherwise) {
|
|
7415
|
-
|
|
7416
|
-
|
|
7658
|
+
emitMatcherTails();
|
|
7659
|
+
const otherwiseEntry = { parser: def.otherwise, usesRouted: def.otherwiseUsesRouted };
|
|
7660
|
+
if (wroteBranch) emitSelectedTail(otherwiseEntry, "else", void 0, coverageIds[coverageIndex++]);
|
|
7661
|
+
else emitSelectedTail(otherwiseEntry, "if", "true", coverageIds[coverageIndex++]);
|
|
7417
7662
|
} else {
|
|
7663
|
+
emitMatcherTails();
|
|
7418
7664
|
const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
|
|
7419
7665
|
const fail = failArrBody(ctx, expected, selector.endVar);
|
|
7420
7666
|
stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
|
|
7421
7667
|
}
|
|
7422
7668
|
return { stmts, valueVar: outV, endVar: outE };
|
|
7423
7669
|
}
|
|
7670
|
+
function emitDispatchKeyCondition(valueVar, key, caseInsensitive) {
|
|
7671
|
+
if (!caseInsensitive) return `${valueVar} === ${JSON.stringify(key)}`;
|
|
7672
|
+
const parts = [`${valueVar}.length === ${key.length}`];
|
|
7673
|
+
for (let i = 0; i < key.length; i++) {
|
|
7674
|
+
parts.push(foldEq(`${valueVar}.charCodeAt(${i})`, key.charCodeAt(i)));
|
|
7675
|
+
}
|
|
7676
|
+
return parts.join(" && ");
|
|
7677
|
+
}
|
|
7678
|
+
function emitDispatchMatcherCondition(valueVar, matcher) {
|
|
7679
|
+
if (matcher.kind === "matches") {
|
|
7680
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
7681
|
+
return `${new RegExp(matcher.value, flags).toString()}.test(${valueVar})`;
|
|
7682
|
+
}
|
|
7683
|
+
const text = matcher.value;
|
|
7684
|
+
const parts = [`${valueVar}.length >= ${text.length}`];
|
|
7685
|
+
const offset = matcher.kind === "endsWith" ? `${valueVar}.length - ${text.length}` : "0";
|
|
7686
|
+
for (let i = 0; i < text.length; i++) {
|
|
7687
|
+
parts.push(matcher.caseInsensitive ? foldEq(`${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`})`, text.charCodeAt(i)) : `${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`}) === ${text.charCodeAt(i)}`);
|
|
7688
|
+
}
|
|
7689
|
+
return parts.join(" && ");
|
|
7690
|
+
}
|
|
7424
7691
|
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
7425
7692
|
const superParser = def.parsers[superIndex];
|
|
7426
7693
|
const regexDef = getCoreRegexDef(superParser);
|
|
@@ -8075,6 +8342,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
8075
8342
|
endVar: pos
|
|
8076
8343
|
};
|
|
8077
8344
|
}
|
|
8345
|
+
function emitRouted(ctx, pos) {
|
|
8346
|
+
const item = v(ctx, "_rt");
|
|
8347
|
+
return {
|
|
8348
|
+
stmts: [
|
|
8349
|
+
`${ind(ctx)}const ${item} = _ctx._routed`,
|
|
8350
|
+
...emitIfFail(ctx, `${item} === undefined || ${item}.span.start !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
8351
|
+
...emitLeafCapture(ctx, `${item}.value`, `${item}.span.start`, `${item}.span.end`)
|
|
8352
|
+
],
|
|
8353
|
+
valueVar: `${item}.value`,
|
|
8354
|
+
endVar: `${item}.span.end`
|
|
8355
|
+
};
|
|
8356
|
+
}
|
|
8078
8357
|
function escapeTokenLiteral(value) {
|
|
8079
8358
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
8080
8359
|
}
|
|
@@ -8540,7 +8819,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8540
8819
|
case "choice":
|
|
8541
8820
|
return emitChoice(p, def, ctx, pos);
|
|
8542
8821
|
case "dispatch":
|
|
8543
|
-
return emitDispatchCombinator(def, ctx, pos);
|
|
8822
|
+
return emitDispatchCombinator(p, def, ctx, pos);
|
|
8544
8823
|
case "attempt":
|
|
8545
8824
|
return emitAttempt(p, def, ctx, pos);
|
|
8546
8825
|
case "many":
|
|
@@ -8665,6 +8944,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8665
8944
|
return emitNot(def, ctx, pos);
|
|
8666
8945
|
case "peek":
|
|
8667
8946
|
return emitPeek(def, ctx, pos);
|
|
8947
|
+
case "routed":
|
|
8948
|
+
return emitRouted(ctx, pos);
|
|
8668
8949
|
case "node":
|
|
8669
8950
|
return emitNode(def, ctx, pos);
|
|
8670
8951
|
case "scanTo":
|
|
@@ -8762,7 +9043,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8762
9043
|
case "choice":
|
|
8763
9044
|
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
8764
9045
|
case "dispatch":
|
|
8765
|
-
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
9046
|
+
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);
|
|
8766
9047
|
case "sepBy":
|
|
8767
9048
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
8768
9049
|
case "scanTo":
|
|
@@ -8777,6 +9058,26 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8777
9058
|
return false;
|
|
8778
9059
|
}
|
|
8779
9060
|
}
|
|
9061
|
+
function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
|
|
9062
|
+
if (seen.has(p)) return false;
|
|
9063
|
+
seen.add(p);
|
|
9064
|
+
const d = p._def;
|
|
9065
|
+
switch (d.tag) {
|
|
9066
|
+
case "routed":
|
|
9067
|
+
return true;
|
|
9068
|
+
case "lazy": {
|
|
9069
|
+
try {
|
|
9070
|
+
return parserUsesRouted2(d.thunk(), seen);
|
|
9071
|
+
} catch {
|
|
9072
|
+
return false;
|
|
9073
|
+
}
|
|
9074
|
+
}
|
|
9075
|
+
case "dispatch":
|
|
9076
|
+
return false;
|
|
9077
|
+
default:
|
|
9078
|
+
return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
8780
9081
|
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
8781
9082
|
if (seen.has(p)) return false;
|
|
8782
9083
|
seen.add(p);
|
|
@@ -8797,7 +9098,7 @@ function childrenOf2(def) {
|
|
|
8797
9098
|
case "choice":
|
|
8798
9099
|
return def.parsers;
|
|
8799
9100
|
case "dispatch":
|
|
8800
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9101
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
8801
9102
|
case "many":
|
|
8802
9103
|
case "attempt":
|
|
8803
9104
|
case "optional":
|
|
@@ -8834,6 +9135,7 @@ function childrenOf2(def) {
|
|
|
8834
9135
|
case "regex":
|
|
8835
9136
|
case "keywords":
|
|
8836
9137
|
case "guard":
|
|
9138
|
+
case "routed":
|
|
8837
9139
|
case "unknown":
|
|
8838
9140
|
return [];
|
|
8839
9141
|
}
|
|
@@ -9541,7 +9843,7 @@ function childrenOf3(def) {
|
|
|
9541
9843
|
case "choice":
|
|
9542
9844
|
return def.parsers;
|
|
9543
9845
|
case "dispatch":
|
|
9544
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9846
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
9545
9847
|
case "many":
|
|
9546
9848
|
case "oneOrMore":
|
|
9547
9849
|
case "optional":
|
|
@@ -9570,6 +9872,7 @@ function childrenOf3(def) {
|
|
|
9570
9872
|
case "literal":
|
|
9571
9873
|
case "regex":
|
|
9572
9874
|
case "keywords":
|
|
9875
|
+
case "routed":
|
|
9573
9876
|
case "guard":
|
|
9574
9877
|
case "recover":
|
|
9575
9878
|
case "unknown":
|
|
@@ -9594,6 +9897,16 @@ function repeatOpts(min, max, trailing) {
|
|
|
9594
9897
|
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
9595
9898
|
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
9596
9899
|
}
|
|
9900
|
+
function matcherExpr(entry) {
|
|
9901
|
+
switch (entry.kind) {
|
|
9902
|
+
case "startsWith":
|
|
9903
|
+
return `startsWith(${JSON.stringify(entry.value)})`;
|
|
9904
|
+
case "endsWith":
|
|
9905
|
+
return `endsWith(${JSON.stringify(entry.value)})`;
|
|
9906
|
+
case "matches":
|
|
9907
|
+
return `matches(${new RegExp(entry.value, entry.flags ?? "").toString()})`;
|
|
9908
|
+
}
|
|
9909
|
+
}
|
|
9597
9910
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
9598
9911
|
try {
|
|
9599
9912
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -9693,7 +10006,11 @@ function evalRuleMapIR(ir) {
|
|
|
9693
10006
|
"choice",
|
|
9694
10007
|
"dispatch",
|
|
9695
10008
|
"when",
|
|
10009
|
+
"startsWith",
|
|
10010
|
+
"endsWith",
|
|
10011
|
+
"matches",
|
|
9696
10012
|
"otherwise",
|
|
10013
|
+
"routed",
|
|
9697
10014
|
"attempt",
|
|
9698
10015
|
"many",
|
|
9699
10016
|
"oneOrMore",
|
|
@@ -9729,7 +10046,11 @@ function evalRuleMapIR(ir) {
|
|
|
9729
10046
|
choice,
|
|
9730
10047
|
dispatch,
|
|
9731
10048
|
when,
|
|
10049
|
+
startsWith,
|
|
10050
|
+
endsWith,
|
|
10051
|
+
matches,
|
|
9732
10052
|
otherwise,
|
|
10053
|
+
routed,
|
|
9733
10054
|
attempt,
|
|
9734
10055
|
many,
|
|
9735
10056
|
oneOrMore,
|
|
@@ -9917,8 +10238,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9917
10238
|
}
|
|
9918
10239
|
case "dispatch": {
|
|
9919
10240
|
const arms = def.cases.map(
|
|
9920
|
-
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)})`
|
|
10241
|
+
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`
|
|
9921
10242
|
);
|
|
10243
|
+
if (def.matchers) {
|
|
10244
|
+
for (const entry of def.matchers) {
|
|
10245
|
+
arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`);
|
|
10246
|
+
}
|
|
10247
|
+
}
|
|
9922
10248
|
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
|
|
9923
10249
|
return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
|
|
9924
10250
|
}
|
|
@@ -9938,6 +10264,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9938
10264
|
return `not(${kid(def.parser)})`;
|
|
9939
10265
|
case "peek":
|
|
9940
10266
|
return `peek(${kid(def.parser)})`;
|
|
10267
|
+
case "routed":
|
|
10268
|
+
return "routed()";
|
|
9941
10269
|
case "trivia":
|
|
9942
10270
|
return `trivia(${kid(def.parser)})`;
|
|
9943
10271
|
case "token":
|
|
@@ -10959,10 +11287,20 @@ function run(entry, input, options = {}) {
|
|
|
10959
11287
|
}
|
|
10960
11288
|
|
|
10961
11289
|
// src/coverage.ts
|
|
11290
|
+
function dispatchMatcher(entry) {
|
|
11291
|
+
switch (entry.kind) {
|
|
11292
|
+
case "startsWith":
|
|
11293
|
+
return startsWith(entry.value);
|
|
11294
|
+
case "endsWith":
|
|
11295
|
+
return endsWith(entry.value);
|
|
11296
|
+
case "matches":
|
|
11297
|
+
return matches(new RegExp(entry.value, entry.flags));
|
|
11298
|
+
}
|
|
11299
|
+
}
|
|
10962
11300
|
var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
|
|
10963
11301
|
function compiledGrammarCoverageDefinitions(grammar) {
|
|
10964
11302
|
const definitions = grammar[GRAMMAR_COVERAGE_DEFINITIONS];
|
|
10965
|
-
if (!Array.isArray(definitions) || !definitions.every((definition) => typeof definition === "object" && definition !== null && typeof definition.id === "string" && (definition.kind === "rule" || definition.kind === "choice-arm" || definition.kind === "label"))) {
|
|
11303
|
+
if (!Array.isArray(definitions) || !definitions.every((definition) => typeof definition === "object" && definition !== null && typeof definition.id === "string" && (definition.kind === "rule" || definition.kind === "choice-arm" || definition.kind === "dispatch-arm" || definition.kind === "label"))) {
|
|
10966
11304
|
throw new TypeError("grammar has no coverage definitions; enable grammarCoverage for this macro build");
|
|
10967
11305
|
}
|
|
10968
11306
|
return definitions;
|
|
@@ -11142,10 +11480,54 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
11142
11480
|
return unsafeSequence(...def.parsers.map(build));
|
|
11143
11481
|
}
|
|
11144
11482
|
case "dispatch": {
|
|
11145
|
-
const
|
|
11146
|
-
|
|
11483
|
+
const ids = maps.dispatches.get(parser2) ?? [];
|
|
11484
|
+
let armIndex = 0;
|
|
11485
|
+
const dispatchStarts = [];
|
|
11486
|
+
const wrapArm = (arm, id) => {
|
|
11487
|
+
const child = build(arm);
|
|
11488
|
+
return {
|
|
11489
|
+
_tag: child._tag,
|
|
11490
|
+
_meta: child._meta,
|
|
11491
|
+
_def: child._def,
|
|
11492
|
+
parse(input, pos, ctx) {
|
|
11493
|
+
const dispatchStart = dispatchStarts[dispatchStarts.length - 1] ?? pos;
|
|
11494
|
+
if (id) trace?.write({ id, phase: "attempt", offset: dispatchStart });
|
|
11495
|
+
const result = child.parse(input, pos, ctx);
|
|
11496
|
+
if (result.ok) {
|
|
11497
|
+
if (id) {
|
|
11498
|
+
collector.hit(id);
|
|
11499
|
+
trace?.write({ id, phase: "selected", offset: dispatchStart, end: result.span.end });
|
|
11500
|
+
trace?.write({ id, phase: "success", offset: dispatchStart, end: result.span.end });
|
|
11501
|
+
}
|
|
11502
|
+
} else if (id) {
|
|
11503
|
+
trace?.write({ id, phase: "failure", offset: result.span.end });
|
|
11504
|
+
}
|
|
11505
|
+
return result;
|
|
11506
|
+
}
|
|
11507
|
+
};
|
|
11508
|
+
};
|
|
11509
|
+
const arms = def.cases.map(
|
|
11510
|
+
(entry2) => when(entry2.keys, wrapArm(entry2.parser, ids[armIndex++]), entry2.caseInsensitive ? { caseInsensitive: true } : void 0)
|
|
11511
|
+
);
|
|
11512
|
+
if (def.matchers) {
|
|
11513
|
+
for (const entry2 of def.matchers) {
|
|
11514
|
+
arms.push(when(dispatchMatcher(entry2), wrapArm(entry2.parser, ids[armIndex++]), entry2.caseInsensitive ? { caseInsensitive: true } : void 0));
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
if (def.otherwise) arms.push(otherwise(wrapArm(def.otherwise, ids[armIndex++])));
|
|
11147
11518
|
const unsafeDispatch = dispatch;
|
|
11148
|
-
|
|
11519
|
+
const base2 = unsafeDispatch(build(def.selector), ...arms);
|
|
11520
|
+
return {
|
|
11521
|
+
...base2,
|
|
11522
|
+
parse(input, pos, ctx) {
|
|
11523
|
+
dispatchStarts.push(pos);
|
|
11524
|
+
try {
|
|
11525
|
+
return base2.parse(input, pos, ctx);
|
|
11526
|
+
} finally {
|
|
11527
|
+
dispatchStarts.pop();
|
|
11528
|
+
}
|
|
11529
|
+
}
|
|
11530
|
+
};
|
|
11149
11531
|
}
|
|
11150
11532
|
case "many":
|
|
11151
11533
|
return many(build(def.parser));
|
|
@@ -11593,6 +11975,7 @@ export {
|
|
|
11593
11975
|
cstBuildHost,
|
|
11594
11976
|
dispatch,
|
|
11595
11977
|
duplicationFindingCount,
|
|
11978
|
+
endsWith,
|
|
11596
11979
|
expect,
|
|
11597
11980
|
extractCharClasses,
|
|
11598
11981
|
field,
|
|
@@ -11615,8 +11998,10 @@ export {
|
|
|
11615
11998
|
lineBreaksIn,
|
|
11616
11999
|
lineStartWithin,
|
|
11617
12000
|
literal,
|
|
12001
|
+
makeWhen,
|
|
11618
12002
|
makeWord,
|
|
11619
12003
|
many,
|
|
12004
|
+
matches,
|
|
11620
12005
|
noTrivia,
|
|
11621
12006
|
node,
|
|
11622
12007
|
not,
|
|
@@ -11633,6 +12018,7 @@ export {
|
|
|
11633
12018
|
regex,
|
|
11634
12019
|
relativize,
|
|
11635
12020
|
relativizeCST,
|
|
12021
|
+
routed,
|
|
11636
12022
|
rules,
|
|
11637
12023
|
run,
|
|
11638
12024
|
runWithGrammarCoverage,
|
|
@@ -11642,6 +12028,7 @@ export {
|
|
|
11642
12028
|
shiftAbsolute,
|
|
11643
12029
|
siteToString,
|
|
11644
12030
|
skip,
|
|
12031
|
+
startsWith,
|
|
11645
12032
|
token,
|
|
11646
12033
|
transform,
|
|
11647
12034
|
trivia,
|