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.
|
||||
|
||||
### What You Get
|
||||
### Built-ins
|
||||
|
||||
#### File System Operations
|
||||
```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:
|
||||
Luash intentionally stays minimal. The injected helpers are:
|
||||
|
||||
```lua
|
||||
ls() -- ls -la
|
||||
pwd() -- Get current directory
|
||||
cd("/path") -- Change directory
|
||||
mkdir("folder") -- Create directory
|
||||
rm("file") -- Remove file
|
||||
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
|
||||
-- Minimal helpers available globally
|
||||
env_get(name) -- Get environment variable (with session overrides)
|
||||
env_set(name, value) -- Set environment variable (session + subprocess export)
|
||||
shell(cmd) -- Run shell command and return trimmed stdout
|
||||
run(cmd) -- Run shell command and stream output (exit code returned)
|
||||
```
|
||||
|
||||
Everything else is standard Lua.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
@@ -266,7 +142,6 @@ The interactive shell supports:
|
||||
- All luash preprocessing features
|
||||
- Multiline input for functions, loops, etc.
|
||||
- Command history with `.history`
|
||||
- Screen clearing with `.clear`
|
||||
- Special commands: `.help`, `.exit`, `.clear`
|
||||
- Real-time expression evaluation
|
||||
|
||||
@@ -329,13 +204,14 @@ file.write_lines("output.txt", processed)
|
||||
|
||||
## Preprocessor Pipeline
|
||||
|
||||
Luash applies several preprocessing steps:
|
||||
Luash applies these preprocessing steps:
|
||||
|
||||
1. **Environment Variable Substitution**: `$VAR` and `${VAR}` → `env.get('VAR')`
|
||||
2. **Interactive Command Processing**: `!command` → `run('command')`
|
||||
3. **Shell Command Substitution**: `` `command` `` → `shell('command')`
|
||||
4. **Variable Interpolation**: `#{var}` in commands → Lua string concatenation
|
||||
5. **Library Injection**: Comprehensive shell utilities loaded automatically
|
||||
1. **Shebang removal (line-preserving)**: `#!/usr/bin/env luash` is removed and replaced by a blank line to preserve line numbers.
|
||||
2. **Environment variable assignment**: `$VAR = value` → `env_set('VAR', value)` (outside strings only)
|
||||
3. **Environment variable substitution**: `$VAR` → `env_get('VAR')` (outside strings only; `${VAR}` is not supported)
|
||||
4. **Interactive commands**: lines starting with `!cmd` → `run("cmd")`
|
||||
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
|
||||
|
||||
|
||||
47
luash
47
luash
@@ -273,20 +273,30 @@ Multi-line input supported for functions, loops, etc.]])
|
||||
repl.buffer = line
|
||||
end
|
||||
|
||||
-- Check if we have a complete expression
|
||||
if not needs_continuation(repl.buffer) and line ~= "" then
|
||||
-- Process and execute
|
||||
table.insert(repl.history, repl.buffer)
|
||||
|
||||
-- Try to compile to determine if we need continuation (parser-driven)
|
||||
if line ~= "" then
|
||||
local processed = preprocess_luash_repl(repl.buffer)
|
||||
|
||||
-- First, try as an expression (so results print automatically)
|
||||
local func, err = load("return " .. processed, "@repl")
|
||||
|
||||
if not func then
|
||||
-- Try without return (for statements)
|
||||
if err and err:match("<eof>") then
|
||||
-- Incomplete chunk; wait for more input
|
||||
-- keep buffer as-is
|
||||
else
|
||||
-- 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
|
||||
|
||||
if func then
|
||||
else
|
||||
-- Executable statement block
|
||||
table.insert(repl.history, repl.buffer)
|
||||
local success, result = pcall(func)
|
||||
if success then
|
||||
if result ~= nil then
|
||||
@@ -295,13 +305,24 @@ Multi-line input supported for functions, loops, etc.]])
|
||||
else
|
||||
print("Error: " .. tostring(result))
|
||||
end
|
||||
else
|
||||
print("Syntax error: " .. err)
|
||||
end
|
||||
|
||||
repl.buffer = ""
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Executable expression
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user