@sema-agent/core
Version:
Stateless, task-oriented AI agent core
129 lines (128 loc) • 10.9 kB
TypeScript
/**
* The candidate readers' VIEW of a word — what the read plan names a candidate by, read off the one lexer's facts.
* Every reader of `shell-read-plan.ts` walks these, never a raw or folded string of its own: the two spellings
* (the home-directory word spelled with the declared home, as bash hands it to the program), and what the shell
* does to the word before the program sees it.
*/
import { type LexedCommand, type LexedWord, type WordPiece } from "../../shell/lex.js";
import { type BindingTable, type BoundValue, type FloorValue } from "./shell-read-bindings.js";
/** `"glob"` = an unquoted pathname-expansion character; `"unresolvable"` = the shell rewrites the word (a brace,
* variable or command substitution) so its text is not the path. The read candidate's own field, spelled here. */
export type WordExpands = false | "glob" | "unresolvable";
/** A word as the candidate readers name it: the lexer's word, the spelling a reader names it by — the names the word
* refers to read through the binding table ({@link wordValues}: `"$F"` after `F=/x` is `/x`; {@link spellingOf}) — and
* what the shell does to it before the program sees it. A text handed to a shell that re-parses it is the program's
* ({@link programTextOf}), never this one. */
export interface Read {
readonly word: LexedWord;
readonly raw: string;
readonly folded: string;
/** A pattern's names are matched by this spelling where it is not {@link folded} (a class the judge widens, read as
* any one character — {@link LexedWord.judged}, {@link judgedSpellingOf}); `folded` stays the name opened where none match. */
readonly pattern?: string;
readonly expands: WordExpands;
/** The names the word refers to were read through the table: its expansion IS the spelling above (and, where the
* names may hold several values, {@link values}). */
readonly settled?: true;
/** Every value the word may expand to (the first spelled {@link folded}, {@link spellingOf}; matched by {@link pattern}),
* each with the parts of it the shell expands as a pattern ({@link BoundValue}); present wherever the names were read
* through the table ({@link settled}). */
readonly values?: readonly BoundValue[];
/** The word refers to `HOME` and `HOME` may hold something other than the declared home here: the spelling the
* word has with the declared home, for the deny judge alone. */
readonly homeFloor?: string;
/** The word's names are unknown here, but their bindings left spellings for the deny judge (an array's elements):
* what the word may then spell, for the deny judge alone. */
readonly floorValues?: readonly FloorValue[];
}
/** The reads of a segment's words against the binding table at that segment (`homeDir` = the declared home, the
* state `HOME` starts in). A word that refers to no name, or only to a `HOME` the text has not touched (`~/x`, placed
* by the establishment at the declared home), reads as written. */
export declare function readsOf(words: readonly LexedWord[], table: BindingTable | undefined, homeDir: string | undefined, split?: boolean): Read[];
/** One reading of a segment: the binding table with each name its words read held to ONE value (or, past
* {@link READINGS_MAX}, made unknown — every value it held stays a deny-floor spelling, {@link BindingTable.floorOf}),
* the reads of its argv against it ({@link readsOf}), and the argv the shell hands the program in it — a word whose
* names settle into one value that is no pattern is that value, as a literal word (a reader of the program position,
* of a launcher's fields, of a program's options reads it as it reads a literal word); any other word as written, its
* names' deny floors in `floors` (a shell that re-reads the word — `sh -c`, `eval`, a launcher's shell — starts with
* them: the word names in that text what it names here). */
export interface SegmentReading {
readonly table: BindingTable;
readonly reads: readonly Read[];
readonly argv: readonly LexedWord[];
readonly floors: ReadonlyMap<string, readonly FloorValue[]>;
}
/**
* The readings of one segment's words (its argv and its redirection targets): the names they read through the table that
* may hold several values here, in the order the words read them (the program position first), each held to one value
* per reading — every combination while the readings stay within {@link READINGS_MAX}, so the program position, a
* launcher's fields, a re-entered text and an operand are spelled from the same values (`$P` read twice takes one value
* in a reading: the shell expands both alike); a name past that bound is unknown in every reading — its word as written,
* every value it held a deny floor — never a sample of its combinations. A segment whose names hold one value each has
* one reading.
*/
export declare function segmentReadingsOf(argv: readonly LexedWord[], targets: readonly LexedWord[], table: BindingTable, homeDir: string | undefined): SegmentReading[];
/** The spelling a reader names a value by, read directly as a path — for a pattern, the name the program opens where it
* matches nothing: its text, a leading `~` spelled as the literal tilde it is ({@link literalTildeSpelling}, the lexer's
* spelling of a written one) — the shell never tilde-expands what a quoted piece or an expansion produced, so a value that
* starts with `~` starts with a literal one (`X=/w; rm \~$X/*` reads `./~/w/*`, as `rm \~/w/*` does). A shell that
* re-parses the value reads {@link programTextOf} instead. */
export declare const spellingOf: (v: BoundValue) => string;
/** The pattern a PART of a pattern word is matched by (an option value attached to its option word, `--file=["!"a]*`; an
* assignment-shaped word's value, `if=["!"a]*`): the word's own judged spelling (`pattern`) past the text in front of
* the part — bash expands the WHOLE word, so the names the part may take are those the word's widened spelling reaches
* (a superset: a name is judged whether or not the word in front of it matches too). `undefined` where the word carries
* none, the part is not the end of the word's spelling, or the text in front holds a widened class itself (its judged
* spelling no longer lines up). */
export declare function partPattern(word: {
readonly folded: string;
readonly pattern?: string;
}, part: string): string | undefined;
/** A candidate under another name (a value of several, a backup, a copy's child): the word's `pattern` matched the word,
* not that name — the name is matched by its own text. */
export declare function renamed<C extends {
readonly spelled: string;
readonly pattern?: string;
}>(c: C, spelled: string): Omit<C, "pattern">;
/** The text a read's word hands a program, as a shell re-parsing it reads it (`eval`, `sh -c`, a launcher's line): its
* first established value as the program receives it, or the lexer's folded spelling — never {@link spellingOf}'s path
* spelling (`X='~'; eval rm -rf "$X"` re-parses `rm -rf ~`, the home). */
export declare const programTextOf: (r: Read) => string;
/** What a read's word hands the program, where the reader knows it: each value its names settled into — one field each
* (an unquoted expansion whose value is not one word is never settled, {@link wordValues}), or, where the word is a
* pattern, the pattern whose matches are the fields, its parts telling pattern characters from literal ones;
* `undefined` for a word the table did not settle (or that names nothing). */
export declare function settledFieldsOf(r: Read): readonly BoundValue[] | undefined;
/** The shell REWRITES this read's word before the program sees it (a parameter, command, process, arithmetic or
* brace expansion, or the character of one) — a word whose names the table settled excepted: its expansion is its
* spelling. */
export declare const rewritten: (r: Read) => boolean;
/** The reading of a text that came out of a word (a sed script's file operand): one shell word, or unresolvable. */
export declare function readOfText(text: string): Read;
/** A home-rooted candidate spelled with `home`: a leading `~` / `~/` — the shell's expansion, or the literal-tilde
* spelling `./~` of one it leaves alone (quoted, escaped, after an option's `=`), which the program it reaches may
* expand itself — or a `$HOME` / `${HOME}` word as {@link homeExpansionOf} reads it, read off the VALUE's pieces when
* the word is assignment-shaped (`A="$HOME/x"`, `dd if="$HOME/x"`: the candidate is the value, quotes kept);
* `undefined` for any other (`~user`, a word that is no home word). */
export declare function spelledWithHome(spelled: string, raw: string, home: string): string | undefined;
/** Why a lexed text's reads cannot all be named — the plan's `unreadable` floor, or `undefined`: the first word whose
* value the text does not contain (an unresolved word), or an unquoted extended-glob opener anywhere a word, a
* redirection target or a here-document delimiter stands (`pack@(age.json)`, `<<@(x)`). After `shopt -s extglob`
* bash expands the pattern (or reads another delimiter) where the lexer reads a split word, so what the text opens is
* not what the plan names: the whole text asks, and a deny hit anywhere still refuses. `globs: false` = the text is an
* arithmetic expression (`$((2*(3+4)))`), which bash evaluates and never globs. A compound assignment's value is no
* such word: the assignment reads nothing. */
export declare function unreadableSentenceOf(lexed: LexedCommand, text: string, globs?: boolean): string | undefined;
/** An `$((…))` body the shell reads as ARITHMETIC: one parenthesised group spanning the whole body (`(2*(3+4))`),
* its bounds read off the lexer's structure — a quoted, escaped or commented parenthesis is text, not a bound — across
* the segments the lexer cuts at `&&` `||` `&` `|` and line breaks (operators arithmetic shares with the shell).
* `$((cat x); :)` opens the same way but its first group closes early: a command substitution holding a subshell. */
export declare function isArithmeticBody(body: string): boolean;
/** What the shell does to a word (or an assignment's value) before the program sees it, read off its pieces: the
* lexer's expansion facts alone. A quoted text that an evaluator or an interpreter later RUNS (an arithmetic
* subscript, an inline program) is no rewrite of the word: the slot it stands in says what it is (the program read
* model's role for it, and the arithmetic rule of `shell-read-bindings.ts`). A substitution is an expansion PIECE
* (command / process / arithmetic), never the placeholder text it leaves in the folded spelling. */
export declare function expandsOf(w: {
readonly pieces: readonly WordPiece[];
}): WordExpands;