/**
 * The character-level scanners under the tightening reader's shell lexer (`shell-lexer.ts`): a double-
 * quoted body, an expansion (`$…`, a backtick, a process substitution), a balanced `(…)` / `{…}` region,
 * and the here-document bodies. Every scanner answers an INDEX (past what it read) or `undefined` for
 * "never closes", and reports through `word.substitution` / the heredoc result whether it passed a
 * COMMAND SUBSTITUTION — `$(…)`, a backtick, `<(…)`/`>(…)`, anywhere, including inside `${…}` and
 * `$((…))` — because a substitution runs a program the lexer does not read: the lexer marks the segment
 * and the reader answers `unreadable` for every rule the segment does not otherwise reach. Reading the
 * commands INSIDE a substitution was tried and retired: every bash quirk of substitution boundaries
 * (a `)` in a comment, a multi-line body in a heredoc, a `$"…"` string) became a way to hide a run, and
 * the fail-closed word costs an approval prompt, never a bypass. Split from the lexer so the lexer's own
 * ceiling holds the segment/peel logic and this one holds the byte walking.
 */
/** A word under construction; `braceOpen`/`braceExpands` track an unquoted `{a,b}` / `{1..3}` pattern, `bracketOpen` a `[…]` class;
 *  `substitution` is set when the word carries a command/process substitution anywhere. */
export interface Word {
    text: string;
    raw: string;
    expands: false | "one" | "many";
    started: boolean;
    braceOpen: boolean;
    braceExpands: boolean;
    bracketOpen: boolean;
    substitution: boolean;
}
export interface Heredoc {
    delimiter: string;
    quoted: boolean;
    dash: boolean;
}
/** Is `expr`, the interior of an arithmetic expansion or command, provably free of a hidden run? Only digits,
 *  operators and spaces are: a NAME or a `$…` evaluates recursively (`a='arr[$(…)]'; $((a))`), and anything
 *  else (`;`, a quote, a bracket) is not arithmetic at all — bash then re-reads `((…))` as nested subshells. */
export declare const arithmeticHidesRun: (expr: string) => boolean;
/** Does an expansion interior, an extended-glob interior or a heredoc line carry a run this reader does not
 *  read — a substitution opener, or a subscript that evaluates indirectly? Backslash-newline is removed
 *  first, as bash removes it before it reads the opener (`$\<newline>(` is `$(`). */
export declare const carriesSubstitution: (text: string) => boolean;
export declare const freshWord: () => Word;
/** Read a `"…"` body into `word` from `i` (just past the opening quote); returns the index past the closing quote. */
export declare function readDoubleQuoted(s: string, i: number, word: Word): number | undefined;
/** The index past the expansion starting at `i` (`$…`, a backtick, or a `<(`/`>(` process substitution);
 *  `i + 1` when the `$` is literal; `undefined` when it never closes. Sets `word.substitution` when the
 *  region is, or contains, a command/process substitution. A `$"…"` string is NOT read here (the lexer
 *  reads it as a double-quoted string, which is what it is); inside double quotes `$'` is a literal `$`
 *  (ANSI-C quoting exists only outside them). */
export declare function readExpansion(s: string, i: number, word: Word, inDoubleQuotes?: boolean): number | undefined;
/** Past the matching `close` for the `open` at `i`, quote-aware (a `)` inside `$(echo ")")` is text).
 *  LINEAR: one pass with a small state stack (nesting depth × quote state), no re-entry. */
export declare function skipBalanced(s: string, i: number, open: string, close: string): number | undefined;
/** Skip the here-document bodies that begin at `i` (the start of a line), one per delimiter in order,
 *  answering the index past them and whether an UNQUOTED body carries a substitution (bash expands such a
 *  body: `$(…)` in it runs). An unquoted body joins a line ending in an ODD run of backslashes with the
 *  next before the delimiter is compared; `<<-` strips leading tabs. */
export declare function skipHeredocBodies(s: string, i: number, docs: readonly Heredoc[]): {
    end: number;
    substitution: boolean;
} | undefined;
/** The LONGEST operator of `ops` at `i` (`>&` before `>`, `&&` before `&`). */
export declare function matchOp(s: string, i: number, ops: readonly string[]): string | undefined;
