/**
 * THE WORKING-DIRECTORY THREAD — where a command stands at each of its segments, and what `PWD` / `OLDPWD` hold there.
 * ONE walk (`shell-read-plan.ts` drives it, segment by segment, beside the binding thread) over a CLOSED word set that
 * moves the base ({@link BaseShift}): `cd` in its forms — a bare relative target searched through `CDPATH` first, `cd -`
 * going to `OLDPWD`, a bare `cd` to `HOME`, `-P` / `set -P` leaving `PWD` a spelling the text does not hold — `pushd` /
 * `popd` with the stack the text pushed, and a literal `eval` text read as the text it is, in this shell. Any other text
 * that may move the base where the plan cannot follow it (`source`, a non-literal `eval`, a computed program word, a
 * call into a function body or a code body that may move, an option the stack reading does not follow) LOSES it: what
 * stands after it cannot be placed. A move inside a subshell, a pipeline member or a function body nobody called is read
 * as a move too — a candidate SET, more places, never fewer (a move may also fail: the unmoved place stays a candidate
 * unless an `&&` run proves the move ran).
 */
import type { Bare } from "./shell-read-argv.js";
import type { BindingTable, WorkingDirectoryState } from "./shell-read-bindings.js";
import type { Read } from "./shell-read-word.js";
import { type LexedWord } from "../../shell/lex.js";
import { type FunctionCall } from "./shell-read-functions.js";
/** A working directory the shell could be standing in, with the latest move this candidate assumes did NOT run
 *  (`skippedCd`), and what the shell holds there: `pwd` — what `PWD` spells (absent: not the base's spelling — a physical
 *  move, or a start the plan does not place), `old` — what `OLDPWD` holds (absent: what the shell inherited, unknown),
 *  `stack` — the directories the text pushed and has not popped, top first (absent: the text changed the stack in a way
 *  this walk does not follow; what lies below the text's own entries is the shell's, unknown). */
export interface BaseCandidate {
    readonly base: string;
    readonly skippedCd: number;
    readonly pwd?: string;
    readonly old?: string;
    readonly dirStack?: readonly string[];
}
/** How one segment moves the base — the closed word set. `cd`: a `cd` (`push`: a `pushd DIR`) to a target, the
 *  declared-home spelling of an unsettled target for the deny floor; `swap`: `pushd` with no directory; `pop`: `popd`;
 *  `stack`: the stack changes and the base does not (`pushd -n`, `popd -n`, `dirs -c`); `eval`: a literal `eval` text;
 *  `lost`: a move this walk does not follow. */
export type BaseShift = {
    readonly kind: "cd";
    readonly target: Bare | "home" | "previous";
    readonly physical: boolean;
    readonly push?: true;
} | {
    readonly kind: "swap" | "pop" | "stack" | "lost";
} | {
    readonly kind: "eval";
    readonly text: string;
};
/** What the walk asks of the plan around it: the declared home (a `~` target), whether the text binds a name anywhere
 *  (`OLDPWD` — then `cd -` reads the table's value; `PWD` — then a move's `OLDPWD` is not the base the candidate stood
 *  in), and where a literal `eval` text started at a candidate ends ({@link WorkdirThread.end}). */
export interface WorkdirContext {
    readonly homeDir?: string;
    readonly textBinds: (name: string) => boolean;
    readonly evalEnd: (text: string, from: BaseCandidate, table: BindingTable) => {
        readonly end: readonly BaseCandidate[] | "unmodelled";
        readonly physical: boolean;
    };
}
/** One segment's move as the walk applies it: the shift, whether an `&&` run proves it ran, the first segment of the
 *  reader's `&&` run, the names there, and whether `cdable_vars` may be on (a bare name may be a variable's value). */
export interface ShiftAt {
    readonly shift: BaseShift;
    readonly provable: boolean;
    readonly andRunStart: number;
    readonly table: CommandTable;
    readonly cdable: boolean;
}
/** The names at a `cd` segment with the command's own prefix assignments in force (`HOME=/ cd`), and which names those are. */
export interface CommandTable extends BindingTable {
    readonly commandSets: ReadonlySet<string>;
}
export declare class WorkdirThread {
    /** The start is where the shell stands (not a placeholder a nested plan is re-based from): `PWD` reads it. */
    private readonly readable;
    private candidates;
    private lostAt;
    /** The reachable bases at the moment the walk lost track — the deny floor of a word it cannot place. */
    private floors;
    /** `set -P` (or `-o physical`) may be on — a text it began in, an `eval` text that set it — : a later move leaves `PWD` a physical
     *  spelling the text does not hold, and a `..` in a target resolves links the plan cannot see. */
    private physical;
    /** The last segment whose move was applied or lost track ({@link WorkingDirectoryState.movedAt}). */
    private movedAt;
    constructor(start: readonly BaseCandidate[] | readonly string[] | "unmodelled", 
    /** The start is where the shell stands (not a placeholder a nested plan is re-based from): `PWD` reads it. */
    readable: boolean, 
    /** The physical mode may already be on where the text starts (an `eval` text run in a shell that set it). */
    startsPhysical?: boolean);
    get lost(): boolean;
    get floorBases(): readonly string[] | undefined;
    /** The candidates a reader whose `&&` run starts at `andRunStart` may stand in. */
    reachable(andRunStart: number): readonly BaseCandidate[];
    /** Where the text ends: every candidate, or `unmodelled`. */
    end(): readonly BaseCandidate[] | "unmodelled";
    /** What `PWD` / `OLDPWD` hold for a reader whose `&&` run starts at `andRunStart`. */
    where(andRunStart: number): WorkingDirectoryState;
    /** `set -P` / `set -o physical`: from here on a move's `PWD` is a physical spelling. */
    setPhysical(): void;
    /** Where the text ends the physical mode may be on (an `eval` text hands it back to the shell that ran it). */
    get mayBePhysical(): boolean;
    /** Stop placing anything from segment `at` on: the reachable bases (and a target's floor spellings) become the deny floor. */
    lose(at: number, reachable: readonly BaseCandidate[], floorTargets?: readonly string[]): void;
    /** Apply segment `si`'s move. */
    shift(si: number, at: ShiftAt, ctx: WorkdirContext): void;
    /** Where one candidate goes: the places (the CDPATH entries a bare target is searched through, then the target itself),
     *  each with the `PWD`, `OLDPWD` and stack the move leaves — or `lost`. */
    private movedFrom;
}
/** Does bash search `CDPATH` for this `cd` target: not an absolute one, not `.` / `..`, not one starting `./` / `../`? */
export declare function searchesCdPath(dir: string): boolean;
/** The base shift of a `cd` / `pushd` / `popd` / `dirs` segment, read off its arguments as the program receives them (and
 *  the move the plan reports for it: a `cd` target, or `unmodelled` for a stack move it cannot name), or `undefined` for
 *  another program. `targetOf` turns the target word into its candidate (and records it as a read where the caller does). */
export declare function baseShiftOf(program: string, args: readonly Read[], targetOf: (r: Read) => Bare): {
    shift: BaseShift;
    reported?: Bare | "home" | "previous" | "unmodelled";
} | undefined;
/** May this text move the base of the shell that runs it: a program of the closed set or one running text in this shell,
 *  a computed program word, a call into one of `functions` — or text the lexer cannot read? */
export declare function textMayMove(text: string, functions: ReadonlySet<string>): boolean;
/** Does this text turn the physical mode on in the shell that runs it? An `eval` text that does is no text to skip. */
export declare function textSetsPhysical(text: string): boolean;
/** The table a `cd` / `pushd` reads `HOME`, `CDPATH` and `OLDPWD` from: the segment's own, with each prefix assignment in
 *  front of the builtin (`HOME=/ cd`, `CDPATH=/ cd usr`) holding its value there — it is in the builtin's environment. A
 *  prefix value naming an earlier prefix's name (`HOME=/ CDPATH=$HOME cd usr`) reads it as bash 5 does (the earlier
 *  assignment applied) and as bash 3.2 does (the value before the command): both. */
export declare function withCommandEnvironment(table: BindingTable, argv: readonly LexedWord[]): CommandTable;
/** Does this text call one of `functions` (a program word naming one) — or is it text the lexer cannot read? */
export declare function textCalls(text: string, functions: ReadonlySet<string>): boolean;
/** May a segment with these words move the base ({@link textMayMove})? */
export declare function segmentMayMove(argv: readonly LexedWord[], functions: ReadonlySet<string>): boolean;
/** What the walk reads of a whole text before it walks it: the text's functions (the names a program word can be one of;
 *  whether it IS one at a segment is the binding thread's answer, `callAt`); per segment whether it may move the base
 *  ({@link segmentMayMove}), may call a function, runs code bodies later (`trap`, `alias`, …), turns on the physical mode
 *  (`set -P`, `set -o physical`); whether a function body may move the base; where `cdable_vars` goes on. */
export interface TextMoves {
    readonly functions: ReadonlySet<string>;
    /** The names whose bodies the plan has not read (an `eval` text defined them). */
    readonly unread: ReadonlySet<string>;
    readonly mayMove: readonly boolean[];
    /** Segment `si` may call a function (read before the walk, to bound where bodies run). */
    readonly mayCallAt: readonly boolean[];
    /** Segment `si` may run a function body: it may call one, hands a code body to run later, or runs text the plan does not read. */
    readonly runsBodyAt: readonly boolean[];
    readonly bodiesAt: readonly (readonly string[])[];
    readonly physicalAt: readonly boolean[];
    readonly cdableFrom: number;
    readonly bodyMoves: boolean;
    /** The function segment `si`'s program word names where the walk stands (`table`) — {@link functionCallOf}. */
    callAt(si: number, table: BindingTable): FunctionCall | undefined;
    /** The segment may call a function a literal `eval` text defined: its body is not read — it may move the base and bind any name. */
    callsUnread(si: number, table: BindingTable): boolean;
    /** The segment runs a body that may move the base where the walk cannot follow it: a call when a body may move or whose body is not read, a code body that may move or call one. */
    losesBaseAt(si: number, table: BindingTable): boolean;
}
export declare function textMovesOf(defined: ReadonlySet<string>, argvs: readonly (readonly LexedWord[])[], inFunctionBody: (si: number) => boolean, inherited?: {
    readonly functions: ReadonlySet<string>;
    readonly unread: ReadonlySet<string>;
}): TextMoves;
