mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 18:46:43 +00:00
Update CapyOS
This commit is contained in:
parent
27bfd6d363
commit
bc87363f7c
28 changed files with 786 additions and 656 deletions
94
Capy64/Assets/Lua/bin/fun/mandelbrot.lua
Normal file
94
Capy64/Assets/Lua/bin/fun/mandelbrot.lua
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
-- Mandelbrot in Capy64
|
||||||
|
|
||||||
|
local gpu = require("gpu")
|
||||||
|
local timer = require("timer")
|
||||||
|
local event = require("event")
|
||||||
|
local term = require("term")
|
||||||
|
|
||||||
|
-- lower = closer = slower
|
||||||
|
local scale = 4
|
||||||
|
|
||||||
|
-- higher = more detailed = slower
|
||||||
|
local iterations = 100
|
||||||
|
|
||||||
|
local pscale = scale
|
||||||
|
local w, h = gpu.getSize()
|
||||||
|
local px = pscale / w
|
||||||
|
local colorUnit = math.floor(0xffffff / iterations)
|
||||||
|
-- todo: make it interactive
|
||||||
|
local dx, dy = 0, 0
|
||||||
|
local cx, cy = math.floor(w / 2), math.floor(h / 2)
|
||||||
|
|
||||||
|
-- z = z^2 + c
|
||||||
|
local function mandelbrot(zr, zi, cr, ci)
|
||||||
|
return zr ^ 2 - zi ^ 2 + cr,
|
||||||
|
2 * zr * zi + ci
|
||||||
|
end
|
||||||
|
|
||||||
|
local function iter(cr, ci)
|
||||||
|
local zr, zi = 0, 0
|
||||||
|
for i = 1, iterations do
|
||||||
|
zr, zi = mandelbrot(zr, zi, cr, ci)
|
||||||
|
if math.abs(zr) >= (pscale >= 2 and pscale or 2) or math.abs(zi) >= (pscale >= 2 and pscale or 2) then
|
||||||
|
return false, colorUnit, i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, 0, iterations
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw()
|
||||||
|
local buffer <close> = gpu.newBuffer()
|
||||||
|
|
||||||
|
for y = 0, h - 1 do
|
||||||
|
for x = 0, w - 1 do
|
||||||
|
local _, _, i = iter((x - cx + dx * pscale) * px, (y - cy + dy * pscale) * px)
|
||||||
|
buffer[y * w + x] = colorUnit * (iterations - i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
gpu.setBuffer(buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- no idea why it's needed
|
||||||
|
timer.sleep(1)
|
||||||
|
|
||||||
|
draw()
|
||||||
|
|
||||||
|
local tw, th = term.getSize()
|
||||||
|
|
||||||
|
while true do
|
||||||
|
term.setPos(1, th)
|
||||||
|
term.setBackground(0)
|
||||||
|
term.setForeground(0xffffff)
|
||||||
|
term.write("X: " .. dx .. "; Y: " .. dy .. "; S: " .. pscale .. "; " .. px .. "!")
|
||||||
|
local ev = { event.pull("key_down") }
|
||||||
|
if ev[1] == "key_down" then
|
||||||
|
local key = ev[3]
|
||||||
|
if key == "up" then
|
||||||
|
dy = dy - 10 / pscale
|
||||||
|
elseif key == "down" then
|
||||||
|
dy = dy + 10 / pscale
|
||||||
|
elseif key == "right" then
|
||||||
|
dx = dx + 10 / pscale
|
||||||
|
elseif key == "left" then
|
||||||
|
dx = dx - 10 / pscale
|
||||||
|
elseif key == "enter" then
|
||||||
|
draw()
|
||||||
|
elseif key == "page_down" then
|
||||||
|
pscale = pscale * 1.25
|
||||||
|
dx = dx * pscale
|
||||||
|
dy = dy * pscale
|
||||||
|
elseif key == "page_up" then
|
||||||
|
pscale = pscale / 1.25
|
||||||
|
dx = dx / pscale
|
||||||
|
dy = dy / pscale
|
||||||
|
elseif key == "r" then
|
||||||
|
pscale = scale
|
||||||
|
dx = 0
|
||||||
|
dy = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
px = pscale / w
|
||||||
|
end
|
73
Capy64/Assets/Lua/bin/fun/melt.lua
Normal file
73
Capy64/Assets/Lua/bin/fun/melt.lua
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
local gpu = require("gpu")
|
||||||
|
local timer = require("timer")
|
||||||
|
local event = require("event")
|
||||||
|
local colors = require("colors")
|
||||||
|
local parallel = require("parallel")
|
||||||
|
|
||||||
|
local melts = 2 ^ 12
|
||||||
|
|
||||||
|
local function contains(arr, val)
|
||||||
|
for k, v in ipairs(arr) do
|
||||||
|
if v == val then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function melt()
|
||||||
|
local w, h = gpu.getSize()
|
||||||
|
local x, y = 0, 0
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local buffer <close> = gpu.getBuffer()
|
||||||
|
for i = 1, melts do
|
||||||
|
local nx = math.random(x, w)
|
||||||
|
local ny = math.random(y, h)
|
||||||
|
|
||||||
|
local c = buffer[ny * w + nx]
|
||||||
|
buffer[(ny + 1) * w + nx] = c
|
||||||
|
end
|
||||||
|
gpu.setBuffer(buffer)
|
||||||
|
|
||||||
|
timer.sleep(10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw()
|
||||||
|
local ox, oy
|
||||||
|
while true do
|
||||||
|
local ev, b, x, y = event.pull("mouse_move", "mouse_down")
|
||||||
|
|
||||||
|
if ev == "mouse_down" then
|
||||||
|
if b == 1 then
|
||||||
|
ox = x
|
||||||
|
oy = y
|
||||||
|
end
|
||||||
|
elseif ev == "mouse_move" then
|
||||||
|
if contains(b, 1) then
|
||||||
|
gpu.plot(x, y, colors.red)
|
||||||
|
gpu.drawLine(x, y, ox, oy, colors.red)
|
||||||
|
ox, oy = x, y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function random()
|
||||||
|
local w, h = gpu.getSize()
|
||||||
|
while true do
|
||||||
|
for i = 1, 24 do
|
||||||
|
gpu.drawString(
|
||||||
|
math.random(-7, w),
|
||||||
|
math.random(-13, h),
|
||||||
|
math.random(0, 0xffffff),
|
||||||
|
string.char(math.random(32, 127))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.sleep(100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
parallel.waitForAny(draw, melt, random)
|
|
@ -5,7 +5,7 @@ local term = require("term")
|
||||||
local function slowPrint(text, delay)
|
local function slowPrint(text, delay)
|
||||||
for i = 1, #text do
|
for i = 1, #text do
|
||||||
local ch = text:sub(i, i)
|
local ch = text:sub(i, i)
|
||||||
write(ch)
|
io.write(ch)
|
||||||
timer.sleep(delay)
|
timer.sleep(delay)
|
||||||
end
|
end
|
||||||
print()
|
print()
|
||||||
|
|
|
@ -19,9 +19,6 @@ local tEnv = {
|
||||||
__tostring = function() return "Call exit() to exit." end,
|
__tostring = function() return "Call exit() to exit." end,
|
||||||
__call = function() bRunning = false end,
|
__call = function() bRunning = false end,
|
||||||
}),
|
}),
|
||||||
["_echo"] = function(...)
|
|
||||||
return ...
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
setmetatable(tEnv, { __index = _ENV })
|
setmetatable(tEnv, { __index = _ENV })
|
||||||
|
|
||||||
|
@ -36,7 +33,7 @@ term.setForeground(colours.white)
|
||||||
|
|
||||||
while bRunning do
|
while bRunning do
|
||||||
term.setForeground(colours.yellow)
|
term.setForeground(colours.yellow)
|
||||||
write("> ")
|
io.write("> ")
|
||||||
term.setForeground(colours.white)
|
term.setForeground(colours.white)
|
||||||
|
|
||||||
local s = io.read(nil, tCommandHistory)
|
local s = io.read(nil, tCommandHistory)
|
||||||
|
@ -46,7 +43,7 @@ while bRunning do
|
||||||
|
|
||||||
local nForcePrint = 0
|
local nForcePrint = 0
|
||||||
local func, e = load(s, "=lua", "t", tEnv)
|
local func, e = load(s, "=lua", "t", tEnv)
|
||||||
local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv)
|
local func2 = load("return " .. s, "=lua", "t", tEnv)
|
||||||
if not func then
|
if not func then
|
||||||
if func2 then
|
if func2 then
|
||||||
func = func2
|
func = func2
|
||||||
|
@ -69,10 +66,10 @@ while bRunning do
|
||||||
n = n + 1
|
n = n + 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print(tResults[2])
|
io.stderr.print(tResults[2])
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print(e)
|
io.stderr.print(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -1,6 +1,5 @@
|
||||||
local term = require("term")
|
local term = require("term")
|
||||||
local colors = require("colors")
|
local colors = require("colors")
|
||||||
local io = require("io")
|
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
|
|
||||||
local exit = false
|
local exit = false
|
||||||
|
@ -17,17 +16,6 @@ local function buildEnvironment()
|
||||||
}, { __index = _G })
|
}, { __index = _G })
|
||||||
end
|
end
|
||||||
|
|
||||||
local function printError(...)
|
|
||||||
local cfg = {term.getForeground()}
|
|
||||||
local cbg = {term.getBackground()}
|
|
||||||
|
|
||||||
term.setForeground(colors.red)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
print(...)
|
|
||||||
term.setForeground(table.unpack(cfg))
|
|
||||||
term.setBackground(table.unpack(cbg))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function tokenise(...)
|
local function tokenise(...)
|
||||||
local sLine = table.concat({ ... }, " ")
|
local sLine = table.concat({ ... }, " ")
|
||||||
local tWords = {}
|
local tWords = {}
|
||||||
|
@ -58,6 +46,10 @@ function shell.resolve(path)
|
||||||
return fs.combine("", path)
|
return fs.combine("", path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if path:sub(1, 1) == "~" then
|
||||||
|
return fs.combine(shell.homePath, path)
|
||||||
|
end
|
||||||
|
|
||||||
return fs.combine(currentDir, path)
|
return fs.combine(currentDir, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -67,7 +59,7 @@ function shell.resolveProgram(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
for seg in shell.path:gmatch("[^;]+") do
|
for seg in shell.path:gmatch("[^;]+") do
|
||||||
local resolved = seg:gsub("%?", path)
|
local resolved = shell.resolve(seg:gsub("%?", path))
|
||||||
if fs.exists(resolved) and not fs.isDir(resolved) then
|
if fs.exists(resolved) and not fs.isDir(resolved) then
|
||||||
return resolved
|
return resolved
|
||||||
end
|
end
|
||||||
|
@ -80,7 +72,7 @@ function shell.run(...)
|
||||||
local path = shell.resolveProgram(command)
|
local path = shell.resolveProgram(command)
|
||||||
|
|
||||||
if not path then
|
if not path then
|
||||||
printError("Command not found: " .. command)
|
io.stderr.print("Command not found: " .. command)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -89,13 +81,13 @@ function shell.run(...)
|
||||||
local func, err = loadfile(path, "t", env)
|
local func, err = loadfile(path, "t", env)
|
||||||
|
|
||||||
if not func then
|
if not func then
|
||||||
printError(err)
|
io.stderr.print(err)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = pcall(func, table.unpack(args, 2))
|
local ok, err = pcall(func, table.unpack(args, 2))
|
||||||
if not ok then
|
if not ok then
|
||||||
printError(err)
|
io.stderr.print(err)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -106,17 +98,21 @@ function shell.exit()
|
||||||
exit = true
|
exit = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not fs.exists(shell.homePath) then
|
||||||
|
fs.makeDir(shell.homePath)
|
||||||
|
end
|
||||||
|
|
||||||
local history = {}
|
local history = {}
|
||||||
local lastExecSuccess = true
|
local lastExecSuccess = true
|
||||||
while not exit do
|
while not exit do
|
||||||
|
term.setBackground(colors.black)
|
||||||
term.setForeground(colors.white)
|
term.setForeground(colors.white)
|
||||||
write(":")
|
io.write(":")
|
||||||
term.setForeground(colors.lightBlue)
|
term.setForeground(colors.lightBlue)
|
||||||
local currentDir = shell.getDir()
|
|
||||||
if currentDir == shell.homePath then
|
if currentDir == shell.homePath then
|
||||||
write("~")
|
io.write("~")
|
||||||
else
|
else
|
||||||
write(currentDir)
|
io.write(currentDir)
|
||||||
end
|
end
|
||||||
|
|
||||||
if lastExecSuccess then
|
if lastExecSuccess then
|
||||||
|
@ -124,7 +120,7 @@ while not exit do
|
||||||
else
|
else
|
||||||
term.setForeground(colors.red)
|
term.setForeground(colors.red)
|
||||||
end
|
end
|
||||||
write("$ ")
|
io.write("$ ")
|
||||||
|
|
||||||
term.setForeground(colors.white)
|
term.setForeground(colors.white)
|
||||||
local line = io.read(nil, history)
|
local line = io.read(nil, history)
|
||||||
|
|
1
Capy64/Assets/Lua/bin/version.lua
Normal file
1
Capy64/Assets/Lua/bin/version.lua
Normal file
|
@ -0,0 +1 @@
|
||||||
|
print(string.format("%s @ %s - %s", os.version(), _HOST, _VERSION))
|
|
@ -19,12 +19,14 @@ if not http.checkURL(args[1]) then
|
||||||
error("Invalid URL", 0)
|
error("Invalid URL", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local response, err = http.get(args[1])
|
local response <close>, err = http.get(args[1], nil, {
|
||||||
|
binary = true,
|
||||||
|
})
|
||||||
if not response then
|
if not response then
|
||||||
error(err, 0)
|
error(err, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local file = fs.open(outputPath, "w")
|
local file <close> = fs.open(outputPath, "w")
|
||||||
file:write(response:readAll())
|
file:write(response:readAll())
|
||||||
file:close()
|
file:close()
|
||||||
response:close()
|
response:close()
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
os.print = print
|
|
3
Capy64/Assets/Lua/boot/00_package.lua
Normal file
3
Capy64/Assets/Lua/boot/00_package.lua
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path
|
||||||
|
|
||||||
|
_G.io = require("io")
|
|
@ -1 +0,0 @@
|
||||||
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path
|
|
15
Capy64/Assets/Lua/boot/01_stdio.lua
Normal file
15
Capy64/Assets/Lua/boot/01_stdio.lua
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
function _G.print(...)
|
||||||
|
local args = { ... }
|
||||||
|
local size = #args
|
||||||
|
local lines = 0
|
||||||
|
for n, v in ipairs(args) do
|
||||||
|
local s = tostring(v)
|
||||||
|
if n < size then
|
||||||
|
s = s .. "\t"
|
||||||
|
end
|
||||||
|
lines = lines + io.write(s)
|
||||||
|
end
|
||||||
|
lines = lines + io.write("\n")
|
||||||
|
|
||||||
|
return lines
|
||||||
|
end
|
|
@ -31,16 +31,16 @@ function http.request(url, body, headers, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
local requestId = http.requestAsync(url, body, headers, options)
|
local requestId = http.requestAsync(url, body, headers, options)
|
||||||
local ev, id, par
|
local ev, id, data, info
|
||||||
repeat
|
repeat
|
||||||
ev, id, par = event.pull("http_response", "http_failure")
|
ev, id, data, info = event.pull("http_response", "http_failure")
|
||||||
until id == requestId
|
until id == requestId
|
||||||
|
|
||||||
if ev == "http_failure" then
|
if ev == "http_failure" then
|
||||||
return nil, par
|
return nil, data
|
||||||
end
|
end
|
||||||
|
|
||||||
return par
|
return data, info
|
||||||
end
|
end
|
||||||
|
|
||||||
function http.get(url, headers, options)
|
function http.get(url, headers, options)
|
|
@ -1,79 +0,0 @@
|
||||||
local term = require("term")
|
|
||||||
local expect = require("expect").expect
|
|
||||||
|
|
||||||
function write(sText)
|
|
||||||
expect(1, sText, "string", "number")
|
|
||||||
|
|
||||||
local w, h = term.getSize()
|
|
||||||
local x, y = term.getPos()
|
|
||||||
|
|
||||||
local nLinesPrinted = 0
|
|
||||||
local function newLine()
|
|
||||||
if y + 1 <= h then
|
|
||||||
term.setPos(1, y + 1)
|
|
||||||
else
|
|
||||||
term.setPos(1, h)
|
|
||||||
term.scroll(1)
|
|
||||||
end
|
|
||||||
x, y = term.getPos()
|
|
||||||
nLinesPrinted = nLinesPrinted + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Print the line with proper word wrapping
|
|
||||||
sText = tostring(sText)
|
|
||||||
while #sText > 0 do
|
|
||||||
local whitespace = string.match(sText, "^[ \t]+")
|
|
||||||
if whitespace then
|
|
||||||
-- Print whitespace
|
|
||||||
term.write(whitespace)
|
|
||||||
x, y = term.getPos()
|
|
||||||
sText = string.sub(sText, #whitespace + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local newline = string.match(sText, "^\n")
|
|
||||||
if newline then
|
|
||||||
-- Print newlines
|
|
||||||
newLine()
|
|
||||||
sText = string.sub(sText, 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local text = string.match(sText, "^[^ \t\n]+")
|
|
||||||
if text then
|
|
||||||
sText = string.sub(sText, #text + 1)
|
|
||||||
if #text > w then
|
|
||||||
-- Print a multiline word
|
|
||||||
while #text > 0 do
|
|
||||||
if x > w then
|
|
||||||
newLine()
|
|
||||||
end
|
|
||||||
term.write(text)
|
|
||||||
text = string.sub(text, w - x + 2)
|
|
||||||
x, y = term.getPos()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Print a word normally
|
|
||||||
if x + #text - 1 > w then
|
|
||||||
newLine()
|
|
||||||
end
|
|
||||||
term.write(text)
|
|
||||||
x, y = term.getPos()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nLinesPrinted
|
|
||||||
end
|
|
||||||
|
|
||||||
function print(...)
|
|
||||||
local nLinesPrinted = 0
|
|
||||||
local nLimit = select("#", ...)
|
|
||||||
for n = 1, nLimit do
|
|
||||||
local s = tostring(select(n, ...))
|
|
||||||
if n < nLimit then
|
|
||||||
s = s .. "\t"
|
|
||||||
end
|
|
||||||
nLinesPrinted = nLinesPrinted + write(s)
|
|
||||||
end
|
|
||||||
nLinesPrinted = nLinesPrinted + write("\n")
|
|
||||||
return nLinesPrinted
|
|
||||||
end
|
|
|
@ -1,29 +1,14 @@
|
||||||
local term = require("term")
|
local term = require("term")
|
||||||
local colors = require("colors")
|
local colors = require("colors")
|
||||||
|
|
||||||
term.setSize(51, 19)
|
|
||||||
|
|
||||||
term.setForeground(0x59c9ff)
|
term.setForeground(0x59c9ff)
|
||||||
term.setBackground(colors.black)
|
term.setBackground(colors.black)
|
||||||
term.clear()
|
term.clear()
|
||||||
term.setPos(1, 1)
|
term.setPos(1, 1)
|
||||||
|
|
||||||
print(os.version())
|
term.write(os.version())
|
||||||
|
term.setPos(1, 2)
|
||||||
|
|
||||||
local func, err = loadfile("/bin/shell.lua")
|
dofile("/bin/shell.lua")
|
||||||
if func then
|
|
||||||
while true do
|
|
||||||
local ok, err = pcall(func)
|
|
||||||
if not ok then
|
|
||||||
print(err)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
print(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
print("Press any key to continue...")
|
|
||||||
coroutine.yield("key_down")
|
|
||||||
os.shutdown(false)
|
os.shutdown(false)
|
|
@ -1,6 +1,47 @@
|
||||||
local fs = require("fs")
|
print("Starting CapyOS")
|
||||||
|
|
||||||
|
local term = require("term")
|
||||||
|
local fs = require("fs")
|
||||||
|
local gpu = require("gpu")
|
||||||
|
|
||||||
|
local nPrint = print
|
||||||
|
local function showError(err)
|
||||||
|
nPrint(err)
|
||||||
|
local x, y = term.getPos()
|
||||||
|
term.setForeground(0xff0000)
|
||||||
|
term.setPos(1, y)
|
||||||
|
term.write(err)
|
||||||
|
|
||||||
|
term.setPos(1, y + 1)
|
||||||
|
term.setForeground(0xffffff)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
|
||||||
|
coroutine.yield("key_down")
|
||||||
|
end
|
||||||
|
|
||||||
|
function os.version()
|
||||||
|
return "CapyOS 0.0.1"
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setSize(51, 19)
|
||||||
|
gpu.setScale(2)
|
||||||
|
|
||||||
|
term.setPos(1, 1)
|
||||||
|
term.write(_HOST)
|
||||||
|
|
||||||
local files = fs.list("/boot")
|
local files = fs.list("/boot")
|
||||||
for k, v in ipairs(files) do
|
for k, v in ipairs(files) do
|
||||||
dofile("/boot/" .. v)
|
local func, err = loadfile("/boot/" .. v)
|
||||||
|
if not func then
|
||||||
|
showError(err)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = pcall(func)
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
showError(debug.traceback(err))
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -1,3 +1,5 @@
|
||||||
|
local expect = require("expect")
|
||||||
|
|
||||||
local palette = {
|
local palette = {
|
||||||
{
|
{
|
||||||
"white",
|
"white",
|
||||||
|
@ -65,29 +67,28 @@ local palette = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
--[[local colors = {
|
|
||||||
white = 0xf0f0f0,
|
|
||||||
orange = 0xf2b233,
|
|
||||||
magenta = 0xe57fd8,
|
|
||||||
lightBlue = 0x99b2f2,
|
|
||||||
yellow = 0xdede6c,
|
|
||||||
lime = 0x7fcc19,
|
|
||||||
pink = 0xf2b2cc,
|
|
||||||
gray = 0x4c4c4c,
|
|
||||||
lightGray = 0x999999,
|
|
||||||
cyan = 0x4c99b2,
|
|
||||||
purple = 0xb266e5,
|
|
||||||
blue = 0x3366cc,
|
|
||||||
brown = 0x7f664c,
|
|
||||||
green = 0x57a64e,
|
|
||||||
red = 0xcc4c4c,
|
|
||||||
black = 0x111111,
|
|
||||||
}]]
|
|
||||||
|
|
||||||
local colors = {}
|
local colors = {}
|
||||||
for k, v in ipairs(palette) do
|
for k, v in ipairs(palette) do
|
||||||
colors[v[1]] = v[2]
|
colors[v[1]] = v[2]
|
||||||
colors[k] = v[2]
|
colors[k] = v[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function colors.packRGB(r, g, b)
|
||||||
|
expect(1, r, "number")
|
||||||
|
expect(2, g, "number")
|
||||||
|
expect(3, b, "number")
|
||||||
|
|
||||||
|
return (r << 16) +
|
||||||
|
(g << 8) +
|
||||||
|
b
|
||||||
|
end
|
||||||
|
|
||||||
|
function colors.unpackRGB(rgb)
|
||||||
|
expect(1, rgb, "number")
|
||||||
|
|
||||||
|
return (rgb >> 16) & 0xff,
|
||||||
|
(rgb >> 8) & 0xff,
|
||||||
|
rgb & 0xff
|
||||||
|
end
|
||||||
|
|
||||||
return colors;
|
return colors;
|
|
@ -1,123 +1,52 @@
|
||||||
--[[- The @{cc.expect} library provides helper functions for verifying that
|
-- Credits: https://github.com/Ocawesome101/recrafted
|
||||||
function arguments are well-formed and of the correct type.
|
|
||||||
|
|
||||||
@module cc.expect
|
-- cc.expect
|
||||||
@since 1.84.0
|
|
||||||
@changed 1.96.0 The module can now be called directly as a function, which wraps around `expect.expect`.
|
|
||||||
@usage Define a basic function and check it has the correct arguments.
|
|
||||||
|
|
||||||
local expect = require "cc.expect"
|
local _expect = {}
|
||||||
local expect, field = expect.expect, expect.field
|
|
||||||
|
|
||||||
local function add_person(name, info)
|
local function checkType(index, valueType, value, ...)
|
||||||
expect(1, name, "string")
|
local expected = table.pack(...)
|
||||||
expect(2, info, "table", "nil")
|
local isType = false
|
||||||
|
|
||||||
if info then
|
for i = 1, expected.n, 1 do
|
||||||
print("Got age=", field(info, "age", "number"))
|
if type(value) == expected[i] then
|
||||||
print("Got gender=", field(info, "gender", "string", "nil"))
|
isType = true
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
add_person("Anastazja") -- `info' is optional
|
if not isType then
|
||||||
add_person("Kion", { age = 23 }) -- `gender' is optional
|
error(string.format("bad %s %s (%s expected, got %s)", valueType,
|
||||||
add_person("Caoimhin", { age = 23, gender = true }) -- error!
|
index, table.concat(expected, " or "), type(value)), 3)
|
||||||
]]
|
|
||||||
|
|
||||||
local native_select, native_type = select, type
|
|
||||||
|
|
||||||
local function get_type_names(...)
|
|
||||||
local types = table.pack(...)
|
|
||||||
for i = types.n, 1, -1 do
|
|
||||||
if types[i] == "nil" then table.remove(types, i) end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if #types <= 1 then
|
return value
|
||||||
return tostring(...)
|
end
|
||||||
else
|
|
||||||
return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
|
function _expect.expect(index, value, ...)
|
||||||
|
return checkType(("#%d"):format(index), "argument", value, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _expect.field(tbl, index, ...)
|
||||||
|
_expect.expect(1, tbl, "table")
|
||||||
|
_expect.expect(2, index, "string")
|
||||||
|
return checkType(("%q"):format(index), "field", tbl[index], ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _expect.range(num, min, max)
|
||||||
|
_expect.expect(1, num, "number")
|
||||||
|
_expect.expect(2, min, "number", "nil")
|
||||||
|
_expect.expect(3, max, "number", "nil")
|
||||||
|
min = min or -math.huge
|
||||||
|
max = max or math.huge
|
||||||
|
if num < min or num > max then
|
||||||
|
error(("number outside of range (expected %d to be within %d and %d")
|
||||||
|
:format(num, min, max), 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Expect an argument to have a specific type.
|
setmetatable(_expect, { __call = function(_, ...)
|
||||||
--
|
return _expect.expect(...)
|
||||||
-- @tparam number index The 1-based argument index.
|
end })
|
||||||
-- @param value The argument's value.
|
|
||||||
-- @tparam string ... The allowed types of the argument.
|
|
||||||
-- @return The given `value`.
|
|
||||||
-- @throws If the value is not one of the allowed types.
|
|
||||||
local function expect(index, value, ...)
|
|
||||||
local t = native_type(value)
|
|
||||||
for i = 1, native_select("#", ...) do
|
|
||||||
if t == native_select(i, ...) then return value end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If we can determine the function name with a high level of confidence, try to include it.
|
return _expect
|
||||||
local name
|
|
||||||
local ok, info = pcall(debug.getinfo, 3, "nS")
|
|
||||||
if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end
|
|
||||||
|
|
||||||
local type_names = get_type_names(...)
|
|
||||||
if name then
|
|
||||||
error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3)
|
|
||||||
else
|
|
||||||
error(("bad argument #%d (expected %s, got %s)"):format(index, type_names, t), 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Expect an field to have a specific type.
|
|
||||||
--
|
|
||||||
-- @tparam table tbl The table to index.
|
|
||||||
-- @tparam string index The field name to check.
|
|
||||||
-- @tparam string ... The allowed types of the argument.
|
|
||||||
-- @return The contents of the given field.
|
|
||||||
-- @throws If the field is not one of the allowed types.
|
|
||||||
local function field(tbl, index, ...)
|
|
||||||
expect(1, tbl, "table")
|
|
||||||
expect(2, index, "string")
|
|
||||||
|
|
||||||
local value = tbl[index]
|
|
||||||
local t = native_type(value)
|
|
||||||
for i = 1, native_select("#", ...) do
|
|
||||||
if t == native_select(i, ...) then return value end
|
|
||||||
end
|
|
||||||
|
|
||||||
if value == nil then
|
|
||||||
error(("field '%s' missing from table"):format(index), 3)
|
|
||||||
else
|
|
||||||
error(("bad field '%s' (expected %s, got %s)"):format(index, get_type_names(...), t), 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_nan(num)
|
|
||||||
return num ~= num
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Expect a number to be within a specific range.
|
|
||||||
--
|
|
||||||
-- @tparam number num The value to check.
|
|
||||||
-- @tparam number min The minimum value, if nil then `-math.huge` is used.
|
|
||||||
-- @tparam number max The maximum value, if nil then `math.huge` is used.
|
|
||||||
-- @return The given `value`.
|
|
||||||
-- @throws If the value is outside of the allowed range.
|
|
||||||
-- @since 1.96.0
|
|
||||||
local function range(num, min, max)
|
|
||||||
expect(1, num, "number")
|
|
||||||
min = expect(2, min, "number", "nil") or -math.huge
|
|
||||||
max = expect(3, max, "number", "nil") or math.huge
|
|
||||||
if min > max then
|
|
||||||
error("min must be less than or equal to max)", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_nan(num) or num < min or num > max then
|
|
||||||
error(("number outside of range (expected %s to be within %s and %s)"):format(num, min, max), 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
return num
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable({
|
|
||||||
expect = expect,
|
|
||||||
field = field,
|
|
||||||
range = range,
|
|
||||||
}, { __call = function(_, ...) return expect(...) end })
|
|
||||||
|
|
|
@ -4,280 +4,279 @@ local term = require("term")
|
||||||
|
|
||||||
local io = {}
|
local io = {}
|
||||||
|
|
||||||
function io.read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
function io.write(text)
|
||||||
expect(1, _sReplaceChar, "string", "nil")
|
text = tostring(text)
|
||||||
expect(2, _tHistory, "table", "nil")
|
|
||||||
expect(3, _fnComplete, "function", "nil")
|
local lines = 0
|
||||||
expect(4, _sDefault, "string", "nil")
|
local w, h = term.getSize()
|
||||||
|
|
||||||
|
local function inc_cy(cy)
|
||||||
|
lines = lines + 1
|
||||||
|
|
||||||
|
if cy > h - 1 then
|
||||||
|
term.scroll(1)
|
||||||
|
return cy
|
||||||
|
else
|
||||||
|
return cy + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while #text > 0 do
|
||||||
|
local nl = text:find("\n") or #text
|
||||||
|
local chunk = text:sub(1, nl)
|
||||||
|
text = text:sub(#chunk + 1)
|
||||||
|
|
||||||
|
local has_nl = chunk:sub(-1) == "\n"
|
||||||
|
if has_nl then chunk = chunk:sub(1, -2) end
|
||||||
|
|
||||||
|
local cx, cy = term.getPos()
|
||||||
|
while #chunk > 0 do
|
||||||
|
if cx > w then
|
||||||
|
term.setPos(1, inc_cy(cy))
|
||||||
|
cx, cy = term.getPos()
|
||||||
|
end
|
||||||
|
|
||||||
|
local to_write = chunk:sub(1, w - cx + 1)
|
||||||
|
term.write(to_write)
|
||||||
|
|
||||||
|
chunk = chunk:sub(#to_write + 1)
|
||||||
|
cx, cy = term.getPos()
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_nl then
|
||||||
|
term.setPos(1, inc_cy(cy))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
|
||||||
|
local empty = {}
|
||||||
|
function io.read(replace, history, complete, default)
|
||||||
|
expect(1, replace, "string", "nil")
|
||||||
|
expect(2, history, "table", "nil")
|
||||||
|
expect(3, complete, "function", "nil")
|
||||||
|
expect(4, default, "string", "nil")
|
||||||
|
|
||||||
|
if replace then replace = replace:sub(1, 1) end
|
||||||
|
local hist = history or {}
|
||||||
|
history = {}
|
||||||
|
for i = 1, #hist, 1 do
|
||||||
|
history[i] = hist[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
local buffer = default or ""
|
||||||
|
local prev_buf = buffer
|
||||||
|
history[#history + 1] = buffer
|
||||||
|
|
||||||
|
local hist_pos = #history
|
||||||
|
local cursor_pos = 0
|
||||||
|
|
||||||
|
local stx, sty = term.getPos()
|
||||||
|
local w, h = term.getSize()
|
||||||
|
|
||||||
|
local dirty = false
|
||||||
|
local completions = {}
|
||||||
|
local comp_id = 0
|
||||||
|
|
||||||
|
local function clearCompletion()
|
||||||
|
if completions[comp_id] then
|
||||||
|
write((" "):rep(#completions[comp_id]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function full_redraw(force)
|
||||||
|
if force or dirty then
|
||||||
|
if complete and buffer ~= prev_buf then
|
||||||
|
completions = complete(buffer) or empty
|
||||||
|
comp_id = math.min(1, #completions)
|
||||||
|
end
|
||||||
|
prev_buf = buffer
|
||||||
|
|
||||||
|
term.setPos(stx, sty)
|
||||||
|
local text = buffer
|
||||||
|
if replace then text = replace:rep(#text) end
|
||||||
|
local ln = io.write(text)
|
||||||
|
|
||||||
|
if completions[comp_id] then
|
||||||
|
local oldfg = term.getForeground()
|
||||||
|
local oldbg = term.getBackground()
|
||||||
|
term.setForeground(colors.white)
|
||||||
|
term.setBackground(colors.gray)
|
||||||
|
ln = ln + write(completions[comp_id])
|
||||||
|
term.setForeground(oldfg)
|
||||||
|
term.setBackground(oldbg)
|
||||||
|
else
|
||||||
|
ln = ln + io.write(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
if sty + ln > h then
|
||||||
|
sty = sty - (sty + ln - h)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set cursor to the appropriate spot
|
||||||
|
local cx, cy = stx, sty
|
||||||
|
cx = cx + #buffer - cursor_pos -- + #(completions[comp_id] or "")
|
||||||
|
while cx > w do
|
||||||
|
cx = cx - w
|
||||||
|
cy = cy + 1
|
||||||
|
end
|
||||||
|
term.setPos(cx, cy)
|
||||||
|
end
|
||||||
|
|
||||||
term.setBlink(true)
|
term.setBlink(true)
|
||||||
|
|
||||||
local sLine
|
|
||||||
if type(_sDefault) == "string" then
|
|
||||||
sLine = _sDefault
|
|
||||||
else
|
|
||||||
sLine = ""
|
|
||||||
end
|
|
||||||
local nHistoryPos
|
|
||||||
local nPos, nScroll = #sLine, 0
|
|
||||||
if _sReplaceChar then
|
|
||||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local tCompletions
|
|
||||||
local nCompletion
|
|
||||||
local function recomplete()
|
|
||||||
if _fnComplete and nPos == #sLine then
|
|
||||||
tCompletions = _fnComplete(sLine)
|
|
||||||
if tCompletions and #tCompletions > 0 then
|
|
||||||
nCompletion = 1
|
|
||||||
else
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
tCompletions = nil
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function uncomplete()
|
|
||||||
tCompletions = nil
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local w = term.getSize()
|
|
||||||
local sx = term.getPos()
|
|
||||||
|
|
||||||
local function redraw(_bClear)
|
|
||||||
local cursor_pos = nPos - nScroll
|
|
||||||
if sx + cursor_pos >= w then
|
|
||||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
|
||||||
nScroll = sx + nPos - w
|
|
||||||
elseif cursor_pos < 0 then
|
|
||||||
-- We've moved beyond the LHS, ensure we're on the edge.
|
|
||||||
nScroll = nPos
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
term.setPos(sx, cy)
|
|
||||||
local sReplace = _bClear and " " or _sReplaceChar
|
|
||||||
if sReplace then
|
|
||||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
|
||||||
else
|
|
||||||
term.write(string.sub(sLine, nScroll + 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if nCompletion then
|
|
||||||
local sCompletion = tCompletions[nCompletion]
|
|
||||||
local oldText, oldBg
|
|
||||||
if not _bClear then
|
|
||||||
oldText = term.getForeground()
|
|
||||||
oldBg = term.getBackground()
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.setBackground(colors.gray)
|
|
||||||
end
|
|
||||||
if sReplace then
|
|
||||||
term.write(string.rep(sReplace, #sCompletion))
|
|
||||||
else
|
|
||||||
term.write(sCompletion)
|
|
||||||
end
|
|
||||||
if not _bClear then
|
|
||||||
term.setForeground(oldText)
|
|
||||||
term.setBackground(oldBg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setPos(sx + nPos - nScroll, cy)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function clear()
|
|
||||||
redraw(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
local function acceptCompletion()
|
|
||||||
if nCompletion then
|
|
||||||
-- Clear
|
|
||||||
clear()
|
|
||||||
|
|
||||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
|
||||||
local sCompletion = tCompletions[nCompletion]
|
|
||||||
sLine = sLine .. sCompletion
|
|
||||||
nPos = #sLine
|
|
||||||
|
|
||||||
-- Redraw
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while true do
|
while true do
|
||||||
local sEvent, param, param1, param2 = event.pull()
|
full_redraw()
|
||||||
if sEvent == "char" then
|
-- get input
|
||||||
-- Typed key
|
local evt, par1, par2 = event.pull()
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos + 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif sEvent == "paste" then
|
if evt == "char" then
|
||||||
-- Pasted text
|
dirty = true
|
||||||
clear()
|
clearCompletion()
|
||||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
if cursor_pos == 0 then
|
||||||
nPos = nPos + #param
|
buffer = buffer .. par1
|
||||||
recomplete()
|
elseif cursor_pos == #buffer then
|
||||||
redraw()
|
buffer = par1 .. buffer
|
||||||
|
|
||||||
elseif sEvent == "key_down" then
|
|
||||||
if param1 == "enter" then
|
|
||||||
-- Enter/Numpad Enter
|
|
||||||
if nCompletion then
|
|
||||||
clear()
|
|
||||||
uncomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
break
|
|
||||||
|
|
||||||
elseif param1 == "left" then
|
|
||||||
-- Left
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
nPos = nPos - 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param1 == "right" then
|
|
||||||
-- Right
|
|
||||||
if nPos < #sLine then
|
|
||||||
-- Move right
|
|
||||||
clear()
|
|
||||||
nPos = nPos + 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
else
|
else
|
||||||
-- Accept autocomplete
|
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
||||||
acceptCompletion()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif param1 == "up" or param1 == "down" then
|
elseif evt == "paste" then
|
||||||
-- Up or down
|
dirty = true
|
||||||
if nCompletion then
|
clearCompletion()
|
||||||
-- Cycle completions
|
if cursor_pos == 0 then
|
||||||
clear()
|
buffer = buffer .. par1
|
||||||
if param == "up" then
|
elseif cursor_pos == #buffer then
|
||||||
nCompletion = nCompletion - 1
|
buffer = par1 .. buffer
|
||||||
if nCompletion < 1 then
|
|
||||||
nCompletion = #tCompletions
|
|
||||||
end
|
|
||||||
elseif param == "down" then
|
|
||||||
nCompletion = nCompletion + 1
|
|
||||||
if nCompletion > #tCompletions then
|
|
||||||
nCompletion = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif _tHistory then
|
|
||||||
-- Cycle history
|
|
||||||
clear()
|
|
||||||
if param1 == "up" then
|
|
||||||
-- Up
|
|
||||||
if nHistoryPos == nil then
|
|
||||||
if #_tHistory > 0 then
|
|
||||||
nHistoryPos = #_tHistory
|
|
||||||
end
|
|
||||||
elseif nHistoryPos > 1 then
|
|
||||||
nHistoryPos = nHistoryPos - 1
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
-- Down
|
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 ..
|
||||||
if nHistoryPos == #_tHistory then
|
buffer:sub(-cursor_pos + (#par1 - 1))
|
||||||
nHistoryPos = nil
|
|
||||||
elseif nHistoryPos ~= nil then
|
|
||||||
nHistoryPos = nHistoryPos + 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
elseif evt == "key_down" then
|
||||||
|
if par2 == "back" and #buffer > 0 then
|
||||||
|
dirty = true
|
||||||
|
if cursor_pos == 0 then
|
||||||
|
buffer = buffer:sub(1, -2)
|
||||||
|
clearCompletion()
|
||||||
|
elseif cursor_pos < #buffer then
|
||||||
|
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
|
||||||
end
|
end
|
||||||
if nHistoryPos then
|
|
||||||
sLine = _tHistory[nHistoryPos]
|
elseif par2 == "delete" and cursor_pos > 0 then
|
||||||
nPos, nScroll = #sLine, 0
|
dirty = true
|
||||||
|
|
||||||
|
if cursor_pos == #buffer then
|
||||||
|
buffer = buffer:sub(2)
|
||||||
|
elseif cursor_pos == 1 then
|
||||||
|
buffer = buffer:sub(1, -2)
|
||||||
else
|
else
|
||||||
sLine = ""
|
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||||
nPos, nScroll = 0, 0
|
|
||||||
end
|
end
|
||||||
uncomplete()
|
cursor_pos = cursor_pos - 1
|
||||||
redraw()
|
|
||||||
|
|
||||||
|
elseif par2 == "up" then
|
||||||
|
if #completions > 1 then
|
||||||
|
dirty = true
|
||||||
|
clearCompletion()
|
||||||
|
if comp_id > 1 then
|
||||||
|
comp_id = comp_id - 1
|
||||||
|
else
|
||||||
|
comp_id = #completions
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif param1 == "back" then
|
elseif hist_pos > 1 then
|
||||||
-- Backspace
|
cursor_pos = 0
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
history[hist_pos] = buffer
|
||||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
hist_pos = hist_pos - 1
|
||||||
nPos = nPos - 1
|
|
||||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
buffer = (" "):rep(#buffer)
|
||||||
recomplete()
|
full_redraw(true)
|
||||||
redraw()
|
|
||||||
|
buffer = history[hist_pos]
|
||||||
|
dirty = true
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif param1 == "home" then
|
elseif par2 == "down" then
|
||||||
-- Home
|
if #completions > 1 then
|
||||||
if nPos > 0 then
|
dirty = true
|
||||||
clear()
|
clearCompletion()
|
||||||
nPos = 0
|
if comp_id < #completions then
|
||||||
recomplete()
|
comp_id = comp_id + 1
|
||||||
redraw()
|
else
|
||||||
|
comp_id = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif param1 == "delete" then
|
elseif hist_pos < #history then
|
||||||
-- Delete
|
cursor_pos = 0
|
||||||
if nPos < #sLine then
|
|
||||||
clear()
|
history[hist_pos] = buffer
|
||||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
hist_pos = hist_pos + 1
|
||||||
recomplete()
|
|
||||||
redraw()
|
buffer = (" "):rep(#buffer)
|
||||||
|
full_redraw(true)
|
||||||
|
|
||||||
|
buffer = history[hist_pos]
|
||||||
|
dirty = true
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif param1 == "end" then
|
elseif par2 == "left" then
|
||||||
-- End
|
if cursor_pos < #buffer then
|
||||||
if nPos < #sLine then
|
clearCompletion()
|
||||||
clear()
|
cursor_pos = cursor_pos + 1
|
||||||
nPos = #sLine
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif param1 == "tab" then
|
elseif par2 == "right" then
|
||||||
-- Tab (accept autocomplete)
|
if cursor_pos > 0 then
|
||||||
acceptCompletion()
|
cursor_pos = cursor_pos - 1
|
||||||
|
|
||||||
|
elseif comp_id > 0 then
|
||||||
|
dirty = true
|
||||||
|
buffer = buffer .. completions[comp_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif sEvent == "mouse_down" or sEvent == "mouse_move" and param == 1 then
|
elseif par2 == "tab" then
|
||||||
local _, cy = term.getPos()
|
if comp_id > 0 then
|
||||||
if param1 >= sx and param1 <= w and param2 == cy then
|
dirty = true
|
||||||
-- Ensure we don't scroll beyond the current line
|
buffer = buffer .. completions[comp_id]
|
||||||
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
|
||||||
redraw()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif sEvent == "term_resize" then
|
elseif par2 == "home" then
|
||||||
-- Terminal resized
|
cursor_pos = #buffer
|
||||||
w = term.getSize()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
end
|
elseif par2 == "end" then
|
||||||
end
|
cursor_pos = 0
|
||||||
|
|
||||||
local _, cy = term.getPos()
|
elseif par2 == "enter" then
|
||||||
term.setBlink(false)
|
clearCompletion()
|
||||||
term.setPos(w + 1, cy)
|
|
||||||
print()
|
print()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return sLine
|
term.setBlink(false)
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
io.stderr = {}
|
||||||
|
|
||||||
|
function io.stderr.write(text)
|
||||||
|
local fg = term.getForeground()
|
||||||
|
term.setForeground(0xff0000)
|
||||||
|
io.write(text)
|
||||||
|
term.setForeground(fg)
|
||||||
|
end
|
||||||
|
|
||||||
|
function io.stderr.print(...)
|
||||||
|
local fg = term.getForeground()
|
||||||
|
term.setForeground(0xff0000)
|
||||||
|
print(...)
|
||||||
|
term.setForeground(fg)
|
||||||
end
|
end
|
||||||
|
|
||||||
return io
|
return io
|
61
Capy64/Assets/Lua/lib/parallel.lua
Normal file
61
Capy64/Assets/Lua/lib/parallel.lua
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
local expect = require("expect")
|
||||||
|
|
||||||
|
local parallel = {}
|
||||||
|
|
||||||
|
local function contains(array, value)
|
||||||
|
for k, v in pairs(array) do
|
||||||
|
if v == value then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run(threads, exitOnAny)
|
||||||
|
local alive = #threads
|
||||||
|
local filters = {}
|
||||||
|
local ev = {}
|
||||||
|
while true do
|
||||||
|
for i, thread in pairs(threads) do
|
||||||
|
if not filters[i] or #filters[i] == 0 or contains(filters[i], ev[1]) or ev[1] == "interrupt" then
|
||||||
|
local pars = table.pack(coroutine.resume(thread, table.unpack(ev)))
|
||||||
|
if pars[1] then
|
||||||
|
filters[i] = table.pack(table.unpack(pars, 2))
|
||||||
|
else
|
||||||
|
error(pars[2], 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if coroutine.status(thread) == "dead" then
|
||||||
|
alive = alive - 1
|
||||||
|
if exitOnAny or alive <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ev = table.pack(coroutine.yield())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parallel.waitForAll(...)
|
||||||
|
local threads = {}
|
||||||
|
for k, v in ipairs({ ... }) do
|
||||||
|
expect(k, v, "function")
|
||||||
|
table.insert(threads, coroutine.create(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
return run(threads, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
function parallel.waitForAny(...)
|
||||||
|
local threads = {}
|
||||||
|
for k, v in ipairs({ ... }) do
|
||||||
|
expect(k, v, "function")
|
||||||
|
table.insert(threads, coroutine.create(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
return run(threads, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
return parallel
|
|
@ -40,10 +40,12 @@ function UTFString:find(pattern, init, plain)
|
||||||
expect(1, pattern, "string", "table")
|
expect(1, pattern, "string", "table")
|
||||||
expect(2, init, "number", "nil")
|
expect(2, init, "number", "nil")
|
||||||
if type(pattern) == "table" then
|
if type(pattern) == "table" then
|
||||||
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)", 2) end
|
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)",
|
||||||
|
2) end
|
||||||
pattern = pattern.str
|
pattern = pattern.str
|
||||||
else pattern = toUTF8(pattern) end
|
else pattern = toUTF8(pattern) end
|
||||||
pattern = string.gsub(string.gsub(pattern, "(.)%.", function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
pattern = string.gsub(string.gsub(pattern, "(.)%.",
|
||||||
|
function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
||||||
local s, e = string.find(self.str, pattern, init and utf8.offset(self.str, init), plain)
|
local s, e = string.find(self.str, pattern, init and utf8.offset(self.str, init), plain)
|
||||||
if not s then return nil end
|
if not s then return nil end
|
||||||
return utf8.len(string.sub(self.str, 1, s - 1)) + 1, utf8.len(string.sub(self.str, 1, e - 1)) + 1
|
return utf8.len(string.sub(self.str, 1, s - 1)) + 1, utf8.len(string.sub(self.str, 1, e - 1)) + 1
|
||||||
|
@ -61,10 +63,12 @@ end
|
||||||
function UTFString:gmatch(pattern)
|
function UTFString:gmatch(pattern)
|
||||||
expect(1, pattern, "string", "table")
|
expect(1, pattern, "string", "table")
|
||||||
if type(pattern) == "table" then
|
if type(pattern) == "table" then
|
||||||
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)", 2) end
|
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)",
|
||||||
|
2) end
|
||||||
pattern = pattern.str
|
pattern = pattern.str
|
||||||
else pattern = toUTF8(pattern) end
|
else pattern = toUTF8(pattern) end
|
||||||
pattern = string.gsub(string.gsub(pattern, "(.)%.", function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
pattern = string.gsub(string.gsub(pattern, "(.)%.",
|
||||||
|
function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
||||||
local iter = string.gmatch(self.str, pattern)
|
local iter = string.gmatch(self.str, pattern)
|
||||||
return function()
|
return function()
|
||||||
local matches = table.pack(iter())
|
local matches = table.pack(iter())
|
||||||
|
@ -82,12 +86,14 @@ function UTFString:gsub(pattern, repl, n)
|
||||||
expect(2, repl, "string", "table", "function")
|
expect(2, repl, "string", "table", "function")
|
||||||
expect(3, n, "number", "nil")
|
expect(3, n, "number", "nil")
|
||||||
if type(pattern) == "table" then
|
if type(pattern) == "table" then
|
||||||
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)", 2) end
|
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)",
|
||||||
|
2) end
|
||||||
pattern = pattern.str
|
pattern = pattern.str
|
||||||
else pattern = toUTF8(pattern) end
|
else pattern = toUTF8(pattern) end
|
||||||
if type(repl) == "table" and getmetatable(repl) == UTFString_mt then repl = repl.str
|
if type(repl) == "table" and getmetatable(repl) == UTFString_mt then repl = repl.str
|
||||||
elseif type(repl) == "string" then repl = toUTF8(repl) end
|
elseif type(repl) == "string" then repl = toUTF8(repl) end
|
||||||
pattern = string.gsub(string.gsub(pattern, "(.)%.", function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
pattern = string.gsub(string.gsub(pattern, "(.)%.",
|
||||||
|
function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
||||||
local s, total = string.gsub(self.str, pattern, repl, n)
|
local s, total = string.gsub(self.str, pattern, repl, n)
|
||||||
return UTFString(s), total
|
return UTFString(s), total
|
||||||
end
|
end
|
||||||
|
@ -111,10 +117,12 @@ function UTFString:match(pattern, init)
|
||||||
expect(1, pattern, "string", "table")
|
expect(1, pattern, "string", "table")
|
||||||
expect(2, init, "number", "nil")
|
expect(2, init, "number", "nil")
|
||||||
if type(pattern) == "table" then
|
if type(pattern) == "table" then
|
||||||
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)", 2) end
|
if getmetatable(pattern) ~= UTFString_mt then error("bad argument #1 (expected string or UTFString, got table)",
|
||||||
|
2) end
|
||||||
pattern = pattern.str
|
pattern = pattern.str
|
||||||
else pattern = toUTF8(pattern) end
|
else pattern = toUTF8(pattern) end
|
||||||
pattern = string.gsub(string.gsub(pattern, "(.)%.", function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
pattern = string.gsub(string.gsub(pattern, "(.)%.",
|
||||||
|
function(s) if s == "%" then return "%." else return s .. utf8.charpattern end end), "^%.", utf8.charpattern)
|
||||||
local matches = table.pack(string.match(self.str, pattern, init and utf8.offset(self.str, init)))
|
local matches = table.pack(string.match(self.str, pattern, init and utf8.offset(self.str, init)))
|
||||||
for i = 1, matches.n do
|
for i = 1, matches.n do
|
||||||
local tt = type(matches[i])
|
local tt = type(matches[i])
|
||||||
|
@ -191,8 +199,10 @@ function UTFString_mt.__concat(a, b)
|
||||||
if type(a) == "string" then return UTFString(a .. b.str)
|
if type(a) == "string" then return UTFString(a .. b.str)
|
||||||
elseif type(b) == "string" then return UTFString(a.str .. b)
|
elseif type(b) == "string" then return UTFString(a.str .. b)
|
||||||
else
|
else
|
||||||
if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to concatenate " .. type(a) .. " and UTFString", 2)
|
if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to concatenate " ..
|
||||||
elseif type(b) ~= "table" or getmetatable(b) ~= UTFString_mt then error("attempt to concatenate UTFString and " .. type(b), 2) end
|
type(a) .. " and UTFString", 2)
|
||||||
|
elseif type(b) ~= "table" or getmetatable(b) ~= UTFString_mt then error("attempt to concatenate UTFString and "
|
||||||
|
.. type(b), 2) end
|
||||||
return UTFString(a.str .. b.str)
|
return UTFString(a.str .. b.str)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -208,8 +218,10 @@ function UTFString_mt.__lt(a, b)
|
||||||
if type(a) == "string" then return a < b.str
|
if type(a) == "string" then return a < b.str
|
||||||
elseif type(b) == "string" then return a.str < b
|
elseif type(b) == "string" then return a.str < b
|
||||||
else
|
else
|
||||||
if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " .. type(a) .. " and UTFString", 2)
|
if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " ..
|
||||||
elseif type(b) ~= "table" or getmetatable(b) ~= UTFString_mt then error("attempt to compare UTFString and " .. type(b), 2) end
|
type(a) .. " and UTFString", 2)
|
||||||
|
elseif type(b) ~= "table" or getmetatable(b) ~= UTFString_mt then error("attempt to compare UTFString and " ..
|
||||||
|
type(b), 2) end
|
||||||
return a.str < b.str
|
return a.str < b.str
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -218,8 +230,10 @@ function UTFString_mt.__le(a, b)
|
||||||
if type(a) == "string" then return a <= b.str
|
if type(a) == "string" then return a <= b.str
|
||||||
elseif type(b) == "string" then return a.str <= b
|
elseif type(b) == "string" then return a.str <= b
|
||||||
else
|
else
|
||||||
if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " .. type(a) .. " and UTFString", 2)
|
if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " ..
|
||||||
elseif type(b) ~= "table" or getmetatable(b) ~= UTFString_mt then error("attempt to compare UTFString and " .. type(b), 2) end
|
type(a) .. " and UTFString", 2)
|
||||||
|
elseif type(b) ~= "table" or getmetatable(b) ~= UTFString_mt then error("attempt to compare UTFString and " ..
|
||||||
|
type(b), 2) end
|
||||||
return a.str <= b.str
|
return a.str <= b.str
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue