Files
HyperionOS/Src/Hyperion-bash/bin/lua

326 lines
9.3 KiB
Plaintext

--:Minify:--
local C_PROMPT = 7
local C_CONT = 13
local C_OUT = 5
local C_ERR = 2
local C_KEY = 3
local C_STR = 9
local C_NUM = 10
local C_BOOL = 8
local C_NIL = 12
local C_TABLE = 13
local function c(col) syscall.devctl(1, "sfgc", col) end
local function w(s) syscall.write(1, tostring(s)) end
local MAX_DEPTH = 6
local MAX_ENTRIES = 64
local function prettyVal(val, indent, seen)
indent = indent or 0
seen = seen or {}
local t = type(val)
if t == "nil" then
c(C_NIL); w("nil")
elseif t == "boolean" then
c(C_BOOL); w(tostring(val))
elseif t == "number" then
c(C_NUM)
if val ~= val then
w("nan")
elseif val == math.huge then
w("inf")
elseif val == -math.huge then
w("-inf")
elseif val == math.floor(val) and math.abs(val) < 1e15 then
w(tostring(math.floor(val)))
else
w(tostring(val))
end
elseif t == "string" then
c(C_STR)
local s = string.format("%q", val)
w(s)
elseif t == "function" then
c(C_TABLE); w(tostring(val))
elseif t == "table" then
if seen[val] then
c(C_TABLE); w("<circular " .. tostring(val) .. ">")
return
end
if indent >= MAX_DEPTH then
c(C_TABLE); w("<table " .. tostring(val) .. ">")
return
end
seen[val] = true
local pad = string.rep(" ", indent)
local padIn = string.rep(" ", indent + 1)
local arrKeys = {}
local hashKeys = {}
local arrMax = #val
for k in pairs(val) do
if type(k) == "number" and k >= 1 and k <= arrMax and k == math.floor(k) then
arrKeys[#arrKeys+1] = k
else
hashKeys[#hashKeys+1] = k
end
end
table.sort(arrKeys)
table.sort(hashKeys, function(a, b)
local ta, tb = type(a), type(b)
if ta == tb then
if ta == "string" then return a < b end
if ta == "number" then return a < b end
return tostring(a) < tostring(b)
end
return ta < tb
end)
local total = #arrKeys + #hashKeys
if total == 0 then
c(C_TABLE); w("{}")
seen[val] = nil
return
end
c(C_TABLE); w("{\n")
local shown = 0
local function printEntry(k, v, isLast)
shown = shown + 1
if shown > MAX_ENTRIES then return true end
w(padIn)
if type(k) == "number" and arrKeys[k] then
else
if type(k) == "string" and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
c(C_KEY); w(k)
else
c(C_TABLE); w("[")
prettyVal(k, indent+1, seen)
c(C_TABLE); w("]")
end
c(C_TABLE); w(" = ")
end
prettyVal(v, indent+1, seen)
c(C_TABLE)
if not isLast then w(",") end
w("\n")
end
for i, k in ipairs(arrKeys) do
w(padIn)
prettyVal(val[k], indent+1, seen)
c(C_TABLE)
if i < total then w(",") end
w("\n")
shown = shown + 1
if shown >= MAX_ENTRIES then
c(C_NIL); w(padIn .. "-- ..." .. (total - shown) .. " more entries\n")
break
end
end
if shown < MAX_ENTRIES then
for i, k in ipairs(hashKeys) do
local isLast = (shown + 1 >= total)
w(padIn)
if type(k) == "string" and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
c(C_KEY); w(k)
else
c(C_TABLE); w("[")
prettyVal(k, indent+1, seen)
c(C_TABLE); w("]")
end
c(C_TABLE); w(" = ")
prettyVal(val[k], indent+1, seen)
c(C_TABLE)
shown = shown + 1
if shown < total then w(",") end
w("\n")
if shown >= MAX_ENTRIES then
local rem = total - shown
if rem > 0 then
c(C_NIL); w(padIn .. "-- ..." .. rem .. " more entries\n")
end
break
end
end
end
c(C_TABLE); w(pad .. "}")
seen[val] = nil
else
c(C_TABLE); w(tostring(val))
end
c(1)
end
local function printResults(...)
local n = select("#", ...)
if n == 0 then return end
for i = 1, n do
if i > 1 then c(C_TABLE); w("\t") end
prettyVal(select(i, ...), 0, {})
end
w("\n")
c(1)
end
local luaEnv = setmetatable({}, {__index = _ENV})
luaEnv._G = luaEnv
luaEnv.print = function(...)
local n = select("#", ...)
for i = 1, n do
if i > 1 then w("\t") end
prettyVal(select(i, ...), 0, {})
end
w("\n")
c(1)
end
luaEnv.pp = function(val)
prettyVal(val, 0, {})
w("\n")
c(1)
end
luaEnv.exit = setmetatable({}, {
__tostring = function() return "function: exit()" end,
__call = function() syscall.exit() end,
})
local function compile(code)
local exprFn = load("return " .. code, "@lua", "t", luaEnv)
if exprFn then return exprFn, true end
local stmtFn, err = load(code, "@lua", "t", luaEnv)
return stmtFn, false, err
end
local function isIncomplete(code)
local _, err = load(code, "@lua", "t", luaEnv)
return err and (err:find("<eof>") ~= nil or err:find("'end'") ~= nil
or err:find("'then'") ~= nil or err:find("'until'") ~= nil)
end
local function cleanErr(msg)
return tostring(msg)
:gsub("^%[string .-%]:", "")
:gsub("^@lua:", "")
:gsub("stack traceback:.*", "")
:match("^%s*(.-)%s*$")
end
local function runCode(code)
local fn, isExpr, err = compile(code)
if not fn then
c(C_ERR); w("[error] "); c(1); w(cleanErr(err) .. "\n")
return
end
local results = table.pack(xpcall(fn, debug.traceback))
local ok = table.remove(results, 1)
results.n = results.n - 1
if not ok then
c(C_ERR); w("[error] "); c(1); w(cleanErr(results[1]) .. "\n")
elseif isExpr and results.n > 0 then
c(C_OUT); w("= ")
printResults(table.unpack(results, 1, results.n))
end
end
local function getUserInput(prompt, history)
c(C_PROMPT); w(prompt); c(1)
local pos = syscall.devctl(1, "gpos")
local ox = tonumber(pos:sub(1, pos:find(";")-1))
local oy = tonumber(pos:sub(pos:find(";")+1))
local input = ""
local cursor = 1
local histIdx = 0
local blink = false
local dirty = true
local function redraw()
syscall.devctl(1, "spos", ox, oy)
w(input:sub(1, cursor-1))
if blink then syscall.devctl(1,"sfgc",16); syscall.devctl(1,"sbgc",1) end
w(cursor > #input and " " or input:sub(cursor, cursor))
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
w(input:sub(cursor+1) .. " ")
dirty = false
end
while true do
local key = syscall.read(0)
if key and key ~= "" then
if key == "\19" then
if cursor > 1 then cursor = cursor - 1; dirty = true end
elseif key == "\20" then
if cursor <= #input then cursor = cursor + 1; dirty = true end
elseif key == "\17" then
if history and histIdx < #history then
histIdx = histIdx + 1
input = history[#history - histIdx + 1]
cursor = #input + 1; dirty = true
end
elseif key == "\18" then
if histIdx > 1 then
histIdx = histIdx - 1
input = history[#history - histIdx + 1]
cursor = #input + 1; dirty = true
elseif histIdx == 1 then
histIdx = 0; input = ""; cursor = 1; dirty = true
end
elseif key == "\b" then
if cursor > 1 then
input = input:sub(1, cursor-2) .. input:sub(cursor)
cursor = cursor - 1; dirty = true
end
elseif key == "\n" then
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
syscall.devctl(1,"spos",ox,oy)
w(input .. " \n")
return input
else
input = input:sub(1, cursor-1) .. key .. input:sub(cursor)
cursor = cursor + 1; dirty = true
end
end
local nb = (math.floor(syscall.getUptime() / 500) % 2) == 0
if nb ~= blink then blink = nb; dirty = true end
if dirty then redraw() end
end
end
syscall.devctl(1, "clear")
syscall.devctl(1, "spos", 1, 1)
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
c(C_NIL)
w("Interactive Lua REPL. exit() to quit.\n\n")
c(1)
local history = {}
while true do
local code = getUserInput("lua> ", history)
if code == "" then goto continue end
while isIncomplete(code) do
code = code .. "\n" .. getUserInput("... ", nil)
end
if code ~= history[#history] then
history[#history+1] = code
end
runCode(code)
::continue::
end