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
|
||||
```
|
||||
|
||||
### 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
|
||||
```bash
|
||||
LUASH_DEBUG=1 ./luash script.luash
|
||||
```
|
||||
|
||||
### Example Script
|
||||
See `example.luash` for a comprehensive demonstration of all features.
|
||||
|
||||
## Installation
|
||||
|
||||
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
|
||||
-- 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]
|
||||
if not filename then
|
||||
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
|
||||
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