Documentation
¶
Overview ¶
Package script turns a .rysh file — a bash script whose statement-position "##" lines are rysh commands — into plain bash (design 021).
The whole design rests on one property of bash: "#" opens a comment at the start of any WORD, not just a line. That means a file full of "##pane info" lines is already a valid bash script in which those lines do nothing, so the same file can be read by bash and by rysh. It also means "##" can only ever appear in statement position: `X=$(##pane info)` is not a comment, it is an unterminated `$(` — a hard syntax error. So there is no expression form, and capture happens through $RYSH_OUT instead (see Prelude).
Transpiling to bash rather than interpreting the file ourselves is what keeps the language honest. Every construct bash has — loops, conditionals, functions, quoting, heredocs, pipes, `set -e`, traps, `$?` — keeps working because bash is still the thing running the script. An interpreter of our own would have to re-implement all of it, and would be subtly wrong about heredocs and multi-line constructs forever.
Index ¶
Constants ¶
const PromptCommand = "prompt"
PromptCommand is the one command word that is a SCRIPT builtin rather than an entry in the daemon's ## dispatch table.
It cannot be a table command: running it means waiting for an agentic turn to finish, and the table's handlers run inside the WorkspaceActor, where blocking would freeze the whole workspace. So `##prompt refactor the parser` compiles to a call to `rysh prompt`, which watches the pane's phase events from outside the actor and blocks there instead.
Spelling it "##prompt" rather than making scripts call `rysh prompt` directly is what keeps the polyglot property: a "##" line is a comment to bash, so the file still parses and still runs with the rysh work skipped. A bare `rysh prompt ...` line would be a command plain bash would try — and fail — to run.
const PromptFunc = "__rysh_prompt"
PromptFunc is the shell function `##prompt <text>` is rewritten into.
const RyshFunc = "__rysh"
RyshFunc is the shell function a rysh line is rewritten into. It is defined by Prelude.
const Version = "1"
Version is exported to scripts as $RYSH_SCRIPT_VERSION so a script can detect what contract it is running under. Bump it when the prelude's observable behaviour changes.
Variables ¶
This section is empty.
Functions ¶
func Prelude ¶
Prelude renders the bash that defines RyshFunc and then sources the transpiled body.
It is a SEPARATE file that sources the script rather than a header prepended to it, and that is load-bearing: prepending would shift every line number, so a bash syntax error or `set -x` trace would name a line the author never wrote. Sourcing resets $LINENO and $BASH_SOURCE to the sourced file, so diagnostics read exactly as if bash had run the .rysh directly. Verified: a syntax error on line 5 of the body reports "<name>.rysh: line 5".
The __rysh body has one subtlety worth the comment it carries: `local x=$(…)` returns local's status, not the command's. Written that way, every exit code in the language would silently be 0 — the single most destructive bug this file could have — so the declaration and the assignment are separate statements.
Types ¶
type Result ¶
type Result struct {
// Bash is the transpiled source. It has exactly as many lines as the input
// (see the package doc on line fidelity).
Bash string
// RyshLines are the 1-based source line numbers that were rewritten.
RyshLines []int
}
Result is a transpiled script.
func Transpile ¶
Transpile rewrites the "##" lines of src into calls to RyshFunc.
Line fidelity is an invariant, not an aspiration: every source line produces exactly one output line, so bash's own diagnostics, `set -x` traces and $LINENO all point at the real line of the .rysh file. Nothing may be inserted — which is why the prelude is a separate file that sources this one rather than a header prepended to it.