@sema-agent/core
Version:
Stateless, task-oriented AI agent core
458 lines (457 loc) • 36.1 kB
TypeScript
/**
* THE BINDING RULE — which shell names a command text binds, and to what, read off the one lexer's facts and the
* program read model's roles. A word that expands a name (`"$F"`, `${F}`, a `~` reading `HOME`) names a path only
* through what the text bound that name to; this module answers "what may `F` hold here", and nothing else.
*
* Two halves:
* · {@link bindingEffectOf} — what ONE segment does to the names: binds a name to a value (an assignment statement, a
* declaration builtin's `NAME=value`, a `for` list), makes a name unknown (a builtin whose operand role binds a name,
* an assigning expansion `${N:=…}`, an arithmetic assignment, a code body that runs later), gives a name an ATTRIBUTE
* (integer, a case change, an array, read-only: a later assignment to it stores something other than its text, or
* nothing), or makes EVERY name unknown (a name reference, `eval`, `source`, a computed name, an unquoted
* here-document `${`), and resets every name to the state it starts the text in (a function body: it runs at its
* call, against that table);
* · {@link BindingThread} — the effects threaded through the segments the way the plan threads the working directory
* (`shell-read-plan.ts`), in their ORDERED state: a binding is one for a reader only where it may run before the
* reader in the same shell — not one written after it, inside a subshell the reader is not in (a closed `( … )`, a
* pipeline member that is not the last, a background job) or in a function body (it runs at a call: a call the plan
* sees makes every name a body binds unknown from there). A value bound in a segment that may not have run before
* a reader (an `&&` / `||` run, a branch, a loop body, the last pipeline member) joins the values the name held
* before instead of replacing them. A reader in a function body loses every name another body binds — unless the
* body declared it with a plain `local`. `PWD` / `OLDPWD` start from what the working-directory thread says.
* An attribute and a name reference hold for every segment they may RUN before: a later one, one in a function body
* (it runs at its call), one in the same loop body (a later beat).
*
* A name the text does not settle is UNKNOWN, and a word expanding an unknown name stays a word the shell rewrites (its
* read cannot be established). `HOME` is an ordinary name whose state at the start of a text is the declared home.
* The shell's own variables (the ones bash sets, holds read-only or evaluates itself) are never bound from text.
*/
import { type LexedRedirect, type LexedSegment, type LexedToken, type LexedWord } from "../../shell/lex.js";
import { type DeclaredAttribute } from "./program-read-model.js";
import { type BuiltinState } from "./shell-read-builtins.js";
/** A value a name may hold — the text the program receives ({@link spelled}: what a shell re-parsing it reads), literal
* throughout, or a PATTERN whose matches are the values (a `for` list word the shell globs before the loop binds it;
* `spelled` is then the pattern as the program receives it where it matches nothing): `glob`, with its `parts` — the
* value in runs: a literal run (quoted or literal text, a settled value) and a pattern run (glob pieces as written,
* quoting kept, and as a reader judges them — {@link WordExpansion.pattern}). The one producer is the piece walk
* ({@link valueOf}). */
export type BoundValue = {
readonly spelled: string;
readonly glob?: never;
readonly parts?: never;
readonly fields?: readonly string[];
} | {
readonly spelled: string;
readonly glob: true;
readonly parts: readonly ValuePart[];
readonly fields?: never;
};
/** One run of a pattern {@link BoundValue}: literal text, or a pattern — the shell expands its characters — `text` as
* written (what a shell re-reading it matches by), `judged` as a reader judges the names it matches. */
export type ValuePart = {
readonly text: string;
readonly pattern: false;
} | {
readonly text: string;
readonly pattern: true;
readonly judged: string;
};
/** A spelling the deny judge alone reads ({@link Binding.floor}, {@link wordFloorValues}): a value, or a word as written
* where its names are unknown — nothing re-reads it, so a pattern says no more than that it is one. */
export interface FloorValue {
readonly spelled: string;
readonly glob?: true;
/** A pattern's spelling as a reader judges the names it matches, where it is not `spelled` ({@link judgedSpellingOf}). */
readonly pattern?: string;
}
/** A pattern value's names as a reader judges them, where that is not its spelling: its runs, a class the judge widens
* as `?` (the lexer's `WordExpansion.pattern.judged`), a leading `~` the literal tilde it is. */
export declare function judgedSpellingOf(v: BoundValue): string | undefined;
/** A value as a deny-floor spelling, its judged spelling with it. */
export declare function floorOf(v: BoundValue): FloorValue;
/** The values a name may hold where the text settles it. */
export type NameValues = readonly BoundValue[];
/** What a name holds at one point of a text: its possible values, or `"unknown"`. (The values are a NAMED arm: the
* release manifest reads a union that spells a word as a vocabulary, and a `readonly …[]` arm beside the word is a shape
* it cannot enumerate — it would then publish the set unreadable.) */
export type NameState = NameValues | "unknown";
/**
* The variables bash keeps itself — it sets them (`PWD`, `REPLY`, `OPTARG`, `BASH_REMATCH`, …), holds them read-only
* (`SHELLOPTS`, `BASHOPTS`, `BASH_VERSINFO`: an assignment to one is refused), or evaluates every value assigned to them
* as ARITHMETIC (the integer variables: `OPTIND`, `RANDOM`, `LINENO`, `HISTCMD`, `BASHPID`, …; the reference
* implementation's table of them, `zsh`'s included, is read whole: a member that is not one in this shell costs a
* question, never a read). Their value is never the text that bound it.
*/
export declare const SHELL_INTEGER_VARIABLES: ReadonlySet<string>;
/** Is this a variable bash keeps itself ({@link SHELL_INTEGER_VARIABLES}, or one it sets)? The working-directory names
* ({@link WORKING_DIRECTORY_NAMES}) are not: their value is the working-directory thread's ({@link WorkingDirectoryState}). */
export declare function shellKeepsName(name: string): boolean;
/** The names bash sets when the working directory moves: `PWD` (where the shell stands, as the move spelled it) and
* `OLDPWD` (the `PWD` it stood in before the last move — `cd -` goes there). A text may assign them too. */
export declare const WORKING_DIRECTORY_NAMES: readonly ["PWD", "OLDPWD"];
export type WorkingDirectoryName = (typeof WORKING_DIRECTORY_NAMES)[number];
/** What the working-directory thread (`shell-read-plan.ts`) says the two names hold at one segment, before anything the
* text assigns them: the bases it may stand in (`PWD`) and the ones it stood in before its last move (`OLDPWD`) —
* `unknown` where it lost track, the move's spelling is not the base (a physical move), or the shell inherited it. */
export type WorkingDirectoryState = {
readonly [K in WorkingDirectoryName]: NameState;
} & {
/** The last segment before the reader whose move the thread applied or lost track at (-1: none in this text). */
readonly movedAt: number;
};
/** The POSIX special builtins: a prefix assignment to one stays after it, and one runs before a function of its name
* (in POSIX mode, which an inherited environment can turn on unseen). */
export declare const SPECIAL_BUILTINS: ReadonlySet<string>;
export declare const functionKey: (name: string) => string;
/** What a function name is at a reader: `defined` — it certainly is one; `absent` — it certainly is not; `maybe` — a branch,
* a loop, a body that may not have run, a removal that may not have found it. */
export type FunctionState = "defined" | "maybe" | "absent";
/** One binding a segment makes: a name and what it now holds (`unknown` = made unknown). `prefix`: a binding that may or
* may not take effect (a prefix assignment to a special builtin, which may or may not outlive its command; an `unset` of a
* function, which a read-only function refuses). `loopVar`: a `for` loop's variable, held through its body. */
export interface Binding {
readonly name: string;
readonly state: NameState;
readonly prefix?: true;
readonly loopVar?: true;
/** `local`: a plain `local NAME…` — in a function body the name is the body's own and never leaves it. `unread`: a
* declaration builtin with an option word (`declare -g`, `local -p`, `local -r`): which scope it binds in is not
* read, so it is neither the body's own name nor covered by an earlier `local`. */
readonly scope?: "local" | "unread";
/** Spellings the name may hold that the deny judge still reads where the name is otherwise unknown (an array's
* elements: what `${A[k]}` expands to is one of them). */
readonly floor?: readonly FloorValue[];
}
/** An attribute a declaration gives a name — or every name (`name` absent: the declaration computed it): a later
* assignment to it is evaluated as arithmetic (`integer`), stores a case change or an array element (`transformed`), or
* is refused (`frozen`) — it binds something other than its text. */
export interface NameAttribute {
readonly name?: string;
readonly kind: Exclude<DeclaredAttribute, "reference" | "unsettled">;
}
/** What ONE segment does to the names. `all`: every name made unknown (`unknown`), or reset to its state at the start
* of the text (`reset` — a function body). `attributes`: the attributes it gives ({@link NameAttribute}). `references`:
* a name reference was declared — from here on an assignment to any name may write through it, so no name holds what
* the text assigned. `inFunction`: the builtin assigns only inside a function body (`local`) — outside one bash refuses
* the command, and its bindings hold nothing the text says. */
export interface BindingEffect {
readonly bindings: readonly Binding[];
readonly all?: "unknown" | "reset";
readonly attributes?: readonly NameAttribute[];
readonly references?: true;
readonly inFunction?: true;
}
/** How one value word reads against the table, the earlier assignments of the same segment applied (bash assigns left
* to right): its possible values, or `unknown`. */
export type ValueReader = (word: Pick<LexedWord, "pieces">, quoted: boolean, overlay: ReadonlyMap<string, NameState>) => NameState;
/** The segment's shape the effect needs beside its words: the functions whose definition heads were dropped from it. */
export interface SegmentShape {
readonly defines: readonly string[];
}
/** The argv projection of a lexed segment: structure that names nothing that runs here is dropped — a subshell
* parenthesis, a bare `{` / `}`, a function definition's head (`NAME ()`, and `function NAME [()]` with a name of
* any spelling, {@link dropDefinitionHeads}), a `case` arm's pattern ({@link dropCasePattern}), and the block words
* at the head. */
export interface ProjectedSegment extends SegmentShape {
readonly argv: readonly LexedWord[];
readonly redirects: readonly LexedRedirect[];
readonly grouped: boolean;
}
export declare function projectSegment(segment: LexedSegment): ProjectedSegment;
/** A function definition's head names nothing that runs here and is dropped from a segment's tokens, in place:
* `NAME ( )` ({@link isDefinitionName}), and `function NAME [( )]` — a bare `function` at command position, a name of any
* spelling. The lexer puts the body's compound command back at command position after the head, so a definition
* nested in a body is dropped the same way; after a drop the token that moved up is looked at again. Answers where the
* heads stood: entry `k` names the functions whose heads were dropped right before the token now at `k` (the last entry,
* past the last token, a head whose body opens in a later segment). */
export declare function dropDefinitionHeads(tokens: LexedToken[]): readonly (readonly string[])[];
/**
* What a word holds through the names it refers to — `$NAME`, `${NAME}`, a leading `~` (the value of `HOME`): `undefined`
* where it refers to no name (it reads as written), else {@link valueOf}. `table.stateOf` is read after `overlay` (the
* names the same segment bound earlier).
*/
export declare function wordValues(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined, quoted: boolean, overlay?: ReadonlyMap<string, NameState>, split?: boolean): readonly BoundValue[] | "unknown" | undefined;
/** The names a word's expansions read as values — a bare `$NAME` / `${NAME}`, and one written inside a bracket the shell
* forms its class from after expanding it ({@link valueOf}) — in the order they stand. */
export declare function namesIn(word: Pick<LexedWord, "pieces">): string[];
/**
* What a VALUE word holds (an assignment's value, a `for` list word, a word handed to a program) — THE piece walk, the one
* producer of {@link BoundValue}s: every combination of the values its pieces take, each field's pattern formed once the
* whole field has expanded ({@link formedField}) — a literal piece its text, a glob piece its pattern as written (a bracket
* holding an expansion its brackets and its body, the body's names substituted in place), `~` and `$NAME` the values the
* table gives:
* · its values — a field the shell globs makes the value a pattern;
* · `"unknown"` — a name it refers to is not settled here, it carries an expansion no name answers (a modifier, a
* substitution, a brace list, `~user`), an UNQUOTED expansion's value is no {@link READABLE_VALUE} (or holds
* whitespace outside `split`), `IFS` may have changed (`quoted` = the whole word is read as one value: an assignment's
* value, which is never split), a field is not formed or a split one forms a pattern, or it would list more than
* {@link WORD_VALUES_MAX} values.
* `split` = the word is read as the ARGUMENTS it becomes: an unquoted value is split into its words on the default `IFS`
* (a value then carries its {@link BoundValue} `fields`).
*/
export declare function valueOf(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined, quoted: boolean, overlay?: ReadonlyMap<string, NameState>, split?: boolean): NameState;
/**
* The spellings a word the table leaves unknown may still take through the deny floors of the names it refers to
* ({@link BindingTable.floorOf}) — `${A[k]}`, `${A[@]}`, `$A` reading an array's elements — or through their values where
* known: for the deny judge alone. Empty where some name it refers to leaves neither.
*/
export declare function wordFloorValues(word: Pick<LexedWord, "pieces">, table: BindingTable | undefined): readonly FloorValue[];
/** Where a segment's argv heads a `for` / `select` loop: the index of that bare word, at the segment's start or past the
* bare control words in front of it (`do for x in …`, `then for …` — a loop nested in a body or a branch); `undefined`
* for any other segment. */
export declare function loopListHeadAt(argv: readonly LexedWord[]): number | undefined;
/**
* What ONE segment does to the names (see the module header). `argv` = the segment's argv projection (structure
* dropped); `readValue` folds a value word against the table (a reader that answers `unknown` gives the names alone).
* `shadow`: the word the shell looks up first is a function of the text where the segment stands (`named`: it is one, the
* builtin of its name does not run — the call's effect is the body's; `maybe`: it may be one, so what the builtin binds is one
* more value its names may hold). `availability`: whether the builtin of a name runs by it where the segment stands
* ({@link BuiltinState}) — `disabled`: the word finds no command and binds nothing, `maybe`: as a function that may stand.
*/
export declare function bindingEffectOf(segment: LexedSegment, argv: readonly LexedWord[], shape: SegmentShape, readValue: ValueReader, shadow?: "named" | "maybe", availability?: (builtin: string) => BuiltinState): BindingEffect;
/**
* The names an ARITHMETIC text may assign: every identifier in a text that carries an assignment operator (`=` that is no
* comparison, `+=` and its kin, `++`, `--`) — read whole, not parsed: a name only compared there is made unknown too.
*/
export declare function arithmeticAssignedNames(text: string): string[];
/** The names a text binds anywhere (every segment, every name the binding rule reads), or `all` where it may bind any
* name — for a body that runs later. Unreadable text binds any. */
export declare function namesBoundIn(text: string, depth?: number): ReadonlySet<string> | "all";
/**
* Does this text bind `HOME` anywhere (or every name)? — the question a reader that spells the declared home for `~` /
* `$HOME` asks of a text it reads as ONE unit (the read-only classifier's segments). The read station asks the
* threaded question instead ({@link BindingThread}).
*/
export declare function textBindsHome(text: string): boolean;
/** What a name holds at one point of a text, asked by name ({@link BindingTable.stateOf}). */
export type NameReader = (name: string) => NameState;
/** What holds for the names where a nested text starts, given by the text around it: the attributes
* ({@link NameAttribute}) and whether a name reference was declared — a substitution body starts with what may run
* before its segment, a text run later or elsewhere (a code body, `eval`'s text, a child shell's `-c`) with all the
* text around it gives. */
export interface InheritedNames {
readonly attributes: readonly NameAttribute[];
readonly references: boolean;
/** The deny floors a name starts the text with ({@link BindingTable.floorOf}): what the words handing it this text as
* written may name there — or, for a substitution body, the floors its segment reads. */
readonly floors?: ReadonlyMap<string, readonly FloorValue[]>;
/** The names something before the nested text may have changed — a change that can reach where it starts, and the
* names its own segment's command environment sets (`TMPDIR=… bash -c …`): there such a name no longer holds what the
* shell provides ({@link BindingTable.providedByShell}). `all`: any name. Absent: none. */
readonly rebound?: ReadonlySet<string> | "all";
}
/** The function and builtin keys of the shell a text runs in — a literal `eval` text, a substitution body (a fork): what each key
* holds where the text starts (the outer table's answer for it), and the names a call to one of the outer functions may change. */
export interface InheritedFunctions {
readonly start: (key: string) => NameState;
readonly bound: ReadonlySet<string> | "all";
}
/** Two rebound sets as one. */
export declare function joinRebound(a: ReadonlySet<string> | "all" | undefined, b: Iterable<string> | "all" | undefined): ReadonlySet<string> | "all";
/** What the thread answers for one reader: what a name may hold, and whether a name carries the integer attribute. */
export interface BindingTable {
stateOf(name: string): NameState;
/** Values assigned to this name are evaluated as arithmetic. */
isInteger(name: string): boolean;
/** The text may have changed `IFS` before this point: an unquoted expansion is split on what the text does not say. */
readonly ifsTouched: boolean;
/** `HOME` may hold something other than the declared home here. */
readonly homeTouched: boolean;
/** Every spelling any binding of this name before here left for the deny judge alone ({@link Binding.floor}). */
floorOf(name: string): readonly FloorValue[];
/** What a substitution body here starts with. */
readonly inherited: InheritedNames;
/** The names a change that can reach this point touched — a binding, or a name made unknown, that may run before it in
* this shell (the thread's order: {@link BindingThread}) — and the names its start says were rebound; `all`: any. */
readonly rebound: ReadonlySet<string> | "all";
/** Does this name still hold what the shell provides here: no change that can reach this point touched it? */
providedByShell(name: string): boolean;
/** What a function of this name is here: the one ordered rule that answers a variable's value answers it
* ({@link FunctionState}). A name the text never defines is `absent` to the caller's own filter, not to this thread. */
functionState(name: string): FunctionState;
/** Whether the builtin of this name runs by it here: the same ordered rule ({@link BuiltinState}). */
builtinState(name: string): BuiltinState;
}
/**
* The effects of a text's segments, threaded the way the plan threads its working directory: a reader at segment `j`
* sees the latest binding of a name, and, where that binding may not have run before `j`, the bindings before it too.
* A binding is CERTAIN for `j` when no block holding it (a branch, a loop, a group) closes or switches branch between it
* and `j` — a block it precedes may come and go — its segment is no pipeline member, background job or prefix
* assignment, and — reached through `&&` / `||` — it lies in `j`'s own `&&` run (`andRunStart`: the same index the
* working-directory thread reads).
*
* A binding exists for a reader only where it may run before the reader IN THE SAME SHELL — the ordered state: one
* written after the reader, one inside a subshell the reader is not in (a `( … )` group that closed before it, a
* pipeline member that is not the last, a background job — a substitution body is a text of its own) and one in a
* function body is NOT a binding for it. A function body runs at its call: a call the plan can see (a program word
* naming one of the text's functions, a code body a builtin runs later, which may name one) makes every name a body
* binds unknown from that segment on ({@link functionBound}); a text the plan does not read (`eval`, `source`, a
* computed program word) already makes every name unknown. The last member of a pipeline may or may not run in this
* shell: its bindings join the values before them. A body's own readers follow its own order; a reader in another body
* loses a name any other body binds ({@link Unreadable}). A name the body declares with a plain `local` (before the
* binding, where the declaration certainly ran) never leaves it.
*
* An attribute ({@link NameAttribute}) and a name reference hold for every segment they may RUN before — a later one,
* one in a function body, one in the same loop body: an assignment there binds something other than its text.
*/
export declare class BindingThread {
private readonly segments;
/** What each segment binds, its names alone (a value is the reader's to read). */
private readonly named;
/** The state a name starts the text in (and is reset to by a function body): the declared home for `HOME`. */
private readonly initial;
/** Names any segment of the text binds (a function body run later reads a `HOME` bound anywhere as unknown). */
private readonly boundAnywhere;
/** Each loop head's segment and its matching `done`'s. */
private readonly loops;
/** `HOME` may already hold something other than the declared home where the text starts (a substitution body
* inside a text that bound it). */
private readonly homeStartsTouched;
/** The functions the shell that runs this text (a literal `eval` text) already holds. */
private readonly inheritedFunctions?;
private readonly entries;
/** The `local` declarations the walk applied as certain ({@link apply}), each where it stands. */
private readonly localDeclarations;
/** The attributes the text gives ({@link BindingEffect.attributes}), and where — read off its names alone, before any
* reader: one given later in the text may run first. */
private readonly attributesAt;
/** Names no assignment makes readable again ({@link Unreadable}). */
private readonly unreadable;
/** The block nesting each segment moves ({@link blockStructure}). */
private readonly blocks;
/** Segments that END in a pipe or a `&`: the command that closes them is a pipeline member that is not the last, or a
* background job — a subshell of its own ({@link ownSubshellAt}: which of the segment's bindings that is). */
private readonly ownSubshell;
/** The floors the names start the text with ({@link InheritedNames.floors}). */
private readonly startFloors;
/** The names rebound where the text starts ({@link InheritedNames.rebound}). */
private readonly startRebound;
/** Where a function body runs ({@link runsBodies}): the last segment that can call one, and the command environment of each call. */
private bodyRuns;
constructor(segments: readonly LexedSegment[],
/** What each segment binds, its names alone (a value is the reader's to read). */
named: readonly BindingEffect[],
/** The state a name starts the text in (and is reset to by a function body): the declared home for `HOME`. */
initial: (name: string) => NameState,
/** Names any segment of the text binds (a function body run later reads a `HOME` bound anywhere as unknown). */
boundAnywhere: (name: string) => boolean,
/** Each loop head's segment and its matching `done`'s. */
loops: ReadonlyMap<number, number>,
/** `HOME` may already hold something other than the declared home where the text starts (a substitution body
* inside a text that bound it). */
homeStartsTouched?: boolean,
/** What holds where the text starts: given before its first segment. */
inherited?: InheritedNames,
/** The functions the shell that runs this text (a literal `eval` text) already holds. */
inheritedFunctions?: InheritedFunctions | undefined);
/** What a text nested at segment `j` starts with ({@link InheritedNames}) — `j` absent: a text run later, which every
* segment may run before (in whatever shell: a later text may run inside a subshell or a body, so every change
* counts). */
inheritedAt(j?: number): InheritedNames;
/** What a name holds where the text starts: a function name unbound, a builtin enabled (what the shell around an `eval` text, a
* substitution body or a text run later holds, if it says); a variable as the plan says. */
private startOf;
/** Does any segment of this text bind the name (or every name)? */
private bindsInText;
/** What a text this one runs at some later point (a `trap` action) finds each function and builtin key: the point is not ordered
* against the segments (it may fire after any of them, inside any body or subshell), so a key holds what the text starts it with
* and every value any segment binds it ({@link InheritedFunctions}). */
heldAnywhere(): InheritedFunctions;
/** Tell the thread where function bodies run: `runs[si]` — segment `si` may run one (a call, a text the plan does not
* read); `atEnd` — a code body may run at the text's end or at any point (`trap`); `environment[si]` — what a call's own
* command environment sets. A call inside a loop may run again after a later segment of the loop. The last run point
* bounds what can precede a body: a binding after it never does. A name a body that makes a call binds — `local` too:
* the shell's scope is dynamic, the callee runs inside it — is unreadable to a reader in any other body. */
runsBodies(runs: readonly boolean[], atEnd: boolean, environment: readonly (ReadonlySet<string> | "all" | undefined)[]): void;
/** The names a reader that runs LATER than it stands may find rebound by segments after it: a reader in a function body
* runs at each call, so every segment up to the last run point (and a call's own command environment) may run before it;
* a text handed to run later (`j` absent) at any point. A reader that runs where it stands has none: the effects the walk
* has applied so far are the ones that precede it. A `local` counts like any binding: it shortens a name's life, not the
* view of a reader that runs inside its extent (a body called from there, a `trap` text it sets). */
private boundLater;
/** Does a binding segment `at` makes, standing at `standing`, run in a subshell of its own — a pipeline member that is
* not the last, a background job? A segment's closing pipe or `&` ends ITS LAST command, at the depth that command
* stands: a binding that stands shallower (a function definition's head, in front of the block its body opens in the
* same segment: `f(){ ls | head; … }`) is not that member — the pipe closes a command inside the body, and the
* definition is the shell's own. */
private ownSubshellAt;
/** Is a binding segment `at` makes one no later reader may count on as certain: made in a pipeline member (it follows a
* `|`, or it is {@link ownSubshellAt}) or a background job? */
private detachedAt;
/** Can a binding segment `at` makes exist for a reader at segment `j` (`j` absent: a reader at the text's end, outside
* every body and subshell)? Not where `at` stands in a function body the reader is not in (the body runs at its call),
* runs in a subshell of its own (a pipeline member that is not the last, a background job), or stands in a `( … )`
* group the reader is not in. */
private visible;
/** Where the binding `name` of segment `at` is made: the segment's own place, or — a function definition — where its head
* stands. */
private standingOf;
/** The names any of the text's function bodies binds — what a call to one of them may change — or `all` where a body
* may bind any name. A name the body keeps `local` ({@link leavesBody}) stays in it. */
functionBound(): ReadonlySet<string> | "all";
/** The function body a binding segment `at` makes belongs to, where the name can outlive it: the body it stands in — a
* plain `local NAME` is the body's own and never leaves it (an optioned declaration, `declare -g` / `local -p`, reads no
* scope, so it can). What a reader in ANOTHER body loses ({@link unreadableBy}); a call to the body changes it unless a
* declaration the body certainly ran covers the name ({@link functionBound}). */
private bodyBinding;
/** Does segment `at` stand in a function body (it runs at a call, not here)? */
inFunctionBody(at: number): boolean;
/** Does segment `at` run in a subshell (a `( … )` group, a pipeline member that is not the last, a background job) or a
* function body — whatever it changes, the shell that runs the next segment may not see? */
runsApart(at: number): boolean;
/** Segment `at` hands this shell a body it runs at ANY later point (a `trap` action, an alias value): every name the
* body may bind (`all`: any) is unreadable to every reader after `at`, whatever is assigned to it in between. */
runsLater(at: number, names: ReadonlySet<string> | "all"): void;
/** What segment `at` makes unreadable: in a function body, the names it binds (a plain `local` stays in it) — every name
* where it may bind any; a name reference, every name. Read before any segment is applied, so it does not ask which
* declarations certainly ran: a name a body assigns after its own `local` is lost to a reader in another body all the same
* (more questions, never fewer). */
private unreadableBy;
/** May segment `x` run before segment `e` runs (`x` being `e`: before a later run of it): `x` comes first in the text,
* `e` stands in a function body (the body runs at its call, after any segment, and may run again), or both stand in
* one loop's body (a later beat runs `x` again first). */
private mayRunBefore;
/** Did the body certainly declare this name `local` before segment `at`: an earlier segment of the body, run on its own
* (no `&&` / `||`, no pipeline member or background job), in a block still open at `at`, whose declaration the walk
* APPLIED as certain — the shell ran its word as the builtin: no function of that name standing, no launcher, path or
* redirection in the way, and the builtin enabled where the body runs (what {@link bindingEffectOf} concluded, the ordered
* state of {@link BindingTable.functionState} and {@link BindingTable.builtinState}; recorded by {@link apply}). */
private declaredLocal;
/** Does the block that holds a binding's words stay open (no closing word, no switch of branch) through `j`? */
private heldThrough;
/** Apply segment `at`'s effect. `andRun`: the segment was reached through `&&` / `||`. `loopEnd`: for a loop variable,
* the index of the loop's end. */
apply(at: number, effect: BindingEffect, andRun: boolean, loopEnd?: number): void;
/** Does a reader at segment `j` lose the name `u` makes unreadable: past a name reference it may run after, or in a
* function body other than the one that binds it? */
private lostAt;
/** Make these names unknown from segment `at` on (a loop body that binds them may have run before any of its beats; a
* call runs a body that binds them). */
kill(at: number, names: Iterable<string> | "all"): void;
/** The table a reader at segment `j` sees; `andRunStart` = the first segment of `j`'s `&&` run; `where` = what the
* working-directory thread says `PWD` / `OLDPWD` hold there (absent: it does not say — both unknown). */
tableAt(j: number, andRunStart: number, where?: WorkingDirectoryState): BindingTable;
}
/** A segment that opens with `((`: an arithmetic command (or a subshell in a subshell — the tokens are the same). */
export declare function isArithmeticCommand(segment: LexedSegment): boolean;
/** The `[[` operators whose operands the arithmetic evaluator reads (`[[ x -eq y ]]`; `[` / `test` compare integers
* without evaluating them). */
export declare const ARITHMETIC_TEST_OPERATORS: ReadonlySet<string>;
/** The substitutions a parameter expansion's body hands the arithmetic evaluator: its subscript (`${a[…]}`) and the
* offset and length of a substring expansion (`${x:off:len}` — the shell expands them, then evaluates them). */
export declare function parameterBodySubstitutions(name: string, body: string, table: BindingTable | undefined): string[];
/**
* The substitutions the shell's ARITHMETIC evaluator runs while it evaluates `text`: every `$(…)`, backquote or process
* substitution inside an array SUBSCRIPT of it (`a[$(…)]` — quoted or not: the evaluator re-reads the text it is
* handed), and — for each name the text refers to whose value the table holds — the same of that value (the evaluator
* evaluates a variable's value as arithmetic too, through any chain of names). A name the table does not settle
* contributes the literal values its bindings left (an array's elements) and nothing else: any other value is data the
* text does not hold. A name already on the chain is a cycle (bash refuses
* it). Each body is shell text the evaluator re-enters; past the reader's budget of values the text itself is handed
* back as a body, so the segment re-enters text it did not follow (unestablished, never silent).
*/
export declare function arithmeticSubstitutions(text: string, table: BindingTable | undefined, chain?: ReadonlySet<string>, budget?: {
left: number;
}): string[];
/** The text of every `[…]` subscript in an arithmetic text (nested brackets balanced; an unclosed one runs to the end). */
export declare function subscriptsOf(text: string): string[];