interactive luash
This commit is contained in:
17
README.md
17
README.md
@@ -255,14 +255,25 @@ wc("-l", "file.txt") -- Count lines
|
|||||||
./luash script.luash
|
./luash script.luash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Interactive Shell (REPL)
|
||||||
|
```bash
|
||||||
|
./luash -i
|
||||||
|
# or
|
||||||
|
./luash_shell.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
The interactive shell supports:
|
||||||
|
- All luash preprocessing features
|
||||||
|
- Multiline input for functions, loops, etc.
|
||||||
|
- Command history with `.history`
|
||||||
|
- Special commands: `.help`, `.exit`, `.clear`
|
||||||
|
- Real-time expression evaluation
|
||||||
|
|
||||||
### Debug Mode
|
### Debug Mode
|
||||||
```bash
|
```bash
|
||||||
LUASH_DEBUG=1 ./luash script.luash
|
LUASH_DEBUG=1 ./luash script.luash
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example Script
|
|
||||||
See `example.luash` for a comprehensive demonstration of all features.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Clone or download the luash repository
|
1. Clone or download the luash repository
|
||||||
|
|||||||
118
interactive.luash
Normal file
118
interactive.luash
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env luash
|
||||||
|
-- Interactive Luash Demo/REPL Alternative
|
||||||
|
-- Shows how to build interactive features within luash
|
||||||
|
|
||||||
|
print("🚀 Luash Interactive Demo")
|
||||||
|
print("This demonstrates interactive capabilities in luash")
|
||||||
|
print()
|
||||||
|
|
||||||
|
-- Simple command processor
|
||||||
|
function process_command(cmd)
|
||||||
|
cmd = cmd:match("^%s*(.-)%s*$") -- trim
|
||||||
|
|
||||||
|
if cmd == "help" then
|
||||||
|
print([[
|
||||||
|
Available commands:
|
||||||
|
sysinfo - Show system information
|
||||||
|
files - List files in current directory
|
||||||
|
env - Show environment variables
|
||||||
|
network - Test network connectivity
|
||||||
|
processes - Show running processes
|
||||||
|
disk - Show disk usage
|
||||||
|
quit - Exit
|
||||||
|
|
||||||
|
Or type any luash expression!]])
|
||||||
|
|
||||||
|
elseif cmd == "sysinfo" then
|
||||||
|
print("System Information:")
|
||||||
|
print(" OS: " .. `uname -s`)
|
||||||
|
print(" Architecture: " .. `uname -m`)
|
||||||
|
print(" Hostname: " .. `hostname`)
|
||||||
|
print(" User: " .. $USER)
|
||||||
|
print(" Uptime: " .. `uptime`)
|
||||||
|
|
||||||
|
elseif cmd == "files" then
|
||||||
|
print("Files in current directory:")
|
||||||
|
files = `ls -la`
|
||||||
|
print(files)
|
||||||
|
|
||||||
|
elseif cmd == "env" then
|
||||||
|
print("Key environment variables:")
|
||||||
|
print(" USER: " .. $USER)
|
||||||
|
print(" HOME: " .. $HOME)
|
||||||
|
print(" SHELL: " .. $SHELL)
|
||||||
|
print(" PATH: " .. `echo $PATH | cut -d: -f1-3`)
|
||||||
|
|
||||||
|
elseif cmd == "network" then
|
||||||
|
print("Network connectivity test:")
|
||||||
|
hosts = {"google.com", "github.com"}
|
||||||
|
for i = 1, #hosts do
|
||||||
|
local host = hosts[i]
|
||||||
|
local result = `ping -c 1 #{host} >/dev/null 2>&1 && echo "ok" || echo "fail"`
|
||||||
|
local status = result == "ok" and "✓" or "✗"
|
||||||
|
print(" " .. status .. " " .. host)
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif cmd == "processes" then
|
||||||
|
print("Top processes:")
|
||||||
|
!ps aux | head -10
|
||||||
|
|
||||||
|
elseif cmd == "disk" then
|
||||||
|
print("Disk usage:")
|
||||||
|
!df -h
|
||||||
|
|
||||||
|
elseif cmd == "quit" or cmd == "exit" then
|
||||||
|
return false
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Try to execute as luash code
|
||||||
|
if cmd ~= "" then
|
||||||
|
print("Executing: " .. cmd)
|
||||||
|
-- In a real REPL, we'd eval this
|
||||||
|
-- For demo, just show some dynamic examples
|
||||||
|
if cmd:match("^%$") then
|
||||||
|
print("Environment variable assignment")
|
||||||
|
elseif cmd:match("`.*`") then
|
||||||
|
print("Shell command execution")
|
||||||
|
else
|
||||||
|
print("Lua expression")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Main interaction loop
|
||||||
|
print("Type 'help' for commands, 'quit' to exit")
|
||||||
|
print()
|
||||||
|
|
||||||
|
-- Simulate some interactive commands
|
||||||
|
commands = {
|
||||||
|
"help",
|
||||||
|
"sysinfo",
|
||||||
|
"files",
|
||||||
|
"network",
|
||||||
|
"quit"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, #commands do
|
||||||
|
local cmd = commands[i]
|
||||||
|
print("luash> " .. cmd)
|
||||||
|
|
||||||
|
if not process_command(cmd) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if i < #commands then
|
||||||
|
print("\nPress Enter to continue...")
|
||||||
|
io.read() -- Wait for user input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print("\n✨ This shows how luash can build interactive applications!")
|
||||||
|
print("For a full REPL, we'd:")
|
||||||
|
print(" - Read input in a loop")
|
||||||
|
print(" - Parse and execute luash expressions")
|
||||||
|
print(" - Handle multiline input")
|
||||||
|
print(" - Maintain command history")
|
||||||
8
luash
8
luash
@@ -1,10 +1,16 @@
|
|||||||
#!/usr/bin/env lua
|
#!/usr/bin/env lua
|
||||||
-- luash: Minimal Lua shell scripting preprocessor
|
-- luash: Minimal Lua shell scripting preprocessor
|
||||||
|
|
||||||
-- 1. Check for an input file
|
-- 1. Check for an input file or REPL mode
|
||||||
local filename = arg[1]
|
local filename = arg[1]
|
||||||
if not filename then
|
if not filename then
|
||||||
print("Usage: luash <script.luash>")
|
print("Usage: luash <script.luash>")
|
||||||
|
print(" luash -i # Interactive mode")
|
||||||
|
return
|
||||||
|
elseif filename == "-i" or filename == "--interactive" then
|
||||||
|
-- Start interactive REPL
|
||||||
|
local script_dir = debug.getinfo(1, "S").source:match("@(.*/)")
|
||||||
|
dofile(script_dir .. "luash_shell.lua")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
295
luash_shell.lua
Executable file
295
luash_shell.lua
Executable file
@@ -0,0 +1,295 @@
|
|||||||
|
#!/usr/bin/env lua
|
||||||
|
-- Luash Interactive Shell - A proper REPL
|
||||||
|
-- Supports multiline input, command history, and all luash features
|
||||||
|
|
||||||
|
-- Load the injected library functions
|
||||||
|
local script_dir = debug.getinfo(1, "S").source:match("@(.*/)")
|
||||||
|
local lib_file = io.open(script_dir .. "__injected_lib.lua", "r")
|
||||||
|
if lib_file then
|
||||||
|
local lib_code = lib_file:read("*a")
|
||||||
|
lib_file:close()
|
||||||
|
local lib_func = load(lib_code)
|
||||||
|
if lib_func then lib_func() end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Luash preprocessing functions (copied from main luash script)
|
||||||
|
local function preprocess_luash(code)
|
||||||
|
-- Remove shebang
|
||||||
|
code = code:gsub("^#![^\r\n]*[\r\n]?", "")
|
||||||
|
|
||||||
|
-- Environment variable processing
|
||||||
|
local lines = {}
|
||||||
|
for line in code:gmatch("([^\r\n]*)") do
|
||||||
|
if not line:match("^%s*%-%-") then -- Skip comments
|
||||||
|
-- Handle environment variable assignment: $VAR=value
|
||||||
|
if line:match("^%$([%w_]+)%s*=") then
|
||||||
|
line = line:gsub("^%$([%w_]+)%s*=%s*(.+)", function(var, value)
|
||||||
|
return "env_set('" .. var .. "', " .. value .. ")"
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
-- Handle $VAR substitution, but not inside strings
|
||||||
|
local in_string = false
|
||||||
|
local quote_char = nil
|
||||||
|
local result = ""
|
||||||
|
local i = 1
|
||||||
|
|
||||||
|
while i <= #line do
|
||||||
|
local char = line:sub(i, i)
|
||||||
|
|
||||||
|
if (char == '"' or char == "'") and (i == 1 or line:sub(i-1, i-1) ~= "\\") then
|
||||||
|
if not in_string then
|
||||||
|
in_string = true
|
||||||
|
quote_char = char
|
||||||
|
elseif char == quote_char then
|
||||||
|
in_string = false
|
||||||
|
quote_char = nil
|
||||||
|
end
|
||||||
|
result = result .. char
|
||||||
|
i = i + 1
|
||||||
|
elseif char == "$" and not in_string then
|
||||||
|
local var_match = line:sub(i):match("^%$([%w_]+)")
|
||||||
|
if var_match then
|
||||||
|
result = result .. "env_get('" .. var_match .. "')"
|
||||||
|
i = i + #var_match + 1
|
||||||
|
else
|
||||||
|
result = result .. char
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result = result .. char
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
line = result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(lines, line)
|
||||||
|
end
|
||||||
|
code = table.concat(lines, "\n")
|
||||||
|
|
||||||
|
-- Shell command processing
|
||||||
|
lines = {}
|
||||||
|
for line in code:gmatch("([^\r\n]*)") do
|
||||||
|
local in_string = false
|
||||||
|
local quote_char = nil
|
||||||
|
local result = ""
|
||||||
|
local i = 1
|
||||||
|
|
||||||
|
while i <= #line do
|
||||||
|
local char = line:sub(i, i)
|
||||||
|
|
||||||
|
if (char == '"' or char == "'") and (i == 1 or line:sub(i-1, i-1) ~= "\\") then
|
||||||
|
if not in_string then
|
||||||
|
in_string = true
|
||||||
|
quote_char = char
|
||||||
|
elseif char == quote_char then
|
||||||
|
in_string = false
|
||||||
|
quote_char = nil
|
||||||
|
end
|
||||||
|
result = result .. char
|
||||||
|
i = i + 1
|
||||||
|
elseif char == "`" and not in_string then
|
||||||
|
local end_pos = line:find("`", i + 1)
|
||||||
|
if end_pos then
|
||||||
|
local command = line:sub(i + 1, end_pos - 1)
|
||||||
|
|
||||||
|
if command:find("#{") then
|
||||||
|
local escaped_cmd = command:gsub('"', '\\"')
|
||||||
|
local interpolated_cmd = escaped_cmd:gsub("#{([%w_.]+)}", '" .. tostring(%1) .. "')
|
||||||
|
result = result .. 'shell("' .. interpolated_cmd .. '")'
|
||||||
|
else
|
||||||
|
result = result .. 'shell("' .. command:gsub('"', '\\"') .. '")'
|
||||||
|
end
|
||||||
|
i = end_pos + 1
|
||||||
|
else
|
||||||
|
result = result .. char
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result = result .. char
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(lines, result)
|
||||||
|
end
|
||||||
|
code = table.concat(lines, "\n")
|
||||||
|
|
||||||
|
-- Interactive commands
|
||||||
|
lines = {}
|
||||||
|
for line in code:gmatch("([^\r\n]*)") do
|
||||||
|
local command = line:match("^%s*!(.*)")
|
||||||
|
if command then
|
||||||
|
local indent = line:match("^(%s*)")
|
||||||
|
table.insert(lines, indent .. 'run("' .. command:gsub('"', '\\"') .. '")')
|
||||||
|
else
|
||||||
|
table.insert(lines, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
code = table.concat(lines, "\n")
|
||||||
|
|
||||||
|
return code
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if expression needs more input (simple heuristic)
|
||||||
|
local function needs_continuation(code)
|
||||||
|
local open_blocks = 0
|
||||||
|
local open_parens = 0
|
||||||
|
local open_brackets = 0
|
||||||
|
local open_braces = 0
|
||||||
|
|
||||||
|
local in_string = false
|
||||||
|
local quote_char = nil
|
||||||
|
|
||||||
|
for i = 1, #code do
|
||||||
|
local char = code:sub(i, i)
|
||||||
|
|
||||||
|
if not in_string then
|
||||||
|
if char == '"' or char == "'" then
|
||||||
|
in_string = true
|
||||||
|
quote_char = char
|
||||||
|
elseif char == "(" then
|
||||||
|
open_parens = open_parens + 1
|
||||||
|
elseif char == ")" then
|
||||||
|
open_parens = open_parens - 1
|
||||||
|
elseif char == "[" then
|
||||||
|
open_brackets = open_brackets + 1
|
||||||
|
elseif char == "]" then
|
||||||
|
open_brackets = open_brackets - 1
|
||||||
|
elseif char == "{" then
|
||||||
|
open_braces = open_braces + 1
|
||||||
|
elseif char == "}" then
|
||||||
|
open_braces = open_braces - 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if char == quote_char and code:sub(i-1, i-1) ~= "\\" then
|
||||||
|
in_string = false
|
||||||
|
quote_char = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for Lua keywords that need 'end'
|
||||||
|
local keywords = {"function", "if", "for", "while", "do", "then"}
|
||||||
|
local end_count = 0
|
||||||
|
|
||||||
|
for _, keyword in ipairs(keywords) do
|
||||||
|
local _, count = code:gsub("%f[%w]" .. keyword .. "%f[%W]", "")
|
||||||
|
open_blocks = open_blocks + count
|
||||||
|
end
|
||||||
|
|
||||||
|
_, end_count = code:gsub("%f[%w]end%f[%W]", "")
|
||||||
|
open_blocks = open_blocks - end_count
|
||||||
|
|
||||||
|
return open_blocks > 0 or open_parens > 0 or open_brackets > 0 or open_braces > 0 or in_string
|
||||||
|
end
|
||||||
|
|
||||||
|
-- REPL state
|
||||||
|
local repl = {
|
||||||
|
buffer = "",
|
||||||
|
history = {},
|
||||||
|
history_index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Add utility functions
|
||||||
|
function trim(s)
|
||||||
|
return s:match("^%s*(.-)%s*$")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Main REPL function
|
||||||
|
local function run_repl()
|
||||||
|
print("🚀 Luash Interactive Shell")
|
||||||
|
print("Type Lua expressions, use $VAR for env vars, `commands` for shell")
|
||||||
|
print("Special commands: .help .exit .clear .history")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local prompt = repl.buffer == "" and "luash> " or " ... "
|
||||||
|
io.write(prompt)
|
||||||
|
io.flush()
|
||||||
|
|
||||||
|
local line = io.read("*line")
|
||||||
|
|
||||||
|
if not line then -- EOF (Ctrl+D)
|
||||||
|
print("\nGoodbye!")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
line = trim(line)
|
||||||
|
|
||||||
|
-- Handle special dot commands
|
||||||
|
if line == ".exit" or line == ".quit" then
|
||||||
|
break
|
||||||
|
elseif line == ".clear" then
|
||||||
|
repl.buffer = ""
|
||||||
|
print("Buffer cleared")
|
||||||
|
elseif line == ".history" then
|
||||||
|
for i, cmd in ipairs(repl.history) do
|
||||||
|
print(string.format("%3d: %s", i, cmd))
|
||||||
|
end
|
||||||
|
elseif line == ".help" then
|
||||||
|
print([[
|
||||||
|
Luash Interactive Shell Help:
|
||||||
|
|
||||||
|
Luash Features:
|
||||||
|
$VAR - Access environment variables
|
||||||
|
$VAR = "value" - Set environment variables
|
||||||
|
`command` - Execute shell commands
|
||||||
|
#{var} - Variable interpolation in commands
|
||||||
|
!command - Interactive shell commands
|
||||||
|
|
||||||
|
Special Commands:
|
||||||
|
.help - Show this help
|
||||||
|
.exit, .quit - Exit the shell
|
||||||
|
.clear - Clear input buffer
|
||||||
|
.history - Show command history
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
print("Hello " .. $USER)
|
||||||
|
files = `ls -la`
|
||||||
|
$GREETING = "Hello World"
|
||||||
|
result = `echo #{$GREETING}`
|
||||||
|
!ps aux | head -5
|
||||||
|
|
||||||
|
Multi-line input supported for functions, loops, etc.]])
|
||||||
|
else
|
||||||
|
-- Add to buffer
|
||||||
|
if repl.buffer ~= "" then
|
||||||
|
repl.buffer = repl.buffer .. "\n" .. line
|
||||||
|
else
|
||||||
|
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)
|
||||||
|
|
||||||
|
local processed = preprocess_luash(repl.buffer)
|
||||||
|
local func, err = load("return " .. processed, "@repl")
|
||||||
|
|
||||||
|
if not func then
|
||||||
|
-- Try without return (for statements)
|
||||||
|
func, err = load(processed, "@repl")
|
||||||
|
end
|
||||||
|
|
||||||
|
if func then
|
||||||
|
local success, result = pcall(func)
|
||||||
|
if success then
|
||||||
|
if result ~= nil then
|
||||||
|
print(tostring(result))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("Error: " .. tostring(result))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("Syntax error: " .. err)
|
||||||
|
end
|
||||||
|
|
||||||
|
repl.buffer = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start the REPL
|
||||||
|
run_repl()
|
||||||
71
repl_demo.luash
Normal file
71
repl_demo.luash
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env luash
|
||||||
|
-- REPL Demo: Showcasing interactive features
|
||||||
|
|
||||||
|
print("🎮 Luash Interactive Shell Demo")
|
||||||
|
print("===============================\n")
|
||||||
|
|
||||||
|
print("Luash now supports a full interactive REPL!")
|
||||||
|
print("Start it with: ./luash -i")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("🔥 REPL Features:")
|
||||||
|
print("✓ All luash preprocessing ($VAR, `commands`, #{interpolation})")
|
||||||
|
print("✓ Multiline input (functions, loops, if blocks)")
|
||||||
|
print("✓ Command history")
|
||||||
|
print("✓ Special dot commands (.help, .exit, .clear, .history)")
|
||||||
|
print("✓ Real-time expression evaluation")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("📝 Example REPL Session:")
|
||||||
|
examples = {
|
||||||
|
'print("Hello " .. $USER)',
|
||||||
|
'$GREETING = "Hello from REPL"',
|
||||||
|
'files = `ls *.luash | wc -l`',
|
||||||
|
'print("Found " .. files .. " luash files")',
|
||||||
|
'host = "google.com"',
|
||||||
|
'status = `ping -c 1 #{host} >/dev/null && echo "ok"`',
|
||||||
|
'print(host .. " is " .. status)',
|
||||||
|
'',
|
||||||
|
'-- Multiline example:',
|
||||||
|
'for i = 1, 3 do',
|
||||||
|
' print("Iteration: " .. i)',
|
||||||
|
'end',
|
||||||
|
'',
|
||||||
|
'-- Function definition:',
|
||||||
|
'function greet(name)',
|
||||||
|
' return "Hello, " .. name .. "!"',
|
||||||
|
'end',
|
||||||
|
'',
|
||||||
|
'print(greet($USER))'
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, example in ipairs(examples) do
|
||||||
|
if example == "" then
|
||||||
|
print()
|
||||||
|
elseif example:match("^%-%-") then
|
||||||
|
print(example)
|
||||||
|
else
|
||||||
|
print("luash> " .. example)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("🚀 Try it now: ./luash -i")
|
||||||
|
print(" Or run: ./luash_shell.lua")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("💡 The REPL supports:")
|
||||||
|
print(" • Environment variables: $HOME, $USER, etc.")
|
||||||
|
print(" • Shell commands: `ls`, `pwd`, `date`")
|
||||||
|
print(" • Variable interpolation: `echo #{variable}`")
|
||||||
|
print(" • Interactive commands: !git status")
|
||||||
|
print(" • Full Lua: functions, loops, tables, etc.")
|
||||||
|
print(" • Command history and editing")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("✨ Perfect for:")
|
||||||
|
print(" • Interactive system administration")
|
||||||
|
print(" • Exploring file systems and processes")
|
||||||
|
print(" • Quick shell command prototyping")
|
||||||
|
print(" • Learning luash syntax")
|
||||||
|
print(" • Debugging shell scripts")
|
||||||
Reference in New Issue
Block a user