--: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("") return end if indent >= MAX_DEPTH then c(C_TABLE); w("") 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("") ~= 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