Refactor REPL expression handling to improve syntax error reporting and execution flow
This commit is contained in:
156
README.md
156
README.md
@@ -110,144 +110,20 @@ Explore organized examples that demonstrate Luash features:
|
|||||||
|
|
||||||
See [examples/README.md](examples/README.md) for the complete learning path.
|
See [examples/README.md](examples/README.md) for the complete learning path.
|
||||||
|
|
||||||
### What You Get
|
### Built-ins
|
||||||
|
|
||||||
#### File System Operations
|
Luash intentionally stays minimal. The injected helpers are:
|
||||||
```lua
|
|
||||||
-- Check file existence
|
|
||||||
if fs.exists("config.txt") then
|
|
||||||
content = fs.read("config.txt")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- File operations
|
|
||||||
fs.write("output.txt", "Hello World")
|
|
||||||
fs.append("log.txt", "New entry\n")
|
|
||||||
|
|
||||||
-- Advanced file operations
|
|
||||||
file.copy("source.txt", "backup.txt")
|
|
||||||
file.move("old.txt", "new.txt")
|
|
||||||
lines = file.lines("data.txt") -- Read as array of lines
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Directory Operations
|
|
||||||
```lua
|
|
||||||
-- Directory management
|
|
||||||
dir.create("new_folder") -- mkdir -p equivalent
|
|
||||||
files = dir.list("/tmp") -- List directory contents
|
|
||||||
current = dir.pwd() -- Get current directory
|
|
||||||
|
|
||||||
-- Convenience aliases
|
|
||||||
mkdir("test")
|
|
||||||
cd("/home/user")
|
|
||||||
ls("/var/log")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### String Utilities
|
|
||||||
```lua
|
|
||||||
-- String manipulation
|
|
||||||
parts = str.split("a,b,c", ",")
|
|
||||||
clean = str.trim(" whitespace ")
|
|
||||||
bool = str.starts_with("hello", "hel")
|
|
||||||
|
|
||||||
-- Text processing
|
|
||||||
result = text.grep("pattern", "file.txt")
|
|
||||||
lines = text.head(10, "large_file.txt")
|
|
||||||
count = text.wc("-l", "data.txt") -- Line count
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Array/Table Operations
|
|
||||||
```lua
|
|
||||||
-- Array utilities
|
|
||||||
found = array.contains({"a", "b", "c"}, "b")
|
|
||||||
doubled = array.map({1, 2, 3}, function(x) return x * 2 end)
|
|
||||||
evens = array.filter({1, 2, 3, 4}, function(x) return x % 2 == 0 end)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Path Utilities
|
|
||||||
```lua
|
|
||||||
-- Path manipulation
|
|
||||||
full_path = path.join("/usr", "local", "bin", "luash")
|
|
||||||
dir_name = path.dirname("/path/to/file.txt") -- "/path/to"
|
|
||||||
base_name = path.basename("/path/to/file.txt") -- "file.txt"
|
|
||||||
extension = path.ext("document.pdf") -- "pdf"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### System Information
|
|
||||||
```lua
|
|
||||||
-- System utilities
|
|
||||||
os_name = sys.os() -- "linux", "darwin", etc.
|
|
||||||
arch = sys.arch() -- "x86_64", "arm64", etc.
|
|
||||||
hostname = sys.hostname() -- Get machine name
|
|
||||||
user = sys.whoami() -- Current username
|
|
||||||
memory_info = sys.memory() -- Memory usage
|
|
||||||
disk_info = sys.disk("/") -- Disk usage
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Network Operations
|
|
||||||
```lua
|
|
||||||
-- Network utilities
|
|
||||||
success = net.download("https://example.com/file.txt", "local_file.txt")
|
|
||||||
response = net.get("https://api.example.com/data")
|
|
||||||
post_result = net.post("https://api.example.com/submit", "key=value")
|
|
||||||
reachable = net.ping("google.com", 3) -- Ping 3 times
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Process Management
|
|
||||||
```lua
|
|
||||||
-- Process operations
|
|
||||||
my_pid = proc.pid()
|
|
||||||
git_path = proc.which("git") -- Find executable path
|
|
||||||
|
|
||||||
-- Job control
|
|
||||||
job.background("long_running_command")
|
|
||||||
job.kill(1234, "TERM") -- Kill process by PID
|
|
||||||
job.killall("firefox") -- Kill all processes by name
|
|
||||||
processes = job.ps() -- Get process list
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logging and Colors
|
|
||||||
```lua
|
|
||||||
-- Colored logging
|
|
||||||
log.info("Information message")
|
|
||||||
log.warn("Warning message")
|
|
||||||
log.error("Error message")
|
|
||||||
log.success("Success message")
|
|
||||||
|
|
||||||
-- Color output
|
|
||||||
print(color.red("Error text"))
|
|
||||||
print(color.green("Success text"))
|
|
||||||
print(color.bold(color.yellow("Important notice")))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Archive Operations
|
|
||||||
```lua
|
|
||||||
-- Compression utilities
|
|
||||||
archive.tar_create("backup.tar.gz", {"file1.txt", "file2.txt"})
|
|
||||||
archive.tar_extract("backup.tar.gz", "/destination")
|
|
||||||
archive.zip_create("archive.zip", {"folder1", "folder2"})
|
|
||||||
archive.zip_extract("archive.zip", "/extract_here")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Convenience Aliases
|
|
||||||
|
|
||||||
Common shell commands are available as Lua functions:
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
ls() -- ls -la
|
-- Minimal helpers available globally
|
||||||
pwd() -- Get current directory
|
env_get(name) -- Get environment variable (with session overrides)
|
||||||
cd("/path") -- Change directory
|
env_set(name, value) -- Set environment variable (session + subprocess export)
|
||||||
mkdir("folder") -- Create directory
|
shell(cmd) -- Run shell command and return trimmed stdout
|
||||||
rm("file") -- Remove file
|
run(cmd) -- Run shell command and stream output (exit code returned)
|
||||||
cp("src", "dst") -- Copy file
|
|
||||||
mv("old", "new") -- Move/rename file
|
|
||||||
cat("file.txt") -- Read file contents
|
|
||||||
echo("Hello") -- Print and return text
|
|
||||||
grep("pattern", "file.txt") -- Search in file
|
|
||||||
head(5, "file.txt") -- First 5 lines
|
|
||||||
tail(10, "file.txt") -- Last 10 lines
|
|
||||||
wc("-l", "file.txt") -- Count lines
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Everything else is standard Lua.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Basic Usage
|
### Basic Usage
|
||||||
@@ -266,7 +142,6 @@ The interactive shell supports:
|
|||||||
- All luash preprocessing features
|
- All luash preprocessing features
|
||||||
- Multiline input for functions, loops, etc.
|
- Multiline input for functions, loops, etc.
|
||||||
- Command history with `.history`
|
- Command history with `.history`
|
||||||
- Screen clearing with `.clear`
|
|
||||||
- Special commands: `.help`, `.exit`, `.clear`
|
- Special commands: `.help`, `.exit`, `.clear`
|
||||||
- Real-time expression evaluation
|
- Real-time expression evaluation
|
||||||
|
|
||||||
@@ -329,13 +204,14 @@ file.write_lines("output.txt", processed)
|
|||||||
|
|
||||||
## Preprocessor Pipeline
|
## Preprocessor Pipeline
|
||||||
|
|
||||||
Luash applies several preprocessing steps:
|
Luash applies these preprocessing steps:
|
||||||
|
|
||||||
1. **Environment Variable Substitution**: `$VAR` and `${VAR}` → `env.get('VAR')`
|
1. **Shebang removal (line-preserving)**: `#!/usr/bin/env luash` is removed and replaced by a blank line to preserve line numbers.
|
||||||
2. **Interactive Command Processing**: `!command` → `run('command')`
|
2. **Environment variable assignment**: `$VAR = value` → `env_set('VAR', value)` (outside strings only)
|
||||||
3. **Shell Command Substitution**: `` `command` `` → `shell('command')`
|
3. **Environment variable substitution**: `$VAR` → `env_get('VAR')` (outside strings only; `${VAR}` is not supported)
|
||||||
4. **Variable Interpolation**: `#{var}` in commands → Lua string concatenation
|
4. **Interactive commands**: lines starting with `!cmd` → `run("cmd")`
|
||||||
5. **Library Injection**: Comprehensive shell utilities loaded automatically
|
5. **Command substitution**: `` `command` `` → `shell("command")`
|
||||||
|
6. **Lua variable interpolation in commands**: inside backticks, `#{var}` is replaced with the Lua value of `var`, with proper quoting handled by the caller
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
51
luash
51
luash
@@ -273,20 +273,44 @@ Multi-line input supported for functions, loops, etc.]])
|
|||||||
repl.buffer = line
|
repl.buffer = line
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we have a complete expression
|
-- Try to compile to determine if we need continuation (parser-driven)
|
||||||
if not needs_continuation(repl.buffer) and line ~= "" then
|
if line ~= "" then
|
||||||
-- Process and execute
|
|
||||||
table.insert(repl.history, repl.buffer)
|
|
||||||
|
|
||||||
local processed = preprocess_luash_repl(repl.buffer)
|
local processed = preprocess_luash_repl(repl.buffer)
|
||||||
|
|
||||||
|
-- First, try as an expression (so results print automatically)
|
||||||
local func, err = load("return " .. processed, "@repl")
|
local func, err = load("return " .. processed, "@repl")
|
||||||
|
|
||||||
if not func then
|
if not func then
|
||||||
-- Try without return (for statements)
|
if err and err:match("<eof>") then
|
||||||
func, err = load(processed, "@repl")
|
-- Incomplete chunk; wait for more input
|
||||||
end
|
-- keep buffer as-is
|
||||||
|
else
|
||||||
if func then
|
-- Try as a statement block
|
||||||
|
func, err = load(processed, "@repl")
|
||||||
|
if not func then
|
||||||
|
if err and err:match("<eof>") then
|
||||||
|
-- Incomplete; wait for more input
|
||||||
|
else
|
||||||
|
-- Definitive syntax error; report and clear buffer
|
||||||
|
print("Syntax error: " .. err)
|
||||||
|
repl.buffer = ""
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Executable statement block
|
||||||
|
table.insert(repl.history, repl.buffer)
|
||||||
|
local success, result = pcall(func)
|
||||||
|
if success then
|
||||||
|
if result ~= nil then
|
||||||
|
print(tostring(result))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("Error: " .. tostring(result))
|
||||||
|
end
|
||||||
|
repl.buffer = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Executable expression
|
||||||
|
table.insert(repl.history, repl.buffer)
|
||||||
local success, result = pcall(func)
|
local success, result = pcall(func)
|
||||||
if success then
|
if success then
|
||||||
if result ~= nil then
|
if result ~= nil then
|
||||||
@@ -295,11 +319,8 @@ Multi-line input supported for functions, loops, etc.]])
|
|||||||
else
|
else
|
||||||
print("Error: " .. tostring(result))
|
print("Error: " .. tostring(result))
|
||||||
end
|
end
|
||||||
else
|
repl.buffer = ""
|
||||||
print("Syntax error: " .. err)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
repl.buffer = ""
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user