parseman 0.7.1 → 0.8.0
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/combinators/expect.d.ts +36 -0
- package/dist/combinators/expect.d.ts.map +1 -0
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/index.cjs +249 -30
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +249 -30
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +248 -31
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.js +248 -31
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Combinator, ParseError } from '../types.ts';
|
|
2
|
+
export type { ParseError };
|
|
3
|
+
/**
|
|
4
|
+
* Statically derive the "expected" token set from a combinator's structure,
|
|
5
|
+
* so a failure can name what it wanted without running anything:
|
|
6
|
+
*
|
|
7
|
+
* literal('}') -> ['"}"']
|
|
8
|
+
* keywords(['a','b']) -> ['"a"', '"b"']
|
|
9
|
+
* choice(x, y) -> expected(x) ++ expected(y) (all alternatives)
|
|
10
|
+
* sequence(a, b, …) -> expected(a) (only the first term can fail first)
|
|
11
|
+
* label('s', x) -> ['s'] (an explicit name wins)
|
|
12
|
+
*
|
|
13
|
+
* This is what lets expect() report a meaningful, IDENTICAL expectation in both
|
|
14
|
+
* the interpreter and the compiled output (the compiled path does not rebuild the
|
|
15
|
+
* runtime `expected` array, so it reads this precomputed set instead).
|
|
16
|
+
*/
|
|
17
|
+
export declare function staticExpected(c: Combinator<unknown>): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Required-token combinator. Tries `combinator`; on success returns it verbatim.
|
|
20
|
+
* On failure it does NOT fail — it records a {@link ParseError} (the statically
|
|
21
|
+
* derived expected set, or `label` when given) into `ctx._errors` and RECOVERS IN
|
|
22
|
+
* PLACE, returning a zero-width success so the enclosing sequence continues as if
|
|
23
|
+
* the token were present.
|
|
24
|
+
*
|
|
25
|
+
* Use it to mark required delimiters/terminators (`}` `)` `;`, a selector before a
|
|
26
|
+
* block) so a missing one is reported with position + expectation rather than
|
|
27
|
+
* aborting the whole parse or being silently swallowed by a catch-all.
|
|
28
|
+
*
|
|
29
|
+
* sequence(literal('{'), declList, expect(literal('}'), '}'))
|
|
30
|
+
*
|
|
31
|
+
* Errors are only collected when the parse runs with `{ recover: true }`; without
|
|
32
|
+
* it, expect() still recovers in place but records nothing (zero overhead beyond
|
|
33
|
+
* the inner attempt).
|
|
34
|
+
*/
|
|
35
|
+
export declare function expect<T>(combinator: Combinator<T>, label?: string): Combinator<T | ParseError>;
|
|
36
|
+
//# sourceMappingURL=expect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/combinators/expect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAyC,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhG,YAAY,EAAE,UAAU,EAAE,CAAA;AAE1B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE,CAoB/D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CAmB/F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../src/combinators/grammar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../src/combinators/grammar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAa,MAAM,aAAa,CAAA;AAGnF,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG;IACzC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;IACnC,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACtD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IACpC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;CACrE;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAwCrF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAElE;AAED,wBAAgB,KAAK,CAAC,CAAC,EACrB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,YAAiB,GACtB,WAAW,CAAC,CAAC,CAAC,CAuBhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanTo.d.ts","sourceRoot":"","sources":["../../src/combinators/scanTo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"scanTo.d.ts","sourceRoot":"","sources":["../../src/combinators/scanTo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAA;AAYpF,MAAM,MAAM,aAAa,GAAG;IAC1B,uGAAuG;IACvG,IAAI,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAA;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,EAC7B,EAAE,IAAS,EAAE,KAAa,EAAE,GAAE,aAAkB,GAC/C,UAAU,CAAC,MAAM,CAAC,CAoEpB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,aAAkB,GAC1B,UAAU,CAAC,MAAM,CAAC,CA0CpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/compiler/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAuB,WAAW,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/compiler/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAuB,WAAW,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;AA2/CzH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAClD,kGAAkG;IAClG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAChF;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE,CAAA;IACvF,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAkCD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAqGhG"}
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
choice: () => choice,
|
|
38
38
|
compile: () => compile,
|
|
39
39
|
completionsAt: () => completionsAt,
|
|
40
|
+
expect: () => expect,
|
|
40
41
|
guard: () => guard,
|
|
41
42
|
isParseError: () => isParseError,
|
|
42
43
|
keywords: () => keywords,
|
|
@@ -61,6 +62,7 @@ __export(index_exports, {
|
|
|
61
62
|
sepBy: () => sepBy,
|
|
62
63
|
sequence: () => sequence,
|
|
63
64
|
skip: () => skip,
|
|
65
|
+
staticExpected: () => staticExpected,
|
|
64
66
|
transform: () => transform,
|
|
65
67
|
trivia: () => trivia,
|
|
66
68
|
triviaEntries: () => triviaEntries,
|
|
@@ -1349,13 +1351,15 @@ function noTrivia(root) {
|
|
|
1349
1351
|
function parse(combinator, input, opts = {}) {
|
|
1350
1352
|
const trackLines = opts.trackLines ?? false;
|
|
1351
1353
|
const _errors = opts.recover ? [] : void 0;
|
|
1354
|
+
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
1352
1355
|
const ctx = {
|
|
1353
1356
|
trackLines,
|
|
1354
|
-
..._errors !== void 0 ? { _errors } : {}
|
|
1357
|
+
..._errors !== void 0 ? { _errors } : {},
|
|
1358
|
+
..._probe !== void 0 ? { _probe } : {}
|
|
1355
1359
|
};
|
|
1356
1360
|
const result = combinator.parse(input, 0, ctx);
|
|
1357
1361
|
if (!result.ok) return result;
|
|
1358
|
-
const withErrors = _errors !== void 0 ? { ...result, errors: _errors } : result;
|
|
1362
|
+
const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
|
|
1359
1363
|
if (trackLines) {
|
|
1360
1364
|
const idx = buildLineIndex(input);
|
|
1361
1365
|
return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
|
|
@@ -1851,6 +1855,119 @@ function firstSetCond(codeVar, fs) {
|
|
|
1851
1855
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1852
1856
|
).join(" || ");
|
|
1853
1857
|
}
|
|
1858
|
+
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1859
|
+
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1860
|
+
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
1861
|
+
}
|
|
1862
|
+
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
1863
|
+
if (seen.has(p)) return false;
|
|
1864
|
+
seen.add(p);
|
|
1865
|
+
const d = p._def;
|
|
1866
|
+
switch (d.tag) {
|
|
1867
|
+
// Atomic / non-capturing-then-failing: a failure happens before any push.
|
|
1868
|
+
case "literal":
|
|
1869
|
+
case "regex":
|
|
1870
|
+
case "keywords":
|
|
1871
|
+
case "guard":
|
|
1872
|
+
case "not":
|
|
1873
|
+
case "trivia":
|
|
1874
|
+
case "scanTo":
|
|
1875
|
+
case "unknown":
|
|
1876
|
+
return false;
|
|
1877
|
+
// node() captures into its own private buffers and rolls them back on
|
|
1878
|
+
// failure (emitNode restores _ctx.* and never pushes on the !ok path).
|
|
1879
|
+
case "node":
|
|
1880
|
+
return false;
|
|
1881
|
+
// choice/firstMatch already roll back each failed arm; on overall failure
|
|
1882
|
+
// nothing committed remains.
|
|
1883
|
+
case "choice":
|
|
1884
|
+
return false;
|
|
1885
|
+
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
1886
|
+
case "optional":
|
|
1887
|
+
case "many":
|
|
1888
|
+
case "oneOrMore":
|
|
1889
|
+
return false;
|
|
1890
|
+
// sepBy emits its own per-iteration rollback in emitSepBy.
|
|
1891
|
+
case "sepBy":
|
|
1892
|
+
return false;
|
|
1893
|
+
// A sequence is the real case: an earlier capturing term followed by a term
|
|
1894
|
+
// that can fail leaves the earlier captures buffered.
|
|
1895
|
+
case "sequence": {
|
|
1896
|
+
const parts = d.parsers;
|
|
1897
|
+
if (parts.length < 2) return parts.some((x) => mayLeavePartialCapture(x, seen));
|
|
1898
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1899
|
+
if (hasNodeDef(parts[i]) || capturesLeaf(parts[i])) return true;
|
|
1900
|
+
}
|
|
1901
|
+
return false;
|
|
1902
|
+
}
|
|
1903
|
+
// Delegating wrappers: defer to the wrapped parser.
|
|
1904
|
+
case "transform":
|
|
1905
|
+
case "label":
|
|
1906
|
+
case "expect":
|
|
1907
|
+
case "withCtx":
|
|
1908
|
+
case "grammar":
|
|
1909
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1910
|
+
case "skip":
|
|
1911
|
+
return mayLeavePartialCapture(d.main, seen);
|
|
1912
|
+
case "recover":
|
|
1913
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1914
|
+
case "lazy": {
|
|
1915
|
+
try {
|
|
1916
|
+
return mayLeavePartialCapture(d.thunk(), seen);
|
|
1917
|
+
} catch {
|
|
1918
|
+
return true;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
// Unknown shapes: be safe and keep the rollback.
|
|
1922
|
+
default:
|
|
1923
|
+
return true;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
1927
|
+
if (seen.has(p)) return false;
|
|
1928
|
+
seen.add(p);
|
|
1929
|
+
const d = p._def;
|
|
1930
|
+
switch (d.tag) {
|
|
1931
|
+
case "literal":
|
|
1932
|
+
case "regex":
|
|
1933
|
+
case "keywords":
|
|
1934
|
+
case "node":
|
|
1935
|
+
return true;
|
|
1936
|
+
case "not":
|
|
1937
|
+
case "guard":
|
|
1938
|
+
case "trivia":
|
|
1939
|
+
case "unknown":
|
|
1940
|
+
return false;
|
|
1941
|
+
case "sequence":
|
|
1942
|
+
case "choice":
|
|
1943
|
+
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
1944
|
+
case "sepBy":
|
|
1945
|
+
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
1946
|
+
case "many":
|
|
1947
|
+
case "oneOrMore":
|
|
1948
|
+
case "optional":
|
|
1949
|
+
case "transform":
|
|
1950
|
+
case "label":
|
|
1951
|
+
case "expect":
|
|
1952
|
+
case "withCtx":
|
|
1953
|
+
case "grammar":
|
|
1954
|
+
case "recover":
|
|
1955
|
+
return capturesLeaf(d.parser, seen);
|
|
1956
|
+
case "skip":
|
|
1957
|
+
return capturesLeaf(d.main, seen);
|
|
1958
|
+
case "scanTo":
|
|
1959
|
+
return true;
|
|
1960
|
+
case "lazy": {
|
|
1961
|
+
try {
|
|
1962
|
+
return capturesLeaf(d.thunk(), seen);
|
|
1963
|
+
} catch {
|
|
1964
|
+
return true;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
default:
|
|
1968
|
+
return true;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1854
1971
|
function emitFallible(inner, ctx, pos) {
|
|
1855
1972
|
const lbl = v(ctx, "_lbl");
|
|
1856
1973
|
const okV = `${lbl}ok`;
|
|
@@ -1864,26 +1981,23 @@ function emitFallible(inner, ctx, pos) {
|
|
|
1864
1981
|
ctx.failLabel = savedLabel;
|
|
1865
1982
|
ctx.indent = savedIndent;
|
|
1866
1983
|
const ind0 = ind(ctx);
|
|
1867
|
-
const
|
|
1868
|
-
const
|
|
1869
|
-
const
|
|
1870
|
-
const
|
|
1984
|
+
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
1985
|
+
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
1986
|
+
const mR = needsRollback ? v(ctx, "_fcr") : null;
|
|
1987
|
+
const mTl = needsRollback ? v(ctx, "_fctl") : null;
|
|
1871
1988
|
const stmts = [
|
|
1872
1989
|
`${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
|
|
1873
1990
|
...mL ? [
|
|
1874
1991
|
`${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
|
|
1875
1992
|
`${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
|
|
1876
|
-
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0
|
|
1877
|
-
`${ind0}const ${mLg} = _ctx._triviaLog?.length ?? 0`
|
|
1993
|
+
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
|
|
1878
1994
|
] : [],
|
|
1879
1995
|
`${ind0}${lbl}: {`,
|
|
1880
1996
|
...r.stmts,
|
|
1881
|
-
`${ind0} ${valV} = ${r.valueVar}`,
|
|
1882
|
-
`${ind0} ${endV} = ${r.endVar}`,
|
|
1883
|
-
`${ind0} ${okV} = true`,
|
|
1997
|
+
`${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
|
|
1884
1998
|
`${ind0}}`,
|
|
1885
1999
|
...mL ? [
|
|
1886
|
-
`${ind0}if (!${okV}) {
|
|
2000
|
+
`${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
|
|
1887
2001
|
] : []
|
|
1888
2002
|
];
|
|
1889
2003
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
@@ -2157,11 +2271,13 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2157
2271
|
gateCond = `_mf[${gateIdx}](_ctx.state)`;
|
|
2158
2272
|
}
|
|
2159
2273
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2160
|
-
const
|
|
2161
|
-
const
|
|
2162
|
-
const
|
|
2163
|
-
const
|
|
2164
|
-
const
|
|
2274
|
+
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
2275
|
+
const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
|
|
2276
|
+
const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
|
|
2277
|
+
const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
|
|
2278
|
+
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2279
|
+
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2280
|
+
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
2165
2281
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2166
2282
|
ctx.indent++;
|
|
2167
2283
|
const ind1 = ind(ctx);
|
|
@@ -2191,11 +2307,7 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2191
2307
|
stmts.push(`${ind0} }`);
|
|
2192
2308
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2193
2309
|
} else {
|
|
2194
|
-
stmts.push(`${ind0} if (${okVar}) {`);
|
|
2195
|
-
stmts.push(`${ind0} ${resValV} = ${valVar}`);
|
|
2196
|
-
stmts.push(`${ind0} ${resEndV} = ${endVar}`);
|
|
2197
|
-
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2198
|
-
stmts.push(`${ind0} }`);
|
|
2310
|
+
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2199
2311
|
if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
|
|
2200
2312
|
}
|
|
2201
2313
|
stmts.push(`${ind0}}`);
|
|
@@ -2540,6 +2652,22 @@ function emitRecover(def, ctx, pos) {
|
|
|
2540
2652
|
];
|
|
2541
2653
|
return { stmts, valueVar: valVar, endVar };
|
|
2542
2654
|
}
|
|
2655
|
+
function emitExpect(def, ctx, pos) {
|
|
2656
|
+
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2657
|
+
const ind0 = ind(ctx);
|
|
2658
|
+
const errV = v(ctx, "_err");
|
|
2659
|
+
const stmts = [
|
|
2660
|
+
...innerStmts,
|
|
2661
|
+
`${ind0}if (!${okVar}) {`,
|
|
2662
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
|
|
2663
|
+
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2664
|
+
`${ind0} ${valVar} = ${errV}`,
|
|
2665
|
+
`${ind0} ${endVar} = ${pos}`,
|
|
2666
|
+
`${ind0} ${okVar} = true`,
|
|
2667
|
+
`${ind0}}`
|
|
2668
|
+
];
|
|
2669
|
+
return { stmts, valueVar: valVar, endVar };
|
|
2670
|
+
}
|
|
2543
2671
|
function emit(p, ctx, pos) {
|
|
2544
2672
|
const def = p._def;
|
|
2545
2673
|
switch (def.tag) {
|
|
@@ -2648,6 +2776,8 @@ function emit(p, ctx, pos) {
|
|
|
2648
2776
|
return emitScanTo(def, ctx, pos);
|
|
2649
2777
|
case "recover":
|
|
2650
2778
|
return emitRecover(def, ctx, pos);
|
|
2779
|
+
case "expect":
|
|
2780
|
+
return emitExpect(def, ctx, pos);
|
|
2651
2781
|
case "guard": {
|
|
2652
2782
|
const fnIdx = ctx.mapFns.length;
|
|
2653
2783
|
ctx.mapFns.push(def.predicate);
|
|
@@ -2731,6 +2861,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2731
2861
|
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
|
|
2732
2862
|
case "recover":
|
|
2733
2863
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
2864
|
+
case "expect":
|
|
2865
|
+
return hasNodeDef(d.parser, seen);
|
|
2734
2866
|
case "withCtx":
|
|
2735
2867
|
return hasNodeDef(d.parser, seen);
|
|
2736
2868
|
default:
|
|
@@ -2799,6 +2931,11 @@ function compile(combinator, mapFnSources) {
|
|
|
2799
2931
|
parseWithContext(input, parseCtx, pos = 0) {
|
|
2800
2932
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
|
|
2801
2933
|
},
|
|
2934
|
+
// Note: collects recover()/expect() errors via _errors. Unlike interpreter
|
|
2935
|
+
// parse({recover:true}) it does NOT populate furthestFail — the compiled path
|
|
2936
|
+
// inlines failures for throughput and deliberately skips _probe bookkeeping.
|
|
2937
|
+
// Callers wanting a furthest-position diagnostic detect unconsumed input
|
|
2938
|
+
// (span.end < input.length) instead, which is mode-agnostic.
|
|
2802
2939
|
parseWithErrors(input, pos = 0) {
|
|
2803
2940
|
const errors = [];
|
|
2804
2941
|
const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
|
|
@@ -2910,6 +3047,58 @@ function isParseError(value) {
|
|
|
2910
3047
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
2911
3048
|
}
|
|
2912
3049
|
|
|
3050
|
+
// src/combinators/expect.ts
|
|
3051
|
+
function staticExpected(c) {
|
|
3052
|
+
const def = c._def;
|
|
3053
|
+
switch (def.tag) {
|
|
3054
|
+
case "literal":
|
|
3055
|
+
return [JSON.stringify(def.value)];
|
|
3056
|
+
case "regex":
|
|
3057
|
+
return [`/${def.source}/`];
|
|
3058
|
+
case "keywords":
|
|
3059
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
3060
|
+
case "label":
|
|
3061
|
+
return [def.label];
|
|
3062
|
+
case "choice":
|
|
3063
|
+
return def.parsers.flatMap(staticExpected);
|
|
3064
|
+
case "sequence":
|
|
3065
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3066
|
+
case "node":
|
|
3067
|
+
case "grammar":
|
|
3068
|
+
case "trivia":
|
|
3069
|
+
case "optional":
|
|
3070
|
+
case "many":
|
|
3071
|
+
case "oneOrMore":
|
|
3072
|
+
case "transform":
|
|
3073
|
+
case "not":
|
|
3074
|
+
return staticExpected(def.parser);
|
|
3075
|
+
case "lazy":
|
|
3076
|
+
return staticExpected(def.thunk());
|
|
3077
|
+
default:
|
|
3078
|
+
return [];
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
function expect(combinator, label2) {
|
|
3082
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3083
|
+
const meta = {
|
|
3084
|
+
firstSet: combinator._meta.firstSet,
|
|
3085
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3086
|
+
isTrivia: false
|
|
3087
|
+
};
|
|
3088
|
+
return {
|
|
3089
|
+
_tag: "expect",
|
|
3090
|
+
_meta: meta,
|
|
3091
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3092
|
+
parse(input, pos, ctx) {
|
|
3093
|
+
const result = combinator.parse(input, pos, ctx);
|
|
3094
|
+
if (result.ok) return result;
|
|
3095
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3096
|
+
ctx._errors?.push(error);
|
|
3097
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3098
|
+
}
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
|
|
2913
3102
|
// src/combinators/completions.ts
|
|
2914
3103
|
function completionsAt(combinator, input, offset) {
|
|
2915
3104
|
const probe = { offset, best: null };
|
|
@@ -2938,7 +3127,11 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2938
3127
|
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
2939
3128
|
parse(input, pos, ctx) {
|
|
2940
3129
|
let cur = pos;
|
|
2941
|
-
const probeCtx = {
|
|
3130
|
+
const probeCtx = {
|
|
3131
|
+
trackLines: ctx.trackLines,
|
|
3132
|
+
state: ctx.state,
|
|
3133
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
3134
|
+
};
|
|
2942
3135
|
const emit2 = (end) => {
|
|
2943
3136
|
if (end > pos && cstCaptureActive(ctx)) {
|
|
2944
3137
|
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
@@ -2974,20 +3167,46 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2974
3167
|
}
|
|
2975
3168
|
function balanced(open, close, options = {}) {
|
|
2976
3169
|
const self = ref();
|
|
2977
|
-
const
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3170
|
+
const skips = options.skip ?? [];
|
|
3171
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
3172
|
+
let bounded = true;
|
|
3173
|
+
for (const sk of skips) {
|
|
3174
|
+
const cs = firstSetClassChars(sk);
|
|
3175
|
+
if (cs === null) {
|
|
3176
|
+
bounded = false;
|
|
3177
|
+
break;
|
|
3178
|
+
}
|
|
3179
|
+
for (const ch of cs) stop.add(ch);
|
|
3180
|
+
}
|
|
3181
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
3182
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
3183
|
+
const inner = many(choice(self, ...skips, content));
|
|
2981
3184
|
const combi = transform(
|
|
2982
|
-
sequence(literal(open), inner, literal(close)),
|
|
2983
|
-
(
|
|
3185
|
+
sequence(literal(open), inner, expect(literal(close), close)),
|
|
3186
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
3187
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
3188
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
2984
3189
|
);
|
|
2985
3190
|
if (combi._def.tag === "transform") {
|
|
2986
|
-
combi._def.fnSrc =
|
|
3191
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
2987
3192
|
}
|
|
2988
3193
|
self.define(combi);
|
|
2989
3194
|
return combi;
|
|
2990
3195
|
}
|
|
3196
|
+
function firstSetClassChars(c) {
|
|
3197
|
+
const fs = c._meta.firstSet;
|
|
3198
|
+
if (fs.kind === "empty") return [];
|
|
3199
|
+
if (fs.kind !== "ranges") return null;
|
|
3200
|
+
const out = [];
|
|
3201
|
+
for (const { lo, hi } of fs.ranges) {
|
|
3202
|
+
if (hi - lo > 8) return null;
|
|
3203
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3204
|
+
}
|
|
3205
|
+
return out;
|
|
3206
|
+
}
|
|
3207
|
+
function escapeClassChar(ch) {
|
|
3208
|
+
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3209
|
+
}
|
|
2991
3210
|
|
|
2992
3211
|
// src/functional/doc.ts
|
|
2993
3212
|
function isNode(x) {
|