UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

129 lines (128 loc) • 10.9 kB
/** * 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;