- #25: $VAR expansion in commands - #26: Shell abbreviations and user-extensible builtins - #27: $(cmd) subcommand syntax - #22: Updated with implementation details (in progress) - #7: Standardize status label
This commit is contained in:
@@ -1,21 +1,74 @@
|
||||
# 22 — Implicit interactive commands (drop `!` prefix)
|
||||
|
||||
**Status:** open
|
||||
**Status:** in progress
|
||||
|
||||
In the REPL, maybe we can get away with not needing the `!` prefix.
|
||||
## Goal
|
||||
|
||||
Lua already attempts to run input as a statement. If that fails, it assumes it might be an expression (e.g. `1 + 2`) and wraps it in `return(...)`. If *that* also fails, we could try wrapping it with `!` prefix semantics as a third fallback.
|
||||
In the REPL, treat unrecognized input as shell commands so users can type `git status` or `ls -la` without the `!` prefix.
|
||||
|
||||
## Execution order
|
||||
## Implementation
|
||||
|
||||
1. Try as Lua statement
|
||||
2. Try as Lua expression (`return ...`)
|
||||
3. Try as shell command (interactive execution)
|
||||
### REPL fallback chain (`loadline()` in `lua.c`)
|
||||
|
||||
## Considerations
|
||||
The standard Lua REPL tries two compilations. We add a third:
|
||||
|
||||
- Ambiguity: `ls` is not valid Lua, so it would fall through to shell — this is the desired behaviour
|
||||
- But `print` is valid Lua (it's a value) — so `print` alone wouldn't trigger shell
|
||||
- What about `git status`? Not valid Lua, would correctly fall through to shell
|
||||
- Error messages: if all three fail, which error do we show?
|
||||
- Performance: three parse attempts per input line
|
||||
1. Try as expression (`return <line>`)
|
||||
2. Try as statement (with continuation for incomplete input)
|
||||
3. **Try as shell command (`!<line>`)**
|
||||
|
||||
This is done by adding `addshellcmd()` which prepends `!` to the line and compiles it, triggering the existing lexer/parser path for interactive commands.
|
||||
|
||||
### The bare identifier problem
|
||||
|
||||
Multi-word input like `git status` fails both Lua paths and correctly falls through to shell. But single-word commands like `ls` compile as `return ls;` — a valid Lua expression that returns `nil` — so they never reach the shell fallback.
|
||||
|
||||
Worse: if `addreturn` is bypassed, `ls` as a statement is *incomplete* Lua (the parser expects `ls(...)` or `ls = ...`), so `multiline()` enters continuation mode and the REPL hangs waiting for more input.
|
||||
|
||||
### Fix: check `_G` before the expression path
|
||||
|
||||
Before trying `return <line>`, check if the line is a bare identifier (single word matching `[a-zA-Z_][a-zA-Z0-9_]*`). If it is, look it up in `_G`:
|
||||
|
||||
- **In `_G`** → proceed normally (`return print` shows the function, `return x` shows its value)
|
||||
- **Not in `_G`** → skip expression and statement paths entirely, go straight to shell
|
||||
|
||||
This is a compile-time check — no runtime error interception, no flags, no special subroutines. ~10 lines of C in `loadline()`:
|
||||
|
||||
```c
|
||||
line = lua_tostring(L, 1);
|
||||
bare = isbareid(line);
|
||||
if (bare && lua_getglobal(L, line) == LUA_TNIL) {
|
||||
lua_pop(L, 1);
|
||||
status = addshellcmd(L); /* straight to shell */
|
||||
}
|
||||
else {
|
||||
if (bare) lua_pop(L, 1);
|
||||
/* normal expression → statement → shell fallback chain */
|
||||
}
|
||||
```
|
||||
|
||||
### Summary of behavior
|
||||
|
||||
| Input | Path taken | Result |
|
||||
|-------|-----------|--------|
|
||||
| `print("hi")` | expression | Lua expression |
|
||||
| `x = 42` | statement | Lua statement |
|
||||
| `if true then print("x") end` | statement | Lua statement |
|
||||
| `print` | expression (in `_G`) | shows function value |
|
||||
| `x` (after `x=42`) | expression (in `_G`) | shows 42 |
|
||||
| `git status` | expression fails → statement fails → shell | runs git |
|
||||
| `ls -la` | expression fails → statement fails → shell | runs ls |
|
||||
| `echo hello` | expression fails → statement fails → shell | runs echo |
|
||||
| `ls` | bare id, not in `_G` → shell | runs ls |
|
||||
| `!pwd` | expression (already valid `!` syntax) | runs pwd |
|
||||
|
||||
### Edge cases
|
||||
|
||||
- A global explicitly set to `nil` (`x = nil`) would still try the expression path — `return x` compiles and returns nil. This is correct: the user defined it, Lua should handle it.
|
||||
- Lua keywords (`if`, `for`, `while`) are not valid identifiers in `isbareid` terms — they fail `addreturn`, enter `multiline` for continuation, and behave normally.
|
||||
- `!cmd` still works — it compiles as a valid expression in the first path.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `lua.c` | `isbareid()`, `addshellcmd()`, modified `loadline()` |
|
||||
|
||||
Reference in New Issue
Block a user