/** * COMMITMENT ANALYSIS — where a construct can still fail after it has consumed * input or dirtied the capture buffers. * * This is a whole-grammar property of the rule graph, and it has exactly two * consumers: the compiler, which bakes the decisions into the emitted source, * and the interpreter, which must reach the SAME decisions at load time. It * lives here, in one module, for that reason. Two hand-written copies of a * predicate like `mayFail` would drift, and a drift here is not a size * regression — it is the compiled and interpreted parsers accepting different * languages. One module, two consumers. * * Every predicate in this file is a sound OVER-approximation of "something can * go wrong": each MUST err toward `true`. A `false` is a proof obligation * discharged on the caller's behalf, and callers delete code on the strength of * it. Elide only where non-failure is PROVEN; when in doubt, say `true` and keep * the machinery. */ import type { Combinator } from '../types.ts'; import type { RefResolver } from '../combinators/first-set.ts'; /** * Can `p` FAIL at all? * * `false` needs a construct whose definition is total: the empty literal, * trivia (a scan of width 0 is a match), and the repeats — but the repeats only * conditionally, see below. * * Everything else stays `true`, including two cases that look elidable and are * not. A `choice` whose last arm is infallible is NOT infallible, because * first-set gating decides at runtime which arms are entered at all and the * total arm may be skipped. And a re-entered cycle is `true` rather than * `false`: a recursive rule's own fallibility is exactly what the recursion is * being asked about, so the safe fixpoint seed is the conservative one. * * TODO(0.47): `expect` is conservative here. It converts its inner parser's * failure into a zero-width success carrying a parse-error node and therefore * never fails, but it currently reports its inner parser's fallibility. Making * it `false` would elide further; it needs its own pass through the tree-diff * gate first, because `expect` sits at exactly the closing-delimiter positions * where a wrong answer is most visible. */ export declare function mayFail(p: Combinator, seen?: Set>): boolean; /** * Does `p`, WHEN IT SUCCEEDS, always consume at least one character? * * The mirror of `mayFail`, and the opposite polarity from * `first-set.ts`'s `matchesEmpty`: this one MUST err toward `false`. A `true` * is a proof that the success path advanced the position, and the sequence * emitter deletes a trivia rewind on the strength of it. * * It cannot be written as `!matchesEmpty(p)`. `matchesEmpty` answers a * first-set question and is allowed to under-report nullability; for `expect` * it does, and that case is exactly the one that matters here. `expect(X)` * turns X's FAILURE into a zero-width SUCCESS carrying a parse-error node — * so `expect(literal('}'))` succeeds having consumed nothing, while * `matchesEmpty` looks through to the non-nullable literal and says "always * consumes". Eliding on that answer moves a rule's span end past trailing * trivia (measured: 38 tree mismatches across the css corpus, spans absorbing * whitespace their legacy build excluded). `recover` manufactures the same * zero-width success, and both are `false` here. * * `dispatch` is `false` rather than deferring to its selector: the selector's * consumption is not unconditionally part of the dispatch's own span, and the * few bytes are not worth the proof obligation. */ export declare function alwaysConsumes(p: Combinator, seen?: Set>): boolean; /** * True when parsing `p` may push a capture (leaf/child/trivia) into the active * buffers and THEN fail, leaving partial state that an enclosing node() would * wrongly absorb. Used to decide whether a fallible block needs CST-rollback. * * Sound over-approximation: the ONLY constructs that capture-then-fail are * - a sequence whose non-final term captures before a later term can fail * - a sepBy/oneOrMore item-then-separator partial (handled by their own * dedicated rollback, so still covered conservatively here) * Atomic terminals (literal/regex/keywords/charClass/guard/not) fail without * having captured. node() buffers into a private sub-scope and discards it on * failure, so it never leaks. choice/firstMatch roll back each failed arm * internally. optional/many never "fail" with partial output. Delegating * wrappers (transform/label/grammar/withCtx/expect) pass through to inner. */ export declare function mayLeavePartialCapture(p: Combinator, seen?: Set>, triviaActive?: boolean): boolean; /** True when `p` can push a leaf/node into the capture buffers on success. */ export declare function capturesLeaf(p: Combinator, seen?: Set>): boolean; /** * Does this combinator tree contain a node() anywhere (following ref/lazy * thunks)? Determines whether the compile emits CST capture — so non-node * grammars stay byte-identical. `seen` guards against recursion cycles. */ export declare function hasNodeDef(p: Combinator, seen?: Set>): boolean; /** * Whether a grammar tree owns a DIRECT semantic node reduction — a `node(..., build)` * whose callback produces the value itself, as opposed to a purely structural * `node(parser)`. * * It is the predicate behind `hostBranchElided`: an artifact only drops a * positioned-CST branch if there was a direct builder to drop, so an all-structural * grammar stays usable with either host (`cst/host-mode.ts`). * * It belongs in THIS module for the reason stated at the top of it: both lowerings * must reach the same answer, and a stamp they disagree about is a driver that * accepts a host it should refuse. It previously lived in `compiler/codegen.ts`, * which forced `table/compile-rule-map.ts` to import the engine the table replaced. * * The descent mirrors the source lowering's own `childrenOf` exactly — including a * `routed()` fallback and a `dispatch` matcher arm, both of which are real emit * sites and neither of which `hasNodeDef` above walks. */ export declare function hasDirectBuildDef(p: Combinator, seen?: Set>): boolean; /** True when `p` can report a committed failure through emitFallible's failure channel. */ export declare function mayCommitFailure(p: Combinator, seen?: Set>, resolve?: RefResolver): boolean; /** * `hasDirectBuilders` / `isRecognitionOnly` for a rule map WITHOUT lowering it. * * `composeLeaf()` gates on both: every pre-final grammar must prove recognition-only, * and the local leaf's direct builders decide whether the recognition pieces need * terminal capture. Both were only ever available as fields on `LinkablePieces`, so * the gate forced a full `compileLinkable()` of every piece purely to read two * booleans off the result — which is why the table lowering appeared to be blocked on * porting the source lowering wholesale. * * They are not lowering products. Both are predicates over the COMBINATOR GRAPH, and * this computes them from the graph directly, with the SAME `externalRefs` rule the * lowering applies (`:6008`): a named `lazy` whose thunk throws is a HOLE, bound by * name at fuse time, and is therefore not evidence of unknown semantics. An UNNAMED * unresolved `ref()` stays semantic — nobody can bind it, so it fails closed. */ export declare function classifyRuleMap(ruleMap: ReadonlyArray]>): { hasDirectBuilders: boolean; isRecognitionOnly: boolean; }; //# sourceMappingURL=commitment.d.ts.map