parseman 0.7.0 → 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 +251 -27
- 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 +251 -27
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +250 -28
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.js +250 -28
- 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
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type { LineIndex } from './compiler/line-index.ts';
|
|
|
22
22
|
export { guard } from './combinators/guard.ts';
|
|
23
23
|
export { withCtx } from './combinators/withCtx.ts';
|
|
24
24
|
export { recover, isParseError } from './combinators/recover.ts';
|
|
25
|
+
export { expect, staticExpected } from './combinators/expect.ts';
|
|
25
26
|
export { completionsAt } from './combinators/completions.ts';
|
|
26
27
|
export { scanTo, balanced } from './combinators/scanTo.ts';
|
|
27
28
|
export type { ScanToOptions } from './combinators/scanTo.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEjM,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AACpE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAClE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE3F,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACxF,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAE5D,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAChG,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AACzF,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEjM,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AACpE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAClE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE3F,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACxF,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAChE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAE5D,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAChG,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AACzF,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1278,13 +1278,15 @@ function noTrivia(root) {
|
|
|
1278
1278
|
function parse(combinator, input, opts = {}) {
|
|
1279
1279
|
const trackLines = opts.trackLines ?? false;
|
|
1280
1280
|
const _errors = opts.recover ? [] : void 0;
|
|
1281
|
+
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
1281
1282
|
const ctx = {
|
|
1282
1283
|
trackLines,
|
|
1283
|
-
..._errors !== void 0 ? { _errors } : {}
|
|
1284
|
+
..._errors !== void 0 ? { _errors } : {},
|
|
1285
|
+
..._probe !== void 0 ? { _probe } : {}
|
|
1284
1286
|
};
|
|
1285
1287
|
const result = combinator.parse(input, 0, ctx);
|
|
1286
1288
|
if (!result.ok) return result;
|
|
1287
|
-
const withErrors = _errors !== void 0 ? { ...result, errors: _errors } : result;
|
|
1289
|
+
const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
|
|
1288
1290
|
if (trackLines) {
|
|
1289
1291
|
const idx = buildLineIndex(input);
|
|
1290
1292
|
return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
|
|
@@ -1780,6 +1782,119 @@ function firstSetCond(codeVar, fs) {
|
|
|
1780
1782
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1781
1783
|
).join(" || ");
|
|
1782
1784
|
}
|
|
1785
|
+
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1786
|
+
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1787
|
+
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
1788
|
+
}
|
|
1789
|
+
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
1790
|
+
if (seen.has(p)) return false;
|
|
1791
|
+
seen.add(p);
|
|
1792
|
+
const d = p._def;
|
|
1793
|
+
switch (d.tag) {
|
|
1794
|
+
// Atomic / non-capturing-then-failing: a failure happens before any push.
|
|
1795
|
+
case "literal":
|
|
1796
|
+
case "regex":
|
|
1797
|
+
case "keywords":
|
|
1798
|
+
case "guard":
|
|
1799
|
+
case "not":
|
|
1800
|
+
case "trivia":
|
|
1801
|
+
case "scanTo":
|
|
1802
|
+
case "unknown":
|
|
1803
|
+
return false;
|
|
1804
|
+
// node() captures into its own private buffers and rolls them back on
|
|
1805
|
+
// failure (emitNode restores _ctx.* and never pushes on the !ok path).
|
|
1806
|
+
case "node":
|
|
1807
|
+
return false;
|
|
1808
|
+
// choice/firstMatch already roll back each failed arm; on overall failure
|
|
1809
|
+
// nothing committed remains.
|
|
1810
|
+
case "choice":
|
|
1811
|
+
return false;
|
|
1812
|
+
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
1813
|
+
case "optional":
|
|
1814
|
+
case "many":
|
|
1815
|
+
case "oneOrMore":
|
|
1816
|
+
return false;
|
|
1817
|
+
// sepBy emits its own per-iteration rollback in emitSepBy.
|
|
1818
|
+
case "sepBy":
|
|
1819
|
+
return false;
|
|
1820
|
+
// A sequence is the real case: an earlier capturing term followed by a term
|
|
1821
|
+
// that can fail leaves the earlier captures buffered.
|
|
1822
|
+
case "sequence": {
|
|
1823
|
+
const parts = d.parsers;
|
|
1824
|
+
if (parts.length < 2) return parts.some((x) => mayLeavePartialCapture(x, seen));
|
|
1825
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1826
|
+
if (hasNodeDef(parts[i]) || capturesLeaf(parts[i])) return true;
|
|
1827
|
+
}
|
|
1828
|
+
return false;
|
|
1829
|
+
}
|
|
1830
|
+
// Delegating wrappers: defer to the wrapped parser.
|
|
1831
|
+
case "transform":
|
|
1832
|
+
case "label":
|
|
1833
|
+
case "expect":
|
|
1834
|
+
case "withCtx":
|
|
1835
|
+
case "grammar":
|
|
1836
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1837
|
+
case "skip":
|
|
1838
|
+
return mayLeavePartialCapture(d.main, seen);
|
|
1839
|
+
case "recover":
|
|
1840
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1841
|
+
case "lazy": {
|
|
1842
|
+
try {
|
|
1843
|
+
return mayLeavePartialCapture(d.thunk(), seen);
|
|
1844
|
+
} catch {
|
|
1845
|
+
return true;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
// Unknown shapes: be safe and keep the rollback.
|
|
1849
|
+
default:
|
|
1850
|
+
return true;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
1854
|
+
if (seen.has(p)) return false;
|
|
1855
|
+
seen.add(p);
|
|
1856
|
+
const d = p._def;
|
|
1857
|
+
switch (d.tag) {
|
|
1858
|
+
case "literal":
|
|
1859
|
+
case "regex":
|
|
1860
|
+
case "keywords":
|
|
1861
|
+
case "node":
|
|
1862
|
+
return true;
|
|
1863
|
+
case "not":
|
|
1864
|
+
case "guard":
|
|
1865
|
+
case "trivia":
|
|
1866
|
+
case "unknown":
|
|
1867
|
+
return false;
|
|
1868
|
+
case "sequence":
|
|
1869
|
+
case "choice":
|
|
1870
|
+
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
1871
|
+
case "sepBy":
|
|
1872
|
+
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
1873
|
+
case "many":
|
|
1874
|
+
case "oneOrMore":
|
|
1875
|
+
case "optional":
|
|
1876
|
+
case "transform":
|
|
1877
|
+
case "label":
|
|
1878
|
+
case "expect":
|
|
1879
|
+
case "withCtx":
|
|
1880
|
+
case "grammar":
|
|
1881
|
+
case "recover":
|
|
1882
|
+
return capturesLeaf(d.parser, seen);
|
|
1883
|
+
case "skip":
|
|
1884
|
+
return capturesLeaf(d.main, seen);
|
|
1885
|
+
case "scanTo":
|
|
1886
|
+
return true;
|
|
1887
|
+
case "lazy": {
|
|
1888
|
+
try {
|
|
1889
|
+
return capturesLeaf(d.thunk(), seen);
|
|
1890
|
+
} catch {
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
default:
|
|
1895
|
+
return true;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1783
1898
|
function emitFallible(inner, ctx, pos) {
|
|
1784
1899
|
const lbl = v(ctx, "_lbl");
|
|
1785
1900
|
const okV = `${lbl}ok`;
|
|
@@ -1793,26 +1908,23 @@ function emitFallible(inner, ctx, pos) {
|
|
|
1793
1908
|
ctx.failLabel = savedLabel;
|
|
1794
1909
|
ctx.indent = savedIndent;
|
|
1795
1910
|
const ind0 = ind(ctx);
|
|
1796
|
-
const
|
|
1797
|
-
const
|
|
1798
|
-
const
|
|
1799
|
-
const
|
|
1911
|
+
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
1912
|
+
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
1913
|
+
const mR = needsRollback ? v(ctx, "_fcr") : null;
|
|
1914
|
+
const mTl = needsRollback ? v(ctx, "_fctl") : null;
|
|
1800
1915
|
const stmts = [
|
|
1801
1916
|
`${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
|
|
1802
1917
|
...mL ? [
|
|
1803
1918
|
`${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
|
|
1804
1919
|
`${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
|
|
1805
|
-
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0
|
|
1806
|
-
`${ind0}const ${mLg} = _ctx._triviaLog?.length ?? 0`
|
|
1920
|
+
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
|
|
1807
1921
|
] : [],
|
|
1808
1922
|
`${ind0}${lbl}: {`,
|
|
1809
1923
|
...r.stmts,
|
|
1810
|
-
`${ind0} ${valV} = ${r.valueVar}`,
|
|
1811
|
-
`${ind0} ${endV} = ${r.endVar}`,
|
|
1812
|
-
`${ind0} ${okV} = true`,
|
|
1924
|
+
`${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
|
|
1813
1925
|
`${ind0}}`,
|
|
1814
1926
|
...mL ? [
|
|
1815
|
-
`${ind0}if (!${okV}) {
|
|
1927
|
+
`${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
|
|
1816
1928
|
] : []
|
|
1817
1929
|
];
|
|
1818
1930
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
@@ -2086,11 +2198,13 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2086
2198
|
gateCond = `_mf[${gateIdx}](_ctx.state)`;
|
|
2087
2199
|
}
|
|
2088
2200
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2089
|
-
const
|
|
2090
|
-
const
|
|
2091
|
-
const
|
|
2092
|
-
const
|
|
2093
|
-
const
|
|
2201
|
+
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
2202
|
+
const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
|
|
2203
|
+
const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
|
|
2204
|
+
const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
|
|
2205
|
+
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2206
|
+
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2207
|
+
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
2094
2208
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2095
2209
|
ctx.indent++;
|
|
2096
2210
|
const ind1 = ind(ctx);
|
|
@@ -2120,11 +2234,7 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2120
2234
|
stmts.push(`${ind0} }`);
|
|
2121
2235
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2122
2236
|
} else {
|
|
2123
|
-
stmts.push(`${ind0} if (${okVar}) {`);
|
|
2124
|
-
stmts.push(`${ind0} ${resValV} = ${valVar}`);
|
|
2125
|
-
stmts.push(`${ind0} ${resEndV} = ${endVar}`);
|
|
2126
|
-
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2127
|
-
stmts.push(`${ind0} }`);
|
|
2237
|
+
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2128
2238
|
if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
|
|
2129
2239
|
}
|
|
2130
2240
|
stmts.push(`${ind0}}`);
|
|
@@ -2469,6 +2579,22 @@ function emitRecover(def, ctx, pos) {
|
|
|
2469
2579
|
];
|
|
2470
2580
|
return { stmts, valueVar: valVar, endVar };
|
|
2471
2581
|
}
|
|
2582
|
+
function emitExpect(def, ctx, pos) {
|
|
2583
|
+
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2584
|
+
const ind0 = ind(ctx);
|
|
2585
|
+
const errV = v(ctx, "_err");
|
|
2586
|
+
const stmts = [
|
|
2587
|
+
...innerStmts,
|
|
2588
|
+
`${ind0}if (!${okVar}) {`,
|
|
2589
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
|
|
2590
|
+
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2591
|
+
`${ind0} ${valVar} = ${errV}`,
|
|
2592
|
+
`${ind0} ${endVar} = ${pos}`,
|
|
2593
|
+
`${ind0} ${okVar} = true`,
|
|
2594
|
+
`${ind0}}`
|
|
2595
|
+
];
|
|
2596
|
+
return { stmts, valueVar: valVar, endVar };
|
|
2597
|
+
}
|
|
2472
2598
|
function emit(p, ctx, pos) {
|
|
2473
2599
|
const def = p._def;
|
|
2474
2600
|
switch (def.tag) {
|
|
@@ -2577,6 +2703,8 @@ function emit(p, ctx, pos) {
|
|
|
2577
2703
|
return emitScanTo(def, ctx, pos);
|
|
2578
2704
|
case "recover":
|
|
2579
2705
|
return emitRecover(def, ctx, pos);
|
|
2706
|
+
case "expect":
|
|
2707
|
+
return emitExpect(def, ctx, pos);
|
|
2580
2708
|
case "guard": {
|
|
2581
2709
|
const fnIdx = ctx.mapFns.length;
|
|
2582
2710
|
ctx.mapFns.push(def.predicate);
|
|
@@ -2660,6 +2788,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2660
2788
|
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
|
|
2661
2789
|
case "recover":
|
|
2662
2790
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
2791
|
+
case "expect":
|
|
2792
|
+
return hasNodeDef(d.parser, seen);
|
|
2663
2793
|
case "withCtx":
|
|
2664
2794
|
return hasNodeDef(d.parser, seen);
|
|
2665
2795
|
default:
|
|
@@ -2728,6 +2858,11 @@ function compile(combinator, mapFnSources) {
|
|
|
2728
2858
|
parseWithContext(input, parseCtx, pos = 0) {
|
|
2729
2859
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
|
|
2730
2860
|
},
|
|
2861
|
+
// Note: collects recover()/expect() errors via _errors. Unlike interpreter
|
|
2862
|
+
// parse({recover:true}) it does NOT populate furthestFail — the compiled path
|
|
2863
|
+
// inlines failures for throughput and deliberately skips _probe bookkeeping.
|
|
2864
|
+
// Callers wanting a furthest-position diagnostic detect unconsumed input
|
|
2865
|
+
// (span.end < input.length) instead, which is mode-agnostic.
|
|
2731
2866
|
parseWithErrors(input, pos = 0) {
|
|
2732
2867
|
const errors = [];
|
|
2733
2868
|
const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
|
|
@@ -2839,6 +2974,58 @@ function isParseError(value) {
|
|
|
2839
2974
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
2840
2975
|
}
|
|
2841
2976
|
|
|
2977
|
+
// src/combinators/expect.ts
|
|
2978
|
+
function staticExpected(c) {
|
|
2979
|
+
const def = c._def;
|
|
2980
|
+
switch (def.tag) {
|
|
2981
|
+
case "literal":
|
|
2982
|
+
return [JSON.stringify(def.value)];
|
|
2983
|
+
case "regex":
|
|
2984
|
+
return [`/${def.source}/`];
|
|
2985
|
+
case "keywords":
|
|
2986
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
2987
|
+
case "label":
|
|
2988
|
+
return [def.label];
|
|
2989
|
+
case "choice":
|
|
2990
|
+
return def.parsers.flatMap(staticExpected);
|
|
2991
|
+
case "sequence":
|
|
2992
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
2993
|
+
case "node":
|
|
2994
|
+
case "grammar":
|
|
2995
|
+
case "trivia":
|
|
2996
|
+
case "optional":
|
|
2997
|
+
case "many":
|
|
2998
|
+
case "oneOrMore":
|
|
2999
|
+
case "transform":
|
|
3000
|
+
case "not":
|
|
3001
|
+
return staticExpected(def.parser);
|
|
3002
|
+
case "lazy":
|
|
3003
|
+
return staticExpected(def.thunk());
|
|
3004
|
+
default:
|
|
3005
|
+
return [];
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
function expect(combinator, label2) {
|
|
3009
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3010
|
+
const meta = {
|
|
3011
|
+
firstSet: combinator._meta.firstSet,
|
|
3012
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3013
|
+
isTrivia: false
|
|
3014
|
+
};
|
|
3015
|
+
return {
|
|
3016
|
+
_tag: "expect",
|
|
3017
|
+
_meta: meta,
|
|
3018
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3019
|
+
parse(input, pos, ctx) {
|
|
3020
|
+
const result = combinator.parse(input, pos, ctx);
|
|
3021
|
+
if (result.ok) return result;
|
|
3022
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3023
|
+
ctx._errors?.push(error);
|
|
3024
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
|
|
2842
3029
|
// src/combinators/completions.ts
|
|
2843
3030
|
function completionsAt(combinator, input, offset) {
|
|
2844
3031
|
const probe = { offset, best: null };
|
|
@@ -2867,7 +3054,11 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2867
3054
|
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
2868
3055
|
parse(input, pos, ctx) {
|
|
2869
3056
|
let cur = pos;
|
|
2870
|
-
const probeCtx = {
|
|
3057
|
+
const probeCtx = {
|
|
3058
|
+
trackLines: ctx.trackLines,
|
|
3059
|
+
state: ctx.state,
|
|
3060
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
3061
|
+
};
|
|
2871
3062
|
const emit2 = (end) => {
|
|
2872
3063
|
if (end > pos && cstCaptureActive(ctx)) {
|
|
2873
3064
|
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
@@ -2902,16 +3093,47 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2902
3093
|
};
|
|
2903
3094
|
}
|
|
2904
3095
|
function balanced(open, close, options = {}) {
|
|
2905
|
-
const
|
|
3096
|
+
const self = ref();
|
|
3097
|
+
const skips = options.skip ?? [];
|
|
3098
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
3099
|
+
let bounded = true;
|
|
3100
|
+
for (const sk of skips) {
|
|
3101
|
+
const cs = firstSetClassChars(sk);
|
|
3102
|
+
if (cs === null) {
|
|
3103
|
+
bounded = false;
|
|
3104
|
+
break;
|
|
3105
|
+
}
|
|
3106
|
+
for (const ch of cs) stop.add(ch);
|
|
3107
|
+
}
|
|
3108
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
3109
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
3110
|
+
const inner = many(choice(self, ...skips, content));
|
|
2906
3111
|
const combi = transform(
|
|
2907
|
-
sequence(literal(open), inner, literal(close)),
|
|
2908
|
-
(
|
|
3112
|
+
sequence(literal(open), inner, expect(literal(close), close)),
|
|
3113
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
3114
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
3115
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
2909
3116
|
);
|
|
2910
3117
|
if (combi._def.tag === "transform") {
|
|
2911
|
-
combi._def.fnSrc =
|
|
3118
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
2912
3119
|
}
|
|
3120
|
+
self.define(combi);
|
|
2913
3121
|
return combi;
|
|
2914
3122
|
}
|
|
3123
|
+
function firstSetClassChars(c) {
|
|
3124
|
+
const fs = c._meta.firstSet;
|
|
3125
|
+
if (fs.kind === "empty") return [];
|
|
3126
|
+
if (fs.kind !== "ranges") return null;
|
|
3127
|
+
const out = [];
|
|
3128
|
+
for (const { lo, hi } of fs.ranges) {
|
|
3129
|
+
if (hi - lo > 8) return null;
|
|
3130
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3131
|
+
}
|
|
3132
|
+
return out;
|
|
3133
|
+
}
|
|
3134
|
+
function escapeClassChar(ch) {
|
|
3135
|
+
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3136
|
+
}
|
|
2915
3137
|
|
|
2916
3138
|
// src/functional/doc.ts
|
|
2917
3139
|
function isNode(x) {
|
|
@@ -3111,6 +3333,7 @@ export {
|
|
|
3111
3333
|
choice,
|
|
3112
3334
|
compile,
|
|
3113
3335
|
completionsAt,
|
|
3336
|
+
expect,
|
|
3114
3337
|
guard,
|
|
3115
3338
|
isParseError,
|
|
3116
3339
|
keywords,
|
|
@@ -3135,6 +3358,7 @@ export {
|
|
|
3135
3358
|
sepBy,
|
|
3136
3359
|
sequence,
|
|
3137
3360
|
skip,
|
|
3361
|
+
staticExpected,
|
|
3138
3362
|
transform,
|
|
3139
3363
|
trivia,
|
|
3140
3364
|
triviaEntries,
|