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.cjs
CHANGED
|
@@ -57,6 +57,7 @@ __export(index_exports, {
|
|
|
57
57
|
cstBuildHost: () => cstBuildHost,
|
|
58
58
|
dispatch: () => dispatch,
|
|
59
59
|
duplicationFindingCount: () => duplicationFindingCount,
|
|
60
|
+
endsWith: () => endsWith,
|
|
60
61
|
expect: () => expect,
|
|
61
62
|
extractCharClasses: () => extractCharClasses,
|
|
62
63
|
field: () => field,
|
|
@@ -79,8 +80,10 @@ __export(index_exports, {
|
|
|
79
80
|
lineBreaksIn: () => lineBreaksIn,
|
|
80
81
|
lineStartWithin: () => lineStartWithin,
|
|
81
82
|
literal: () => literal,
|
|
83
|
+
makeWhen: () => makeWhen,
|
|
82
84
|
makeWord: () => makeWord,
|
|
83
85
|
many: () => many,
|
|
86
|
+
matches: () => matches,
|
|
84
87
|
noTrivia: () => noTrivia,
|
|
85
88
|
node: () => node,
|
|
86
89
|
not: () => not,
|
|
@@ -97,6 +100,7 @@ __export(index_exports, {
|
|
|
97
100
|
regex: () => regex,
|
|
98
101
|
relativize: () => relativize,
|
|
99
102
|
relativizeCST: () => relativizeCST,
|
|
103
|
+
routed: () => routed,
|
|
100
104
|
rules: () => rules,
|
|
101
105
|
run: () => run,
|
|
102
106
|
runWithGrammarCoverage: () => runWithGrammarCoverage,
|
|
@@ -106,6 +110,7 @@ __export(index_exports, {
|
|
|
106
110
|
shiftAbsolute: () => shiftAbsolute,
|
|
107
111
|
siteToString: () => siteToString,
|
|
108
112
|
skip: () => skip,
|
|
113
|
+
startsWith: () => startsWith,
|
|
109
114
|
token: () => token,
|
|
110
115
|
transform: () => transform,
|
|
111
116
|
trivia: () => trivia,
|
|
@@ -171,6 +176,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
171
176
|
return d.value.length === 0;
|
|
172
177
|
case "keywords":
|
|
173
178
|
return false;
|
|
179
|
+
case "routed":
|
|
180
|
+
return false;
|
|
174
181
|
case "regex":
|
|
175
182
|
try {
|
|
176
183
|
const m = new RegExp(d.source).exec("");
|
|
@@ -1232,6 +1239,8 @@ function derive(c, seen) {
|
|
|
1232
1239
|
case "not":
|
|
1233
1240
|
case "peek":
|
|
1234
1241
|
return deriveExpected2(def.parser);
|
|
1242
|
+
case "routed":
|
|
1243
|
+
return ["routed()"];
|
|
1235
1244
|
case "lazy": {
|
|
1236
1245
|
const name = c._ruleName;
|
|
1237
1246
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -2056,42 +2065,198 @@ function sequence(...parsers) {
|
|
|
2056
2065
|
}
|
|
2057
2066
|
|
|
2058
2067
|
// src/combinators/dispatch.ts
|
|
2059
|
-
function
|
|
2068
|
+
function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
2069
|
+
if (seen.has(parser2)) return false;
|
|
2070
|
+
seen.add(parser2);
|
|
2071
|
+
const def = parser2._def;
|
|
2072
|
+
switch (def.tag) {
|
|
2073
|
+
case "routed":
|
|
2074
|
+
return true;
|
|
2075
|
+
case "sequence":
|
|
2076
|
+
case "choice":
|
|
2077
|
+
return def.parsers.some((entry) => parserUsesRouted(entry, seen));
|
|
2078
|
+
case "dispatch":
|
|
2079
|
+
return false;
|
|
2080
|
+
case "sepBy":
|
|
2081
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.separator, seen);
|
|
2082
|
+
case "skip":
|
|
2083
|
+
return parserUsesRouted(def.main, seen) || parserUsesRouted(def.skipped, seen);
|
|
2084
|
+
case "grammar":
|
|
2085
|
+
return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
|
|
2086
|
+
case "scanTo":
|
|
2087
|
+
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
|
|
2088
|
+
case "recover":
|
|
2089
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
2090
|
+
case "lazy":
|
|
2091
|
+
try {
|
|
2092
|
+
return parserUsesRouted(def.thunk(), seen);
|
|
2093
|
+
} catch {
|
|
2094
|
+
return false;
|
|
2095
|
+
}
|
|
2096
|
+
case "many":
|
|
2097
|
+
case "oneOrMore":
|
|
2098
|
+
case "optional":
|
|
2099
|
+
case "attempt":
|
|
2100
|
+
case "transform":
|
|
2101
|
+
case "trivia":
|
|
2102
|
+
case "token":
|
|
2103
|
+
case "leaf":
|
|
2104
|
+
case "label":
|
|
2105
|
+
case "field":
|
|
2106
|
+
case "not":
|
|
2107
|
+
case "peek":
|
|
2108
|
+
case "node":
|
|
2109
|
+
case "withCtx":
|
|
2110
|
+
case "expect":
|
|
2111
|
+
return parserUsesRouted(def.parser, seen);
|
|
2112
|
+
default:
|
|
2113
|
+
return false;
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
function branchUsesRouted(branch) {
|
|
2117
|
+
return branch.usesRouted === true || parserUsesRouted(branch.parser);
|
|
2118
|
+
}
|
|
2119
|
+
function asciiFoldKey(key) {
|
|
2120
|
+
let out = "";
|
|
2121
|
+
for (let i = 0; i < key.length; i++) {
|
|
2122
|
+
const c = key.charCodeAt(i);
|
|
2123
|
+
out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
2124
|
+
}
|
|
2125
|
+
return out;
|
|
2126
|
+
}
|
|
2127
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2128
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2129
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2130
|
+
switch (matcher.kind) {
|
|
2131
|
+
case "startsWith":
|
|
2132
|
+
return candidate.startsWith(expected);
|
|
2133
|
+
case "endsWith":
|
|
2134
|
+
return candidate.endsWith(expected);
|
|
2135
|
+
case "matches": {
|
|
2136
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2137
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
function isStringMatcher(value) {
|
|
2142
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2143
|
+
const kind = value.kind;
|
|
2144
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2145
|
+
}
|
|
2146
|
+
function fixedMatcher(kind, value) {
|
|
2147
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2148
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2149
|
+
return { kind, value };
|
|
2150
|
+
}
|
|
2151
|
+
function startsWith(prefix) {
|
|
2152
|
+
return fixedMatcher("startsWith", prefix);
|
|
2153
|
+
}
|
|
2154
|
+
function endsWith(suffix) {
|
|
2155
|
+
return fixedMatcher("endsWith", suffix);
|
|
2156
|
+
}
|
|
2157
|
+
function matches(pattern) {
|
|
2158
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2159
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2160
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2161
|
+
}
|
|
2162
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2163
|
+
}
|
|
2164
|
+
function when(key, parser2, opts = {}) {
|
|
2165
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2166
|
+
if (isStringMatcher(key)) {
|
|
2167
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2168
|
+
}
|
|
2060
2169
|
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2061
2170
|
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2062
2171
|
for (const item of keys) {
|
|
2063
2172
|
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2064
2173
|
}
|
|
2065
|
-
return { kind: "when", keys, parser: parser2 };
|
|
2174
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2175
|
+
}
|
|
2176
|
+
function makeWhen(opts = {}) {
|
|
2177
|
+
return (key, parser2) => when(key, parser2, opts);
|
|
2066
2178
|
}
|
|
2067
2179
|
function otherwise(parser2) {
|
|
2068
|
-
return { kind: "otherwise", parser: parser2 };
|
|
2180
|
+
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2181
|
+
}
|
|
2182
|
+
function routed() {
|
|
2183
|
+
return {
|
|
2184
|
+
_tag: "routed",
|
|
2185
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
2186
|
+
_def: { tag: "routed" },
|
|
2187
|
+
parse(_input, pos, ctx) {
|
|
2188
|
+
const item = ctx._routed;
|
|
2189
|
+
if (item === void 0) {
|
|
2190
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2191
|
+
}
|
|
2192
|
+
if (pos !== item.span.start) {
|
|
2193
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2194
|
+
}
|
|
2195
|
+
if (cstCaptureActive(ctx)) {
|
|
2196
|
+
pushCstLeaf(ctx, { _tag: "leaf", value: item.value, span: item.span });
|
|
2197
|
+
}
|
|
2198
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2069
2201
|
}
|
|
2070
2202
|
function dispatch(selector, ...arms) {
|
|
2203
|
+
if (parserUsesRouted(selector)) {
|
|
2204
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2205
|
+
}
|
|
2071
2206
|
let fallback;
|
|
2207
|
+
let fallbackUsesRouted = false;
|
|
2208
|
+
const matchers = [];
|
|
2072
2209
|
const cases = [];
|
|
2073
|
-
const seen =
|
|
2210
|
+
const seen = [];
|
|
2074
2211
|
for (let i = 0; i < arms.length; i++) {
|
|
2075
2212
|
const arm = arms[i];
|
|
2076
2213
|
if (arm.kind === "otherwise") {
|
|
2077
2214
|
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2078
2215
|
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2079
2216
|
fallback = arm.parser;
|
|
2217
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
2080
2218
|
continue;
|
|
2081
2219
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2220
|
+
if (arm.kind === "whenMatcher") {
|
|
2221
|
+
const matcher = arm.matcher;
|
|
2222
|
+
matchers.push({
|
|
2223
|
+
kind: matcher.kind,
|
|
2224
|
+
value: matcher.value,
|
|
2225
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2226
|
+
parser: arm.parser,
|
|
2227
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2228
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2229
|
+
});
|
|
2230
|
+
continue;
|
|
2085
2231
|
}
|
|
2086
|
-
|
|
2232
|
+
for (const key of arm.keys) {
|
|
2233
|
+
const folded = asciiFoldKey(key);
|
|
2234
|
+
for (const prior of seen) {
|
|
2235
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2236
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2237
|
+
}
|
|
2238
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2239
|
+
}
|
|
2240
|
+
cases.push({
|
|
2241
|
+
keys: arm.keys,
|
|
2242
|
+
parser: arm.parser,
|
|
2243
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2244
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2245
|
+
});
|
|
2087
2246
|
}
|
|
2088
2247
|
const meta = {
|
|
2089
2248
|
firstSet: selector._meta.firstSet,
|
|
2090
|
-
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2249
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2091
2250
|
isTrivia: false
|
|
2092
2251
|
};
|
|
2093
2252
|
const byKey = /* @__PURE__ */ new Map();
|
|
2094
|
-
|
|
2253
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2254
|
+
for (const entry of cases) {
|
|
2255
|
+
for (const key of entry.keys) {
|
|
2256
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2257
|
+
else byKey.set(key, entry);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2095
2260
|
return {
|
|
2096
2261
|
_tag: "dispatch",
|
|
2097
2262
|
_meta: meta,
|
|
@@ -2099,18 +2264,35 @@ function dispatch(selector, ...arms) {
|
|
|
2099
2264
|
tag: "dispatch",
|
|
2100
2265
|
selector,
|
|
2101
2266
|
cases,
|
|
2102
|
-
...
|
|
2267
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2268
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
2103
2269
|
},
|
|
2104
2270
|
parse(input, pos, ctx) {
|
|
2271
|
+
if (parserUsesRouted(selector)) {
|
|
2272
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2273
|
+
}
|
|
2274
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
2105
2275
|
const selected = selector.parse(input, pos, ctx);
|
|
2106
2276
|
if (!selected.ok) return selected;
|
|
2107
|
-
const
|
|
2108
|
-
if (
|
|
2277
|
+
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 });
|
|
2278
|
+
if (branch === void 0) {
|
|
2109
2279
|
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2110
2280
|
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2111
2281
|
}
|
|
2112
|
-
const
|
|
2113
|
-
const
|
|
2282
|
+
const savedRouted = ctx._routed;
|
|
2283
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2284
|
+
let mark = saveTriviaMark(ctx);
|
|
2285
|
+
if (usesRouted) {
|
|
2286
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2287
|
+
mark = saveTriviaMark(ctx);
|
|
2288
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2289
|
+
}
|
|
2290
|
+
let result;
|
|
2291
|
+
try {
|
|
2292
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2293
|
+
} finally {
|
|
2294
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2295
|
+
}
|
|
2114
2296
|
if (!result.ok) {
|
|
2115
2297
|
rollbackTrivia(ctx, mark);
|
|
2116
2298
|
return { ...result, committed: true };
|
|
@@ -2497,6 +2679,7 @@ function markUnusedValues(root) {
|
|
|
2497
2679
|
case "dispatch":
|
|
2498
2680
|
visit(def.selector, consumed);
|
|
2499
2681
|
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
2682
|
+
if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
|
|
2500
2683
|
if (def.otherwise) visit(def.otherwise, consumed);
|
|
2501
2684
|
return;
|
|
2502
2685
|
case "node":
|
|
@@ -2775,7 +2958,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2775
2958
|
case "choice":
|
|
2776
2959
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2777
2960
|
case "dispatch":
|
|
2778
|
-
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2961
|
+
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);
|
|
2779
2962
|
case "sepBy":
|
|
2780
2963
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2781
2964
|
case "skip":
|
|
@@ -2844,7 +3027,7 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2844
3027
|
case "choice":
|
|
2845
3028
|
return d.parsers.some((x) => parserHasTriviaSite(x, seen));
|
|
2846
3029
|
case "dispatch":
|
|
2847
|
-
return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
|
|
3030
|
+
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);
|
|
2848
3031
|
case "grammar":
|
|
2849
3032
|
return parserHasTriviaSite(d.parser, seen);
|
|
2850
3033
|
case "lazy": {
|
|
@@ -2871,7 +3054,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2871
3054
|
case "choice":
|
|
2872
3055
|
return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
|
|
2873
3056
|
case "dispatch":
|
|
2874
|
-
return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
|
|
3057
|
+
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);
|
|
2875
3058
|
case "sepBy":
|
|
2876
3059
|
return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
|
|
2877
3060
|
case "skip":
|
|
@@ -3273,7 +3456,7 @@ function leaf(root, fn) {
|
|
|
3273
3456
|
}
|
|
3274
3457
|
|
|
3275
3458
|
// src/version.ts
|
|
3276
|
-
var PARSEMAN_VERSION = "0.39.
|
|
3459
|
+
var PARSEMAN_VERSION = "0.39.1";
|
|
3277
3460
|
|
|
3278
3461
|
// src/cst/host-mode.ts
|
|
3279
3462
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3533,6 +3716,7 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3533
3716
|
if (d.tag === "dispatch") {
|
|
3534
3717
|
kids.push(d.selector);
|
|
3535
3718
|
for (const c of d.cases) kids.push(c.parser);
|
|
3719
|
+
if (d.matchers) for (const c of d.matchers) kids.push(c.parser);
|
|
3536
3720
|
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3537
3721
|
}
|
|
3538
3722
|
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
@@ -5056,13 +5240,25 @@ function escapeClassChar(ch2) {
|
|
|
5056
5240
|
}
|
|
5057
5241
|
|
|
5058
5242
|
// src/compiler/grammar-coverage-ids.ts
|
|
5243
|
+
function idPart(value) {
|
|
5244
|
+
return encodeURIComponent(value);
|
|
5245
|
+
}
|
|
5246
|
+
function dispatchArmIds(def, path) {
|
|
5247
|
+
const ids = [];
|
|
5248
|
+
for (const entry of def.cases) ids.push(`dispatch:${path}/when:${entry.keys.map(idPart).join("|")}`);
|
|
5249
|
+
for (const entry of def.matchers ?? []) {
|
|
5250
|
+
ids.push(`dispatch:${path}/matcher:${entry.kind}:${idPart(entry.value)}${entry.kind === "matches" && entry.flags ? `:${idPart(entry.flags)}` : ""}`);
|
|
5251
|
+
}
|
|
5252
|
+
if (def.otherwise) ids.push(`dispatch:${path}/otherwise`);
|
|
5253
|
+
return ids;
|
|
5254
|
+
}
|
|
5059
5255
|
function children(def, winners) {
|
|
5060
5256
|
switch (def.tag) {
|
|
5061
5257
|
case "sequence":
|
|
5062
5258
|
case "choice":
|
|
5063
5259
|
return def.parsers;
|
|
5064
5260
|
case "dispatch":
|
|
5065
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
5261
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
5066
5262
|
case "many":
|
|
5067
5263
|
case "oneOrMore":
|
|
5068
5264
|
case "optional":
|
|
@@ -5105,6 +5301,7 @@ function children(def, winners) {
|
|
|
5105
5301
|
function buildGrammarPlan(entry, winners) {
|
|
5106
5302
|
const definitions = /* @__PURE__ */ new Map();
|
|
5107
5303
|
const choices = /* @__PURE__ */ new WeakMap();
|
|
5304
|
+
const dispatches = /* @__PURE__ */ new WeakMap();
|
|
5108
5305
|
const attempts = /* @__PURE__ */ new WeakMap();
|
|
5109
5306
|
const labels = /* @__PURE__ */ new WeakMap();
|
|
5110
5307
|
const rules2 = /* @__PURE__ */ new WeakMap();
|
|
@@ -5135,6 +5332,11 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5135
5332
|
choices.set(parser2, ids);
|
|
5136
5333
|
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
5137
5334
|
}
|
|
5335
|
+
if (parser2._def.tag === "dispatch") {
|
|
5336
|
+
const ids = dispatchArmIds(parser2._def, path);
|
|
5337
|
+
dispatches.set(parser2, ids);
|
|
5338
|
+
ids.forEach((id) => definitions.set(id, { id, kind: "dispatch-arm" }));
|
|
5339
|
+
}
|
|
5138
5340
|
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path}`);
|
|
5139
5341
|
if (parser2._def.tag === "label") {
|
|
5140
5342
|
const id = `label:${path}`;
|
|
@@ -5147,7 +5349,7 @@ function buildGrammarPlan(entry, winners) {
|
|
|
5147
5349
|
for (const root of roots) {
|
|
5148
5350
|
visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
|
|
5149
5351
|
}
|
|
5150
|
-
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
|
|
5352
|
+
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
|
|
5151
5353
|
}
|
|
5152
5354
|
|
|
5153
5355
|
// src/compiler/scannable-run.ts
|
|
@@ -6521,6 +6723,7 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6521
6723
|
case "literal":
|
|
6522
6724
|
case "regex":
|
|
6523
6725
|
case "keywords":
|
|
6726
|
+
case "routed":
|
|
6524
6727
|
return { alts: [[seg(p._meta.firstSet, matchesEmpty(p))]] };
|
|
6525
6728
|
case "choice": {
|
|
6526
6729
|
const alts = [];
|
|
@@ -6871,7 +7074,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6871
7074
|
case "choice":
|
|
6872
7075
|
return false;
|
|
6873
7076
|
case "dispatch":
|
|
6874
|
-
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);
|
|
7077
|
+
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);
|
|
6875
7078
|
case "attempt":
|
|
6876
7079
|
return false;
|
|
6877
7080
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
@@ -6970,6 +7173,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6970
7173
|
case "keywords":
|
|
6971
7174
|
case "node":
|
|
6972
7175
|
case "token":
|
|
7176
|
+
case "routed":
|
|
6973
7177
|
case "leaf":
|
|
6974
7178
|
return true;
|
|
6975
7179
|
case "not":
|
|
@@ -6982,7 +7186,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6982
7186
|
case "choice":
|
|
6983
7187
|
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
6984
7188
|
case "dispatch":
|
|
6985
|
-
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
7189
|
+
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);
|
|
6986
7190
|
case "sepBy":
|
|
6987
7191
|
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6988
7192
|
case "many":
|
|
@@ -7460,6 +7664,7 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7460
7664
|
for (const item of d.cases) {
|
|
7461
7665
|
if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
|
|
7462
7666
|
}
|
|
7667
|
+
if (d.matchers?.some((item) => mayRecordRecoveryError(item.parser, recovery, seen))) return true;
|
|
7463
7668
|
return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
|
|
7464
7669
|
}
|
|
7465
7670
|
case "attempt":
|
|
@@ -7494,53 +7699,120 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
|
7494
7699
|
return false;
|
|
7495
7700
|
}
|
|
7496
7701
|
}
|
|
7497
|
-
function emitDispatchCombinator(def, ctx, pos) {
|
|
7702
|
+
function emitDispatchCombinator(parser2, def, ctx, pos) {
|
|
7703
|
+
if (parserUsesRouted2(def.selector)) {
|
|
7704
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
7705
|
+
}
|
|
7706
|
+
const coverageIds = ctx.coverage?.plan.dispatches.get(parser2) ?? [];
|
|
7707
|
+
const branchUsesRouted2 = (entry) => entry.usesRouted === true || parserUsesRouted2(entry.parser);
|
|
7708
|
+
const hasRoutedBranch = def.cases.some(branchUsesRouted2) || (def.matchers ? def.matchers.some(branchUsesRouted2) : false) || (def.otherwise ? def.otherwiseUsesRouted === true || parserUsesRouted2(def.otherwise) : false);
|
|
7709
|
+
const selectorLeafMark = hasRoutedBranch ? v(ctx, "_dsl") : null;
|
|
7710
|
+
const selectorRawMark = hasRoutedBranch ? v(ctx, "_dsr") : null;
|
|
7711
|
+
const selectorTriviaMark = hasRoutedBranch ? v(ctx, "_dst") : null;
|
|
7712
|
+
const selectorLogMark = hasRoutedBranch ? v(ctx, "_dsg") : null;
|
|
7713
|
+
const selectorFieldMark = hasRoutedBranch ? v(ctx, "_dsf") : null;
|
|
7714
|
+
const selectorErrorMark = hasRoutedBranch ? v(ctx, "_dse") : null;
|
|
7498
7715
|
const selector = emit(def.selector, ctx, pos);
|
|
7499
7716
|
const outV = v(ctx, "_dval");
|
|
7500
7717
|
const outE = v(ctx, "_dend");
|
|
7501
7718
|
const keyV = v(ctx, "_dkey");
|
|
7502
7719
|
const stmts = [
|
|
7720
|
+
...selectorLeafMark ? [
|
|
7721
|
+
`${ind(ctx)}const ${selectorLeafMark} = _ctx._cstLeaves?.length ?? 0`,
|
|
7722
|
+
`${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
|
|
7723
|
+
`${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
7724
|
+
`${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
|
|
7725
|
+
`${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
|
|
7726
|
+
`${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
|
|
7727
|
+
] : [],
|
|
7503
7728
|
...selector.stmts,
|
|
7504
7729
|
`${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
|
|
7505
7730
|
`${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
|
|
7506
7731
|
];
|
|
7507
7732
|
let wroteBranch = false;
|
|
7508
|
-
const emitSelectedTail = (
|
|
7733
|
+
const emitSelectedTail = (entry, keyword, condition, coverageId) => {
|
|
7734
|
+
const parser3 = entry.parser;
|
|
7509
7735
|
const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
|
|
7510
7736
|
stmts.push(head);
|
|
7511
7737
|
ctx.indent++;
|
|
7512
|
-
const
|
|
7738
|
+
const usesRouted = branchUsesRouted2(entry);
|
|
7739
|
+
const routedV = usesRouted ? v(ctx, "_drt") : null;
|
|
7740
|
+
const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
|
|
7513
7741
|
const errMark = mayError ? v(ctx, "_derr") : null;
|
|
7514
7742
|
const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
|
|
7743
|
+
if (routedV) {
|
|
7744
|
+
stmts.push(
|
|
7745
|
+
`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`,
|
|
7746
|
+
...selectorLeafMark ? [
|
|
7747
|
+
`${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}`
|
|
7748
|
+
] : []
|
|
7749
|
+
);
|
|
7750
|
+
}
|
|
7515
7751
|
if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
|
|
7516
7752
|
if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
|
|
7517
|
-
const tail = emitTailFallible(
|
|
7753
|
+
const tail = emitTailFallible(parser3, ctx, usesRouted ? pos : selector.endVar);
|
|
7754
|
+
stmts.push(...coverageAttempt(ctx, coverageId, pos));
|
|
7518
7755
|
const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
|
|
7519
7756
|
const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
|
|
7757
|
+
const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
|
|
7758
|
+
const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
|
|
7520
7759
|
stmts.push(
|
|
7521
7760
|
...tail.stmts,
|
|
7522
|
-
`${ind(ctx)}if (!${tail.okVar}) { ${errorRollback}${logRollback}${committedFailBody(ctx)} }`,
|
|
7761
|
+
`${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
|
|
7762
|
+
...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
|
|
7523
7763
|
`${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
|
|
7524
|
-
`${ind(ctx)}${outE} = ${tail.endVar}
|
|
7764
|
+
`${ind(ctx)}${outE} = ${tail.endVar}`,
|
|
7765
|
+
...coverageHit(ctx, coverageId, pos, tail.endVar)
|
|
7525
7766
|
);
|
|
7526
7767
|
ctx.indent--;
|
|
7527
7768
|
stmts.push(`${ind(ctx)}}`);
|
|
7528
7769
|
};
|
|
7770
|
+
let coverageIndex = 0;
|
|
7529
7771
|
for (const entry of def.cases) {
|
|
7530
|
-
const condition = entry.keys.map((key) =>
|
|
7531
|
-
emitSelectedTail(entry
|
|
7772
|
+
const condition = entry.keys.map((key) => emitDispatchKeyCondition(keyV, key, entry.caseInsensitive)).join(" || ");
|
|
7773
|
+
emitSelectedTail(entry, wroteBranch ? "else if" : "if", condition, coverageIds[coverageIndex++]);
|
|
7532
7774
|
wroteBranch = true;
|
|
7533
7775
|
}
|
|
7776
|
+
const emitMatcherTails = () => {
|
|
7777
|
+
for (const matcher of def.matchers ?? []) {
|
|
7778
|
+
emitSelectedTail(matcher, wroteBranch ? "else if" : "if", emitDispatchMatcherCondition(keyV, matcher), coverageIds[coverageIndex++]);
|
|
7779
|
+
wroteBranch = true;
|
|
7780
|
+
}
|
|
7781
|
+
};
|
|
7534
7782
|
if (def.otherwise) {
|
|
7535
|
-
|
|
7536
|
-
|
|
7783
|
+
emitMatcherTails();
|
|
7784
|
+
const otherwiseEntry = { parser: def.otherwise, usesRouted: def.otherwiseUsesRouted };
|
|
7785
|
+
if (wroteBranch) emitSelectedTail(otherwiseEntry, "else", void 0, coverageIds[coverageIndex++]);
|
|
7786
|
+
else emitSelectedTail(otherwiseEntry, "if", "true", coverageIds[coverageIndex++]);
|
|
7537
7787
|
} else {
|
|
7788
|
+
emitMatcherTails();
|
|
7538
7789
|
const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
|
|
7539
7790
|
const fail = failArrBody(ctx, expected, selector.endVar);
|
|
7540
7791
|
stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
|
|
7541
7792
|
}
|
|
7542
7793
|
return { stmts, valueVar: outV, endVar: outE };
|
|
7543
7794
|
}
|
|
7795
|
+
function emitDispatchKeyCondition(valueVar, key, caseInsensitive) {
|
|
7796
|
+
if (!caseInsensitive) return `${valueVar} === ${JSON.stringify(key)}`;
|
|
7797
|
+
const parts = [`${valueVar}.length === ${key.length}`];
|
|
7798
|
+
for (let i = 0; i < key.length; i++) {
|
|
7799
|
+
parts.push(foldEq(`${valueVar}.charCodeAt(${i})`, key.charCodeAt(i)));
|
|
7800
|
+
}
|
|
7801
|
+
return parts.join(" && ");
|
|
7802
|
+
}
|
|
7803
|
+
function emitDispatchMatcherCondition(valueVar, matcher) {
|
|
7804
|
+
if (matcher.kind === "matches") {
|
|
7805
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
7806
|
+
return `${new RegExp(matcher.value, flags).toString()}.test(${valueVar})`;
|
|
7807
|
+
}
|
|
7808
|
+
const text = matcher.value;
|
|
7809
|
+
const parts = [`${valueVar}.length >= ${text.length}`];
|
|
7810
|
+
const offset = matcher.kind === "endsWith" ? `${valueVar}.length - ${text.length}` : "0";
|
|
7811
|
+
for (let i = 0; i < text.length; i++) {
|
|
7812
|
+
parts.push(matcher.caseInsensitive ? foldEq(`${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`})`, text.charCodeAt(i)) : `${valueVar}.charCodeAt(${offset}${i === 0 ? "" : ` + ${i}`}) === ${text.charCodeAt(i)}`);
|
|
7813
|
+
}
|
|
7814
|
+
return parts.join(" && ");
|
|
7815
|
+
}
|
|
7544
7816
|
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
7545
7817
|
const superParser = def.parsers[superIndex];
|
|
7546
7818
|
const regexDef = getCoreRegexDef(superParser);
|
|
@@ -8195,6 +8467,18 @@ function emitPeek(def, ctx, pos) {
|
|
|
8195
8467
|
endVar: pos
|
|
8196
8468
|
};
|
|
8197
8469
|
}
|
|
8470
|
+
function emitRouted(ctx, pos) {
|
|
8471
|
+
const item = v(ctx, "_rt");
|
|
8472
|
+
return {
|
|
8473
|
+
stmts: [
|
|
8474
|
+
`${ind(ctx)}const ${item} = _ctx._routed`,
|
|
8475
|
+
...emitIfFail(ctx, `${item} === undefined || ${item}.span.start !== ${pos}`, failBody(ctx, '"routed()"', pos)),
|
|
8476
|
+
...emitLeafCapture(ctx, `${item}.value`, `${item}.span.start`, `${item}.span.end`)
|
|
8477
|
+
],
|
|
8478
|
+
valueVar: `${item}.value`,
|
|
8479
|
+
endVar: `${item}.span.end`
|
|
8480
|
+
};
|
|
8481
|
+
}
|
|
8198
8482
|
function escapeTokenLiteral(value) {
|
|
8199
8483
|
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
8200
8484
|
}
|
|
@@ -8660,7 +8944,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8660
8944
|
case "choice":
|
|
8661
8945
|
return emitChoice(p, def, ctx, pos);
|
|
8662
8946
|
case "dispatch":
|
|
8663
|
-
return emitDispatchCombinator(def, ctx, pos);
|
|
8947
|
+
return emitDispatchCombinator(p, def, ctx, pos);
|
|
8664
8948
|
case "attempt":
|
|
8665
8949
|
return emitAttempt(p, def, ctx, pos);
|
|
8666
8950
|
case "many":
|
|
@@ -8785,6 +9069,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8785
9069
|
return emitNot(def, ctx, pos);
|
|
8786
9070
|
case "peek":
|
|
8787
9071
|
return emitPeek(def, ctx, pos);
|
|
9072
|
+
case "routed":
|
|
9073
|
+
return emitRouted(ctx, pos);
|
|
8788
9074
|
case "node":
|
|
8789
9075
|
return emitNode(def, ctx, pos);
|
|
8790
9076
|
case "scanTo":
|
|
@@ -8882,7 +9168,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8882
9168
|
case "choice":
|
|
8883
9169
|
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
8884
9170
|
case "dispatch":
|
|
8885
|
-
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
9171
|
+
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);
|
|
8886
9172
|
case "sepBy":
|
|
8887
9173
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
8888
9174
|
case "scanTo":
|
|
@@ -8897,6 +9183,26 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8897
9183
|
return false;
|
|
8898
9184
|
}
|
|
8899
9185
|
}
|
|
9186
|
+
function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
|
|
9187
|
+
if (seen.has(p)) return false;
|
|
9188
|
+
seen.add(p);
|
|
9189
|
+
const d = p._def;
|
|
9190
|
+
switch (d.tag) {
|
|
9191
|
+
case "routed":
|
|
9192
|
+
return true;
|
|
9193
|
+
case "lazy": {
|
|
9194
|
+
try {
|
|
9195
|
+
return parserUsesRouted2(d.thunk(), seen);
|
|
9196
|
+
} catch {
|
|
9197
|
+
return false;
|
|
9198
|
+
}
|
|
9199
|
+
}
|
|
9200
|
+
case "dispatch":
|
|
9201
|
+
return false;
|
|
9202
|
+
default:
|
|
9203
|
+
return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
|
|
9204
|
+
}
|
|
9205
|
+
}
|
|
8900
9206
|
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
8901
9207
|
if (seen.has(p)) return false;
|
|
8902
9208
|
seen.add(p);
|
|
@@ -8917,7 +9223,7 @@ function childrenOf2(def) {
|
|
|
8917
9223
|
case "choice":
|
|
8918
9224
|
return def.parsers;
|
|
8919
9225
|
case "dispatch":
|
|
8920
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9226
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
8921
9227
|
case "many":
|
|
8922
9228
|
case "attempt":
|
|
8923
9229
|
case "optional":
|
|
@@ -8954,6 +9260,7 @@ function childrenOf2(def) {
|
|
|
8954
9260
|
case "regex":
|
|
8955
9261
|
case "keywords":
|
|
8956
9262
|
case "guard":
|
|
9263
|
+
case "routed":
|
|
8957
9264
|
case "unknown":
|
|
8958
9265
|
return [];
|
|
8959
9266
|
}
|
|
@@ -9661,7 +9968,7 @@ function childrenOf3(def) {
|
|
|
9661
9968
|
case "choice":
|
|
9662
9969
|
return def.parsers;
|
|
9663
9970
|
case "dispatch":
|
|
9664
|
-
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9971
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.matchers ? def.matchers.map((entry) => entry.parser) : [], ...def.otherwise ? [def.otherwise] : []];
|
|
9665
9972
|
case "many":
|
|
9666
9973
|
case "oneOrMore":
|
|
9667
9974
|
case "optional":
|
|
@@ -9690,6 +9997,7 @@ function childrenOf3(def) {
|
|
|
9690
9997
|
case "literal":
|
|
9691
9998
|
case "regex":
|
|
9692
9999
|
case "keywords":
|
|
10000
|
+
case "routed":
|
|
9693
10001
|
case "guard":
|
|
9694
10002
|
case "recover":
|
|
9695
10003
|
case "unknown":
|
|
@@ -9714,6 +10022,16 @@ function repeatOpts(min, max, trailing) {
|
|
|
9714
10022
|
if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
|
|
9715
10023
|
return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
|
|
9716
10024
|
}
|
|
10025
|
+
function matcherExpr(entry) {
|
|
10026
|
+
switch (entry.kind) {
|
|
10027
|
+
case "startsWith":
|
|
10028
|
+
return `startsWith(${JSON.stringify(entry.value)})`;
|
|
10029
|
+
case "endsWith":
|
|
10030
|
+
return `endsWith(${JSON.stringify(entry.value)})`;
|
|
10031
|
+
case "matches":
|
|
10032
|
+
return `matches(${new RegExp(entry.value, entry.flags ?? "").toString()})`;
|
|
10033
|
+
}
|
|
10034
|
+
}
|
|
9717
10035
|
function serializeRuleMap(ruleMap, scanSkip) {
|
|
9718
10036
|
try {
|
|
9719
10037
|
return new Serializer(ruleMap, scanSkip ?? []).run();
|
|
@@ -9813,7 +10131,11 @@ function evalRuleMapIR(ir) {
|
|
|
9813
10131
|
"choice",
|
|
9814
10132
|
"dispatch",
|
|
9815
10133
|
"when",
|
|
10134
|
+
"startsWith",
|
|
10135
|
+
"endsWith",
|
|
10136
|
+
"matches",
|
|
9816
10137
|
"otherwise",
|
|
10138
|
+
"routed",
|
|
9817
10139
|
"attempt",
|
|
9818
10140
|
"many",
|
|
9819
10141
|
"oneOrMore",
|
|
@@ -9849,7 +10171,11 @@ function evalRuleMapIR(ir) {
|
|
|
9849
10171
|
choice,
|
|
9850
10172
|
dispatch,
|
|
9851
10173
|
when,
|
|
10174
|
+
startsWith,
|
|
10175
|
+
endsWith,
|
|
10176
|
+
matches,
|
|
9852
10177
|
otherwise,
|
|
10178
|
+
routed,
|
|
9853
10179
|
attempt,
|
|
9854
10180
|
many,
|
|
9855
10181
|
oneOrMore,
|
|
@@ -10037,8 +10363,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
10037
10363
|
}
|
|
10038
10364
|
case "dispatch": {
|
|
10039
10365
|
const arms = def.cases.map(
|
|
10040
|
-
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)})`
|
|
10366
|
+
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`
|
|
10041
10367
|
);
|
|
10368
|
+
if (def.matchers) {
|
|
10369
|
+
for (const entry of def.matchers) {
|
|
10370
|
+
arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`);
|
|
10371
|
+
}
|
|
10372
|
+
}
|
|
10042
10373
|
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
|
|
10043
10374
|
return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
|
|
10044
10375
|
}
|
|
@@ -10058,6 +10389,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
10058
10389
|
return `not(${kid(def.parser)})`;
|
|
10059
10390
|
case "peek":
|
|
10060
10391
|
return `peek(${kid(def.parser)})`;
|
|
10392
|
+
case "routed":
|
|
10393
|
+
return "routed()";
|
|
10061
10394
|
case "trivia":
|
|
10062
10395
|
return `trivia(${kid(def.parser)})`;
|
|
10063
10396
|
case "token":
|
|
@@ -11079,10 +11412,20 @@ function run(entry, input, options = {}) {
|
|
|
11079
11412
|
}
|
|
11080
11413
|
|
|
11081
11414
|
// src/coverage.ts
|
|
11415
|
+
function dispatchMatcher(entry) {
|
|
11416
|
+
switch (entry.kind) {
|
|
11417
|
+
case "startsWith":
|
|
11418
|
+
return startsWith(entry.value);
|
|
11419
|
+
case "endsWith":
|
|
11420
|
+
return endsWith(entry.value);
|
|
11421
|
+
case "matches":
|
|
11422
|
+
return matches(new RegExp(entry.value, entry.flags));
|
|
11423
|
+
}
|
|
11424
|
+
}
|
|
11082
11425
|
var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
|
|
11083
11426
|
function compiledGrammarCoverageDefinitions(grammar) {
|
|
11084
11427
|
const definitions = grammar[GRAMMAR_COVERAGE_DEFINITIONS];
|
|
11085
|
-
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"))) {
|
|
11428
|
+
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"))) {
|
|
11086
11429
|
throw new TypeError("grammar has no coverage definitions; enable grammarCoverage for this macro build");
|
|
11087
11430
|
}
|
|
11088
11431
|
return definitions;
|
|
@@ -11262,10 +11605,54 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
11262
11605
|
return unsafeSequence(...def.parsers.map(build));
|
|
11263
11606
|
}
|
|
11264
11607
|
case "dispatch": {
|
|
11265
|
-
const
|
|
11266
|
-
|
|
11608
|
+
const ids = maps.dispatches.get(parser2) ?? [];
|
|
11609
|
+
let armIndex = 0;
|
|
11610
|
+
const dispatchStarts = [];
|
|
11611
|
+
const wrapArm = (arm, id) => {
|
|
11612
|
+
const child = build(arm);
|
|
11613
|
+
return {
|
|
11614
|
+
_tag: child._tag,
|
|
11615
|
+
_meta: child._meta,
|
|
11616
|
+
_def: child._def,
|
|
11617
|
+
parse(input, pos, ctx) {
|
|
11618
|
+
const dispatchStart = dispatchStarts[dispatchStarts.length - 1] ?? pos;
|
|
11619
|
+
if (id) trace?.write({ id, phase: "attempt", offset: dispatchStart });
|
|
11620
|
+
const result = child.parse(input, pos, ctx);
|
|
11621
|
+
if (result.ok) {
|
|
11622
|
+
if (id) {
|
|
11623
|
+
collector.hit(id);
|
|
11624
|
+
trace?.write({ id, phase: "selected", offset: dispatchStart, end: result.span.end });
|
|
11625
|
+
trace?.write({ id, phase: "success", offset: dispatchStart, end: result.span.end });
|
|
11626
|
+
}
|
|
11627
|
+
} else if (id) {
|
|
11628
|
+
trace?.write({ id, phase: "failure", offset: result.span.end });
|
|
11629
|
+
}
|
|
11630
|
+
return result;
|
|
11631
|
+
}
|
|
11632
|
+
};
|
|
11633
|
+
};
|
|
11634
|
+
const arms = def.cases.map(
|
|
11635
|
+
(entry2) => when(entry2.keys, wrapArm(entry2.parser, ids[armIndex++]), entry2.caseInsensitive ? { caseInsensitive: true } : void 0)
|
|
11636
|
+
);
|
|
11637
|
+
if (def.matchers) {
|
|
11638
|
+
for (const entry2 of def.matchers) {
|
|
11639
|
+
arms.push(when(dispatchMatcher(entry2), wrapArm(entry2.parser, ids[armIndex++]), entry2.caseInsensitive ? { caseInsensitive: true } : void 0));
|
|
11640
|
+
}
|
|
11641
|
+
}
|
|
11642
|
+
if (def.otherwise) arms.push(otherwise(wrapArm(def.otherwise, ids[armIndex++])));
|
|
11267
11643
|
const unsafeDispatch = dispatch;
|
|
11268
|
-
|
|
11644
|
+
const base2 = unsafeDispatch(build(def.selector), ...arms);
|
|
11645
|
+
return {
|
|
11646
|
+
...base2,
|
|
11647
|
+
parse(input, pos, ctx) {
|
|
11648
|
+
dispatchStarts.push(pos);
|
|
11649
|
+
try {
|
|
11650
|
+
return base2.parse(input, pos, ctx);
|
|
11651
|
+
} finally {
|
|
11652
|
+
dispatchStarts.pop();
|
|
11653
|
+
}
|
|
11654
|
+
}
|
|
11655
|
+
};
|
|
11269
11656
|
}
|
|
11270
11657
|
case "many":
|
|
11271
11658
|
return many(build(def.parser));
|