Files
lush_grading/lush_reference.md
Cormac Shannon be8d657b24 Initial commit: Lush vs Bash AI benchmarking framework
Benchmark harness that uses LLM agents to solve shell scripting tasks
in both Bash and Lush, then compares correctness and code quality.

- CLI with run, run-all, list-tasks, report, and export commands
- Agent loop with retry support via Anthropic Claude provider
- Test harness executing solutions in sandboxed subprocesses
- LLM-driven questionnaire for subjective code quality evaluation
- HTML report export with charts (matplotlib)
- 8 Category A tasks (write-from-scratch in both languages)
- 4 Category B tasks (verify provided Bash, convert to Lush)
- Lush language reference for agent context
2026-03-29 17:56:30 +01:00

247 lines
5.7 KiB
Markdown

# Lush
Lush is a shell built on [Lua 5.5.0](https://www.lua.org/). It extends Lua with shell-oriented features — backtick commands, pipes, globbing, interpolation, environment variables — while remaining a fully compatible Lua 5.5 interpreter.
Based on [Lua](https://www.lua.org/) by R. Ierusalimschy, L. H. de Figueiredo, W. Celes (PUC-Rio).
## Quick Start
Build and run:
```sh
make
./lush
```
## Language Guide
Lush is Lua with shell extensions. Every valid Lua program runs unchanged. The additions are described below.
### Backtick Commands
Execute shell commands with backticks. Each command runs in its own child process. Stdout and stderr are captured and returned as a table with `code`, `stdout`, and `stderr` fields.
```lua
local r = `echo hello`
print(r.stdout) -- "hello\n"
print(r.code) -- 0
local r = `ls nonexistent`
print(r.code) -- non-zero
print(r.stderr) -- error message
```
Empty backticks return `{code=0, stdout="", stderr=""}`. A command killed by a signal returns exit code 128 + signal number (e.g. 137 for SIGKILL).
### Interactive Commands
Prefix a line with `!` to run a command in a child process with stdout and stderr inherited from the terminal. The result is stored in `_`.
```lua
!ls -la
print(_.code) -- 0
!sh -c "exit 42"
print(_.code) -- 42
```
Works anywhere a statement is valid — inside functions, loops, if-blocks.
### Pipes
Use `|` to connect commands in a pipeline. Each stage runs in its own process with stdout piped to the next stage's stdin.
```lua
local r = `echo hello | tr a-z A-Z`
print(r.stdout) -- "HELLO\n"
!ps aux | grep lush | head -5
```
The exit code comes from the last stage. Stderr from the last stage is captured (in backtick mode); stderr from middle stages goes to the terminal. Quoting (`"a|b"`, `'a|b'`, `a\|b`) treats `|` as literal.
### String Interpolation
Three forms of interpolation inside backtick and interactive commands:
`**${expr}` — Lua expression:**
```lua
local name = "world"
`echo ${name}` -- hello world
`echo ${40 + 2}` -- 42
`echo ${name:upper()}` -- WORLD
```
`**$NAME` — Environment variable:**
```lua
`echo $HOME`
`echo $USER`
```
`**$(cmd)` — Subcommand substitution:**
```lua
`echo today is $(date +%Y-%m-%d)`
`echo $(echo $(echo nested))` -- nested substitution
```
Escape with backslash: `\$HOME` produces the literal string `$HOME`.
### Environment Variables
Read and write environment variables directly from Lua using `$NAME` syntax:
```lua
-- Read
local home = $HOME
local path = $PATH
-- Write (visible to child processes)
$MY_VAR = "hello"
`echo $MY_VAR` -- hello
-- Unset
$MY_VAR = nil
-- Numbers are coerced to strings
$PORT = 8080
```
### Globbing
Shell glob patterns are expanded inside backtick commands:
```lua
`echo *.lua` -- expands to matching files
`echo src/**/*.c`
`echo file?.txt` -- single-character wildcard
`echo config.[ch]` -- bracket patterns
```
Brace expansion:
```lua
`echo {a,b,c}` -- a b c
`echo file.{lua,c,h}` -- file.lua file.c file.h
```
Tilde expansion:
```lua
`echo ~` -- /home/user
`echo ~/projects` -- /home/user/projects
```
Quoting suppresses expansion: `"*.lua"`, `'*.lua'`, and `\*.lua` are all literal.
### Quoting
Single quotes are literal (no escapes):
```lua
`echo 'hello world'`
`echo 'no $expansion here'`
```
Double quotes allow escapes and interpolation:
```lua
`echo "hello world"`
`echo "tab:\there"`
`echo "path is $HOME"`
`echo "sum is ${1 + 2}"`
```
Backslash outside quotes escapes the next character:
```lua
`echo hello\ world` -- single argument: hello world
```
### Builtins
Built-in commands that affect the shell process:
```lua
`cd /tmp` -- change directory
`cd -` -- previous directory
`cd` -- home directory
`umask` -- query current umask
`umask 0077` -- set umask
```
### The `lush` Library
Programmatic API for shell operations:
```lua
lush.capture("echo hello") -- like backticks, returns {code, stdout, stderr}
lush.run("echo hello") -- returns stdout with trailing newline stripped
lush.interactive("ls") -- like !, returns result and sets _
lush.envget("HOME") -- read env var
lush.envset("MY_VAR", "value") -- write env var
```
### Extensibility
**Custom commands** run in a child process (like any external command), so their output is captured and they support piping:
```lua
lush.commands.greet = function(name, ...)
print("hello " .. (({...})[1] or "world"))
end
`greet lush` -- hello lush
`greet lush | tr a-z A-Z` -- HELLO LUSH
```
**Custom builtins** run in the current Lua process, so they can modify shell state (like `cd` does). They receive the raw command table and return a result table:
```lua
lush.builtins.hello = function(cmd, name)
return {code = 0, stdout = "hi " .. (name or "") .. "\n", stderr = ""}
end
`hello world` -- hi world
```
**Aliases:**
```lua
lush.aliases.ll = "ls -la"
`ll` -- runs: ls -la
lush.aliases.ll = nil -- remove alias
```
### Configuration
Lush loads config files on interactive startup (suppressed with `-E`):
- `$XDG_CONFIG_HOME/lush/config.lua` — main config
- `$XDG_CONFIG_HOME/lush/config.d/*.lua` — additional configs, loaded alphabetically
Use config files to set up aliases, custom commands, prompts, and environment.
### Prompt
Customize the interactive prompt:
```lua
-- In config.lua:
_PROMPT = "lush> "
_PROMPT2 = "...> "
-- Dynamic prompt:
_PROMPT = setmetatable({}, {
__tostring = function()
return os.date("%H:%M") .. " > "
end
})
```