From bc87363f7cd231234b00f725b3a0adec53d1b085 Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Fri, 27 Jan 2023 21:59:22 +0100 Subject: [PATCH] Update CapyOS --- Capy64/Assets/Lua/bin/exit.lua | 2 +- Capy64/Assets/Lua/bin/fun/mandelbrot.lua | 94 ++++ Capy64/Assets/Lua/bin/fun/melt.lua | 73 +++ Capy64/Assets/Lua/bin/hello.lua | 4 +- Capy64/Assets/Lua/bin/ls.lua | 4 +- Capy64/Assets/Lua/bin/lua.lua | 17 +- Capy64/Assets/Lua/bin/mkdir.lua | 4 +- Capy64/Assets/Lua/bin/pwd.lua | 2 +- Capy64/Assets/Lua/bin/reboot.lua | 2 +- Capy64/Assets/Lua/bin/rm.lua | 4 +- Capy64/Assets/Lua/bin/shell.lua | 40 +- Capy64/Assets/Lua/bin/shutdown.lua | 2 +- Capy64/Assets/Lua/bin/version.lua | 1 + Capy64/Assets/Lua/bin/wget.lua | 12 +- Capy64/Assets/Lua/boot/00_common.lua | 1 - Capy64/Assets/Lua/boot/00_package.lua | 3 + Capy64/Assets/Lua/boot/01_package.lua | 1 - Capy64/Assets/Lua/boot/01_stdio.lua | 15 + .../Lua/boot/{04_http.lua => 02_http.lua} | 8 +- Capy64/Assets/Lua/boot/02_stdio.lua | 79 --- .../Lua/boot/{03_timer.lua => 02_timer.lua} | 2 +- Capy64/Assets/Lua/boot/99_shell.lua | 23 +- Capy64/Assets/Lua/init.lua | 47 +- Capy64/Assets/Lua/lib/colors.lua | 41 +- Capy64/Assets/Lua/lib/expect.lua | 149 ++---- Capy64/Assets/Lua/lib/io.lua | 483 +++++++++--------- Capy64/Assets/Lua/lib/parallel.lua | 61 +++ Capy64/Assets/Lua/lib/utfstring.lua | 268 +++++----- 28 files changed, 786 insertions(+), 656 deletions(-) create mode 100644 Capy64/Assets/Lua/bin/fun/mandelbrot.lua create mode 100644 Capy64/Assets/Lua/bin/fun/melt.lua create mode 100644 Capy64/Assets/Lua/bin/version.lua delete mode 100644 Capy64/Assets/Lua/boot/00_common.lua create mode 100644 Capy64/Assets/Lua/boot/00_package.lua delete mode 100644 Capy64/Assets/Lua/boot/01_package.lua create mode 100644 Capy64/Assets/Lua/boot/01_stdio.lua rename Capy64/Assets/Lua/boot/{04_http.lua => 02_http.lua} (93%) delete mode 100644 Capy64/Assets/Lua/boot/02_stdio.lua rename Capy64/Assets/Lua/boot/{03_timer.lua => 02_timer.lua} (98%) create mode 100644 Capy64/Assets/Lua/lib/parallel.lua diff --git a/Capy64/Assets/Lua/bin/exit.lua b/Capy64/Assets/Lua/bin/exit.lua index 0aa454a..b90092c 100644 --- a/Capy64/Assets/Lua/bin/exit.lua +++ b/Capy64/Assets/Lua/bin/exit.lua @@ -1 +1 @@ -shell.exit() \ No newline at end of file +shell.exit() diff --git a/Capy64/Assets/Lua/bin/fun/mandelbrot.lua b/Capy64/Assets/Lua/bin/fun/mandelbrot.lua new file mode 100644 index 0000000..1dcbf6c --- /dev/null +++ b/Capy64/Assets/Lua/bin/fun/mandelbrot.lua @@ -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 = 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 diff --git a/Capy64/Assets/Lua/bin/fun/melt.lua b/Capy64/Assets/Lua/bin/fun/melt.lua new file mode 100644 index 0000000..ebf3dd3 --- /dev/null +++ b/Capy64/Assets/Lua/bin/fun/melt.lua @@ -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 = 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) diff --git a/Capy64/Assets/Lua/bin/hello.lua b/Capy64/Assets/Lua/bin/hello.lua index 22eb1bb..de32216 100644 --- a/Capy64/Assets/Lua/bin/hello.lua +++ b/Capy64/Assets/Lua/bin/hello.lua @@ -5,7 +5,7 @@ local term = require("term") local function slowPrint(text, delay) for i = 1, #text do local ch = text:sub(i, i) - write(ch) + io.write(ch) timer.sleep(delay) end print() @@ -14,4 +14,4 @@ end local color = colors[math.random(1, #colors)] term.setForeground(color) -slowPrint("Hello, World!", 50) \ No newline at end of file +slowPrint("Hello, World!", 50) diff --git a/Capy64/Assets/Lua/bin/ls.lua b/Capy64/Assets/Lua/bin/ls.lua index 2590419..58a0607 100644 --- a/Capy64/Assets/Lua/bin/ls.lua +++ b/Capy64/Assets/Lua/bin/ls.lua @@ -14,7 +14,7 @@ if not fs.isDir(dir) then end local files = fs.list(dir) -for k,v in ipairs(files) do +for k, v in ipairs(files) do if fs.isDir(fs.combine(dir, v)) then term.setForeground(colors.lightBlue) print(v .. "/") @@ -22,4 +22,4 @@ for k,v in ipairs(files) do term.setForeground(colors.white) print(v) end -end \ No newline at end of file +end diff --git a/Capy64/Assets/Lua/bin/lua.lua b/Capy64/Assets/Lua/bin/lua.lua index 6a44e18..1fc9038 100644 --- a/Capy64/Assets/Lua/bin/lua.lua +++ b/Capy64/Assets/Lua/bin/lua.lua @@ -19,9 +19,6 @@ local tEnv = { __tostring = function() return "Call exit() to exit." end, __call = function() bRunning = false end, }), - ["_echo"] = function(...) - return ... - end, } setmetatable(tEnv, { __index = _ENV }) @@ -35,9 +32,9 @@ print("Call exit() to exit.") term.setForeground(colours.white) while bRunning do - term.setForeground( colours.yellow ) - write("> ") - term.setForeground( colours.white ) + term.setForeground(colours.yellow) + io.write("> ") + term.setForeground(colours.white) local s = io.read(nil, tCommandHistory) if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then @@ -46,7 +43,7 @@ while bRunning do local nForcePrint = 0 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 func2 then func = func2 @@ -69,10 +66,10 @@ while bRunning do n = n + 1 end else - print(tResults[2]) + io.stderr.print(tResults[2]) end else - print(e) + io.stderr.print(e) end -end \ No newline at end of file +end diff --git a/Capy64/Assets/Lua/bin/mkdir.lua b/Capy64/Assets/Lua/bin/mkdir.lua index 47cd736..614a3e6 100644 --- a/Capy64/Assets/Lua/bin/mkdir.lua +++ b/Capy64/Assets/Lua/bin/mkdir.lua @@ -1,5 +1,5 @@ local fs = require("fs") -local args = {...} +local args = { ... } if #args == 0 then print("Usage: mkdir ") @@ -11,4 +11,4 @@ if fs.exists(dir) then error("Path already exists", 0) end -fs.makeDir(dir) \ No newline at end of file +fs.makeDir(dir) diff --git a/Capy64/Assets/Lua/bin/pwd.lua b/Capy64/Assets/Lua/bin/pwd.lua index 6a30176..b619fb5 100644 --- a/Capy64/Assets/Lua/bin/pwd.lua +++ b/Capy64/Assets/Lua/bin/pwd.lua @@ -1 +1 @@ -print(shell.getDir()) \ No newline at end of file +print(shell.getDir()) diff --git a/Capy64/Assets/Lua/bin/reboot.lua b/Capy64/Assets/Lua/bin/reboot.lua index 5a82260..35ebe35 100644 --- a/Capy64/Assets/Lua/bin/reboot.lua +++ b/Capy64/Assets/Lua/bin/reboot.lua @@ -4,4 +4,4 @@ print("Goodbye!") timer.sleep(1000) -os.shutdown(true) \ No newline at end of file +os.shutdown(true) diff --git a/Capy64/Assets/Lua/bin/rm.lua b/Capy64/Assets/Lua/bin/rm.lua index 9e798fb..428fcad 100644 --- a/Capy64/Assets/Lua/bin/rm.lua +++ b/Capy64/Assets/Lua/bin/rm.lua @@ -1,10 +1,10 @@ local fs = require("fs") -local args = {...} +local args = { ... } if #args == 0 then print("Usage: rm ") return end local file = shell.resolve(args[1]) -fs.delete(file, true) \ No newline at end of file +fs.delete(file, true) diff --git a/Capy64/Assets/Lua/bin/shell.lua b/Capy64/Assets/Lua/bin/shell.lua index e39664f..353589b 100644 --- a/Capy64/Assets/Lua/bin/shell.lua +++ b/Capy64/Assets/Lua/bin/shell.lua @@ -1,6 +1,5 @@ local term = require("term") local colors = require("colors") -local io = require("io") local fs = require("fs") local exit = false @@ -17,17 +16,6 @@ local function buildEnvironment() }, { __index = _G }) 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 sLine = table.concat({ ... }, " ") local tWords = {} @@ -58,6 +46,10 @@ function shell.resolve(path) return fs.combine("", path) end + if path:sub(1, 1) == "~" then + return fs.combine(shell.homePath, path) + end + return fs.combine(currentDir, path) end @@ -67,7 +59,7 @@ function shell.resolveProgram(path) end 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 return resolved end @@ -80,7 +72,7 @@ function shell.run(...) local path = shell.resolveProgram(command) if not path then - printError("Command not found: " .. command) + io.stderr.print("Command not found: " .. command) return false end @@ -89,13 +81,13 @@ function shell.run(...) local func, err = loadfile(path, "t", env) if not func then - printError(err) + io.stderr.print(err) return false end local ok, err = pcall(func, table.unpack(args, 2)) if not ok then - printError(err) + io.stderr.print(err) return false end @@ -106,25 +98,29 @@ function shell.exit() exit = true end +if not fs.exists(shell.homePath) then + fs.makeDir(shell.homePath) +end + local history = {} local lastExecSuccess = true while not exit do + term.setBackground(colors.black) term.setForeground(colors.white) - write(":") + io.write(":") term.setForeground(colors.lightBlue) - local currentDir = shell.getDir() if currentDir == shell.homePath then - write("~") + io.write("~") else - write(currentDir) + io.write(currentDir) end - + if lastExecSuccess then term.setForeground(colors.yellow) else term.setForeground(colors.red) end - write("$ ") + io.write("$ ") term.setForeground(colors.white) local line = io.read(nil, history) diff --git a/Capy64/Assets/Lua/bin/shutdown.lua b/Capy64/Assets/Lua/bin/shutdown.lua index 6bcb27c..7d465ac 100644 --- a/Capy64/Assets/Lua/bin/shutdown.lua +++ b/Capy64/Assets/Lua/bin/shutdown.lua @@ -4,4 +4,4 @@ print("Goodbye!") timer.sleep(1000) -os.shutdown(false) \ No newline at end of file +os.shutdown(false) diff --git a/Capy64/Assets/Lua/bin/version.lua b/Capy64/Assets/Lua/bin/version.lua new file mode 100644 index 0000000..3ec71b4 --- /dev/null +++ b/Capy64/Assets/Lua/bin/version.lua @@ -0,0 +1 @@ +print(string.format("%s @ %s - %s", os.version(), _HOST, _VERSION)) diff --git a/Capy64/Assets/Lua/bin/wget.lua b/Capy64/Assets/Lua/bin/wget.lua index 5eb07ec..594a8d3 100644 --- a/Capy64/Assets/Lua/bin/wget.lua +++ b/Capy64/Assets/Lua/bin/wget.lua @@ -1,7 +1,7 @@ local http = require("http") local fs = require("fs") -local args = {...} +local args = { ... } if not http then error("HTTP is not enabled", 0) @@ -12,21 +12,23 @@ if #args == 0 then return false end -local outputName = args[2] or fs.getName(args[1]) +local outputName = args[2] or fs.getName(args[1]) local outputPath = shell.resolve(outputName) if not http.checkURL(args[1]) then error("Invalid URL", 0) end -local response, err = http.get(args[1]) +local response , err = http.get(args[1], nil, { + binary = true, +}) if not response then error(err, 0) end -local file = fs.open(outputPath, "w") +local file = fs.open(outputPath, "w") file:write(response:readAll()) file:close() response:close() -print("File written to " .. outputPath) \ No newline at end of file +print("File written to " .. outputPath) diff --git a/Capy64/Assets/Lua/boot/00_common.lua b/Capy64/Assets/Lua/boot/00_common.lua deleted file mode 100644 index eb4dd1c..0000000 --- a/Capy64/Assets/Lua/boot/00_common.lua +++ /dev/null @@ -1 +0,0 @@ -os.print = print \ No newline at end of file diff --git a/Capy64/Assets/Lua/boot/00_package.lua b/Capy64/Assets/Lua/boot/00_package.lua new file mode 100644 index 0000000..2cc7dd8 --- /dev/null +++ b/Capy64/Assets/Lua/boot/00_package.lua @@ -0,0 +1,3 @@ +package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path + +_G.io = require("io") diff --git a/Capy64/Assets/Lua/boot/01_package.lua b/Capy64/Assets/Lua/boot/01_package.lua deleted file mode 100644 index 54ac974..0000000 --- a/Capy64/Assets/Lua/boot/01_package.lua +++ /dev/null @@ -1 +0,0 @@ -package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path \ No newline at end of file diff --git a/Capy64/Assets/Lua/boot/01_stdio.lua b/Capy64/Assets/Lua/boot/01_stdio.lua new file mode 100644 index 0000000..32f6864 --- /dev/null +++ b/Capy64/Assets/Lua/boot/01_stdio.lua @@ -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 diff --git a/Capy64/Assets/Lua/boot/04_http.lua b/Capy64/Assets/Lua/boot/02_http.lua similarity index 93% rename from Capy64/Assets/Lua/boot/04_http.lua rename to Capy64/Assets/Lua/boot/02_http.lua index faffa58..9116dac 100644 --- a/Capy64/Assets/Lua/boot/04_http.lua +++ b/Capy64/Assets/Lua/boot/02_http.lua @@ -31,16 +31,16 @@ function http.request(url, body, headers, options) end local requestId = http.requestAsync(url, body, headers, options) - local ev, id, par + local ev, id, data, info repeat - ev, id, par = event.pull("http_response", "http_failure") + ev, id, data, info = event.pull("http_response", "http_failure") until id == requestId if ev == "http_failure" then - return nil, par + return nil, data end - return par + return data, info end function http.get(url, headers, options) diff --git a/Capy64/Assets/Lua/boot/02_stdio.lua b/Capy64/Assets/Lua/boot/02_stdio.lua deleted file mode 100644 index 3ce5655..0000000 --- a/Capy64/Assets/Lua/boot/02_stdio.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Capy64/Assets/Lua/boot/03_timer.lua b/Capy64/Assets/Lua/boot/02_timer.lua similarity index 98% rename from Capy64/Assets/Lua/boot/03_timer.lua rename to Capy64/Assets/Lua/boot/02_timer.lua index be532f4..ae933fc 100644 --- a/Capy64/Assets/Lua/boot/03_timer.lua +++ b/Capy64/Assets/Lua/boot/02_timer.lua @@ -11,4 +11,4 @@ function timer.sleep(n) repeat local _, par = event.pull("timer") until par == timerId -end \ No newline at end of file +end diff --git a/Capy64/Assets/Lua/boot/99_shell.lua b/Capy64/Assets/Lua/boot/99_shell.lua index 1e939bf..fd89fd7 100644 --- a/Capy64/Assets/Lua/boot/99_shell.lua +++ b/Capy64/Assets/Lua/boot/99_shell.lua @@ -1,29 +1,14 @@ local term = require("term") local colors = require("colors") -term.setSize(51, 19) - term.setForeground(0x59c9ff) term.setBackground(colors.black) term.clear() term.setPos(1, 1) -print(os.version()) +term.write(os.version()) +term.setPos(1, 2) -local func, err = loadfile("/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 +dofile("/bin/shell.lua") - - -print("Press any key to continue...") -coroutine.yield("key_down") -os.shutdown(false) \ No newline at end of file +os.shutdown(false) diff --git a/Capy64/Assets/Lua/init.lua b/Capy64/Assets/Lua/init.lua index 560f975..00bc02e 100644 --- a/Capy64/Assets/Lua/init.lua +++ b/Capy64/Assets/Lua/init.lua @@ -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") for k, v in ipairs(files) do - dofile("/boot/" .. v) -end \ No newline at end of file + 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 diff --git a/Capy64/Assets/Lua/lib/colors.lua b/Capy64/Assets/Lua/lib/colors.lua index 5a2aca7..b0fa97d 100644 --- a/Capy64/Assets/Lua/lib/colors.lua +++ b/Capy64/Assets/Lua/lib/colors.lua @@ -1,3 +1,5 @@ +local expect = require("expect") + local palette = { { "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 = {} for k, v in ipairs(palette) do colors[v[1]] = v[2] colors[k] = v[2] end -return colors; \ No newline at end of file +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; diff --git a/Capy64/Assets/Lua/lib/expect.lua b/Capy64/Assets/Lua/lib/expect.lua index 483f757..976928f 100644 --- a/Capy64/Assets/Lua/lib/expect.lua +++ b/Capy64/Assets/Lua/lib/expect.lua @@ -1,123 +1,52 @@ ---[[- The @{cc.expect} library provides helper functions for verifying that -function arguments are well-formed and of the correct type. +-- Credits: https://github.com/Ocawesome101/recrafted -@module 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. +-- cc.expect - local expect = require "cc.expect" - local expect, field = expect.expect, expect.field +local _expect = {} - local function add_person(name, info) - expect(1, name, "string") - expect(2, info, "table", "nil") +local function checkType(index, valueType, value, ...) + local expected = table.pack(...) + local isType = false - if info then - print("Got age=", field(info, "age", "number")) - print("Got gender=", field(info, "gender", "string", "nil")) + for i = 1, expected.n, 1 do + if type(value) == expected[i] then + isType = true + break end end - add_person("Anastazja") -- `info' is optional - add_person("Kion", { age = 23 }) -- `gender' is optional - add_person("Caoimhin", { age = 23, gender = true }) -- error! -]] - -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 + if not isType then + error(string.format("bad %s %s (%s expected, got %s)", valueType, + index, table.concat(expected, " or "), type(value)), 3) end - if #types <= 1 then - return tostring(...) - else - return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types] + return value +end + +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 ---- Expect an argument to have a specific type. --- --- @tparam number index The 1-based argument index. --- @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 +setmetatable(_expect, { __call = function(_, ...) + return _expect.expect(...) +end }) - -- If we can determine the function name with a high level of confidence, try to include it. - 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 }) +return _expect diff --git a/Capy64/Assets/Lua/lib/io.lua b/Capy64/Assets/Lua/lib/io.lua index b4a74d3..7fafadb 100644 --- a/Capy64/Assets/Lua/lib/io.lua +++ b/Capy64/Assets/Lua/lib/io.lua @@ -4,280 +4,279 @@ local term = require("term") local io = {} -function io.read(_sReplaceChar, _tHistory, _fnComplete, _sDefault) - expect(1, _sReplaceChar, "string", "nil") - expect(2, _tHistory, "table", "nil") - expect(3, _fnComplete, "function", "nil") - expect(4, _sDefault, "string", "nil") +function io.write(text) + text = tostring(text) + + local lines = 0 + 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) - 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 - local sEvent, param, param1, param2 = event.pull() - if sEvent == "char" then - -- Typed key - clear() - sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1) - nPos = nPos + 1 - recomplete() - redraw() + full_redraw() + -- get input + local evt, par1, par2 = event.pull() - elseif sEvent == "paste" then - -- Pasted text - clear() - sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1) - nPos = nPos + #param - recomplete() - redraw() + if evt == "char" then + dirty = true + clearCompletion() + if cursor_pos == 0 then + buffer = buffer .. par1 + elseif cursor_pos == #buffer then + buffer = par1 .. buffer + else + buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos) + end - elseif sEvent == "key_down" then - if param1 == "enter" then - -- Enter/Numpad Enter - if nCompletion then - clear() - uncomplete() - redraw() - end - break + elseif evt == "paste" then + dirty = true + clearCompletion() + if cursor_pos == 0 then + buffer = buffer .. par1 + elseif cursor_pos == #buffer then + buffer = par1 .. buffer + else + buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. + buffer:sub(-cursor_pos + (#par1 - 1)) + end - elseif param1 == "left" then - -- Left - if nPos > 0 then - clear() - nPos = nPos - 1 - recomplete() - redraw() + 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 - elseif param1 == "right" then - -- Right - if nPos < #sLine then - -- Move right - clear() - nPos = nPos + 1 - recomplete() - redraw() + elseif par2 == "delete" and cursor_pos > 0 then + dirty = true + + if cursor_pos == #buffer then + buffer = buffer:sub(2) + elseif cursor_pos == 1 then + buffer = buffer:sub(1, -2) else - -- Accept autocomplete - acceptCompletion() + buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1) end + cursor_pos = cursor_pos - 1 - elseif param1 == "up" or param1 == "down" then - -- Up or down - if nCompletion then - -- Cycle completions - clear() - if param == "up" then - nCompletion = nCompletion - 1 - 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 + elseif par2 == "up" then + if #completions > 1 then + dirty = true + clearCompletion() + if comp_id > 1 then + comp_id = comp_id - 1 else - -- Down - if nHistoryPos == #_tHistory then - nHistoryPos = nil - elseif nHistoryPos ~= nil then - nHistoryPos = nHistoryPos + 1 - end + comp_id = #completions end - if nHistoryPos then - sLine = _tHistory[nHistoryPos] - nPos, nScroll = #sLine, 0 + + elseif hist_pos > 1 then + cursor_pos = 0 + + history[hist_pos] = buffer + hist_pos = hist_pos - 1 + + buffer = (" "):rep(#buffer) + full_redraw(true) + + buffer = history[hist_pos] + dirty = true + end + + elseif par2 == "down" then + if #completions > 1 then + dirty = true + clearCompletion() + if comp_id < #completions then + comp_id = comp_id + 1 else - sLine = "" - nPos, nScroll = 0, 0 + comp_id = 1 end - uncomplete() - redraw() + elseif hist_pos < #history then + cursor_pos = 0 + + history[hist_pos] = buffer + hist_pos = hist_pos + 1 + + buffer = (" "):rep(#buffer) + full_redraw(true) + + buffer = history[hist_pos] + dirty = true end - elseif param1 == "back" then - -- Backspace - if nPos > 0 then - clear() - sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1) - nPos = nPos - 1 - if nScroll > 0 then nScroll = nScroll - 1 end - recomplete() - redraw() + elseif par2 == "left" then + if cursor_pos < #buffer then + clearCompletion() + cursor_pos = cursor_pos + 1 end - elseif param1 == "home" then - -- Home - if nPos > 0 then - clear() - nPos = 0 - recomplete() - redraw() + elseif par2 == "right" then + if cursor_pos > 0 then + cursor_pos = cursor_pos - 1 + + elseif comp_id > 0 then + dirty = true + buffer = buffer .. completions[comp_id] end - elseif param1 == "delete" then - -- Delete - if nPos < #sLine then - clear() - sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2) - recomplete() - redraw() + elseif par2 == "tab" then + if comp_id > 0 then + dirty = true + buffer = buffer .. completions[comp_id] end - elseif param1 == "end" then - -- End - if nPos < #sLine then - clear() - nPos = #sLine - recomplete() - redraw() - end + elseif par2 == "home" then + cursor_pos = #buffer - elseif param1 == "tab" then - -- Tab (accept autocomplete) - acceptCompletion() + elseif par2 == "end" then + cursor_pos = 0 + elseif par2 == "enter" then + clearCompletion() + print() + break end - - elseif sEvent == "mouse_down" or sEvent == "mouse_move" and param == 1 then - local _, cy = term.getPos() - if param1 >= sx and param1 <= w and param2 == cy then - -- Ensure we don't scroll beyond the current line - nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine) - redraw() - end - - elseif sEvent == "term_resize" then - -- Terminal resized - w = term.getSize() - redraw() - end end - local _, cy = term.getPos() term.setBlink(false) - term.setPos(w + 1, cy) - print() - return sLine + return buffer end -return io \ No newline at end of file +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 + +return io diff --git a/Capy64/Assets/Lua/lib/parallel.lua b/Capy64/Assets/Lua/lib/parallel.lua new file mode 100644 index 0000000..bdb79b8 --- /dev/null +++ b/Capy64/Assets/Lua/lib/parallel.lua @@ -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 diff --git a/Capy64/Assets/Lua/lib/utfstring.lua b/Capy64/Assets/Lua/lib/utfstring.lua index 7119373..b442b6d 100644 --- a/Capy64/Assets/Lua/lib/utfstring.lua +++ b/Capy64/Assets/Lua/lib/utfstring.lua @@ -1,7 +1,7 @@ local expect = require "expect" local UTFString = {} -local UTFString_mt = {__index = UTFString, __name = "UTFString"} +local UTFString_mt = { __index = UTFString, __name = "UTFString" } local function toUTF8(s) -- convert from ANSI if the string is invalid in UTF-8 @@ -18,10 +18,10 @@ local function toUTF8(s) end function UTFString:new(s) - return setmetatable({str = toUTF8(s)}, UTFString_mt) + return setmetatable({ str = toUTF8(s) }, UTFString_mt) end -setmetatable(UTFString, {__call = UTFString.new}) +setmetatable(UTFString, { __call = UTFString.new }) local utf8_case_conv_utl, utf8_case_conv_ltu = nil, {} -- defined at bottom of file @@ -40,10 +40,12 @@ function UTFString:find(pattern, init, plain) expect(1, pattern, "string", "table") expect(2, init, "number", "nil") 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 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) 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 @@ -61,10 +63,12 @@ end function UTFString:gmatch(pattern) expect(1, pattern, "string", "table") 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 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) return function() local matches = table.pack(iter()) @@ -82,12 +86,14 @@ function UTFString:gsub(pattern, repl, n) expect(2, repl, "string", "table", "function") expect(3, n, "number", "nil") 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 - else pattern = toUTF8(pattern)end + else pattern = toUTF8(pattern) end if type(repl) == "table" and getmetatable(repl) == UTFString_mt then repl = repl.str 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) return UTFString(s), total end @@ -111,10 +117,12 @@ function UTFString:match(pattern, init) expect(1, pattern, "string", "table") expect(2, init, "number", "nil") 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 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))) for i = 1, matches.n do local tt = type(matches[i]) @@ -143,7 +151,7 @@ function UTFString:rep(n, sep) end function UTFString:reverse() - local codes = {n = 0} + local codes = { n = 0 } for _, c in utf8.codes(self.str) do codes.n = codes.n + 1 codes[codes.n] = c @@ -191,8 +199,10 @@ function UTFString_mt.__concat(a, b) if type(a) == "string" then return UTFString(a .. b.str) elseif type(b) == "string" then return UTFString(a.str .. b) else - if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to concatenate " .. 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 + if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to concatenate " .. + 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) end end @@ -208,8 +218,10 @@ function UTFString_mt.__lt(a, b) if type(a) == "string" then return a < b.str elseif type(b) == "string" then return a.str < b else - if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " .. 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 + if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " .. + 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 end end @@ -218,8 +230,10 @@ function UTFString_mt.__le(a, b) if type(a) == "string" then return a <= b.str elseif type(b) == "string" then return a.str <= b else - if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " .. 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 + if type(a) ~= "table" or getmetatable(a) ~= UTFString_mt then error("attempt to compare " .. + 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 end end @@ -245,7 +259,7 @@ utf8_case_conv_utl = { [0x0047] = 0x0067, [0x0048] = 0x0068, [0x0049] = 0x0069, - + [0x004A] = 0x006A, [0x004B] = 0x006B, [0x004C] = 0x006C, @@ -294,7 +308,7 @@ utf8_case_conv_utl = { [0x00DC] = 0x00FC, [0x00DD] = 0x00FD, [0x00DE] = 0x00FE, - [0x00DF] = {0x0073, 0x0073}, + [0x00DF] = { 0x0073, 0x0073 }, [0x0100] = 0x0101, [0x0102] = 0x0103, [0x0104] = 0x0105, @@ -319,8 +333,8 @@ utf8_case_conv_utl = { [0x012A] = 0x012B, [0x012C] = 0x012D, [0x012E] = 0x012F, - [0x0130] = {0x0069, 0x0307}, - + [0x0130] = { 0x0069, 0x0307 }, + [0x0132] = 0x0133, [0x0134] = 0x0135, [0x0136] = 0x0137, @@ -332,7 +346,7 @@ utf8_case_conv_utl = { [0x0143] = 0x0144, [0x0145] = 0x0146, [0x0147] = 0x0148, - [0x0149] = {0x02BC, 0x006E}, + [0x0149] = { 0x02BC, 0x006E }, [0x014A] = 0x014B, [0x014C] = 0x014D, [0x014E] = 0x014F, @@ -420,7 +434,7 @@ utf8_case_conv_utl = { [0x01EA] = 0x01EB, [0x01EC] = 0x01ED, [0x01EE] = 0x01EF, - [0x01F0] = {0x006A, 0x030C}, + [0x01F0] = { 0x006A, 0x030C }, [0x01F1] = 0x01F3, [0x01F2] = 0x01F3, [0x01F4] = 0x01F5, @@ -481,7 +495,7 @@ utf8_case_conv_utl = { [0x038C] = 0x03CC, [0x038E] = 0x03CD, [0x038F] = 0x03CE, - [0x0390] = {0x03B9, 0x0308, 0x0301}, + [0x0390] = { 0x03B9, 0x0308, 0x0301 }, [0x0391] = 0x03B1, [0x0392] = 0x03B2, [0x0393] = 0x03B3, @@ -508,7 +522,7 @@ utf8_case_conv_utl = { [0x03A9] = 0x03C9, [0x03AA] = 0x03CA, [0x03AB] = 0x03CB, - [0x03B0] = {0x03C5, 0x0308, 0x0301}, + [0x03B0] = { 0x03C5, 0x0308, 0x0301 }, [0x03C2] = 0x03C3, [0x03CF] = 0x03D7, [0x03D0] = 0x03B2, @@ -723,7 +737,7 @@ utf8_case_conv_utl = { [0x0554] = 0x0584, [0x0555] = 0x0585, [0x0556] = 0x0586, - [0x0587] = {0x0565, 0x0582}, + [0x0587] = { 0x0565, 0x0582 }, [0x10A0] = 0x2D00, [0x10A1] = 0x2D01, [0x10A2] = 0x2D02, @@ -900,13 +914,13 @@ utf8_case_conv_utl = { [0x1E90] = 0x1E91, [0x1E92] = 0x1E93, [0x1E94] = 0x1E95, - [0x1E96] = {0x0068, 0x0331}, - [0x1E97] = {0x0074, 0x0308}, - [0x1E98] = {0x0077, 0x030A}, - [0x1E99] = {0x0079, 0x030A}, - [0x1E9A] = {0x0061, 0x02BE}, + [0x1E96] = { 0x0068, 0x0331 }, + [0x1E97] = { 0x0074, 0x0308 }, + [0x1E98] = { 0x0077, 0x030A }, + [0x1E99] = { 0x0079, 0x030A }, + [0x1E9A] = { 0x0061, 0x02BE }, [0x1E9B] = 0x1E61, - [0x1E9E] = {0x0073, 0x0073}, + [0x1E9E] = { 0x0073, 0x0073 }, --1E9E; S;0x00DF, ; # LATIN CAPITAL LETTER SHARP S [0x1EA0] = 0x1EA1, [0x1EA2] = 0x1EA3, @@ -992,10 +1006,10 @@ utf8_case_conv_utl = { [0x1F4B] = 0x1F43, [0x1F4C] = 0x1F44, [0x1F4D] = 0x1F45, - [0x1F50] = {0x03C5, 0x0313}, - [0x1F52] = {0x03C5, 0x0313, 0x0300}, - [0x1F54] = {0x03C5, 0x0313, 0x0301}, - [0x1F56] = {0x03C5, 0x0313, 0x0342}, + [0x1F50] = { 0x03C5, 0x0313 }, + [0x1F52] = { 0x03C5, 0x0313, 0x0300 }, + [0x1F54] = { 0x03C5, 0x0313, 0x0301 }, + [0x1F56] = { 0x03C5, 0x0313, 0x0342 }, [0x1F59] = 0x1F51, [0x1F5B] = 0x1F53, [0x1F5D] = 0x1F55, @@ -1008,129 +1022,129 @@ utf8_case_conv_utl = { [0x1F6D] = 0x1F65, [0x1F6E] = 0x1F66, [0x1F6F] = 0x1F67, - [0x1F80] = {0x1F00, 0x03B9}, - [0x1F81] = {0x1F01, 0x03B9}, - [0x1F82] = {0x1F02, 0x03B9}, - [0x1F83] = {0x1F03, 0x03B9}, - [0x1F84] = {0x1F04, 0x03B9}, - [0x1F85] = {0x1F05, 0x03B9}, - [0x1F86] = {0x1F06, 0x03B9}, - [0x1F87] = {0x1F07, 0x03B9}, - [0x1F88] = {0x1F00, 0x03B9}, + [0x1F80] = { 0x1F00, 0x03B9 }, + [0x1F81] = { 0x1F01, 0x03B9 }, + [0x1F82] = { 0x1F02, 0x03B9 }, + [0x1F83] = { 0x1F03, 0x03B9 }, + [0x1F84] = { 0x1F04, 0x03B9 }, + [0x1F85] = { 0x1F05, 0x03B9 }, + [0x1F86] = { 0x1F06, 0x03B9 }, + [0x1F87] = { 0x1F07, 0x03B9 }, + [0x1F88] = { 0x1F00, 0x03B9 }, --1F88; S;0x1F80, ; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI - [0x1F89] = {0x1F01, 0x03B9}, + [0x1F89] = { 0x1F01, 0x03B9 }, --1F89; S;0x1F81, ; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI - [0x1F8A] = {0x1F02, 0x03B9}, + [0x1F8A] = { 0x1F02, 0x03B9 }, --1F8A; S;0x1F82, ; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI - [0x1F8B] = {0x1F03, 0x03B9}, + [0x1F8B] = { 0x1F03, 0x03B9 }, --1F8B; S;0x1F83, ; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI - [0x1F8C] = {0x1F04, 0x03B9}, + [0x1F8C] = { 0x1F04, 0x03B9 }, --1F8C; S;0x1F84, ; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI - [0x1F8D] = {0x1F05, 0x03B9}, + [0x1F8D] = { 0x1F05, 0x03B9 }, --1F8D; S;0x1F85, ; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI - [0x1F8E] = {0x1F06, 0x03B9}, + [0x1F8E] = { 0x1F06, 0x03B9 }, --1F8E; S;0x1F86, ; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI - [0x1F8F] = {0x1F07, 0x03B9}, + [0x1F8F] = { 0x1F07, 0x03B9 }, --1F8F; S;0x1F87, ; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI - [0x1F90] = {0x1F20, 0x03B9}, - [0x1F91] = {0x1F21, 0x03B9}, - [0x1F92] = {0x1F22, 0x03B9}, - [0x1F93] = {0x1F23, 0x03B9}, - [0x1F94] = {0x1F24, 0x03B9}, - [0x1F95] = {0x1F25, 0x03B9}, - [0x1F96] = {0x1F26, 0x03B9}, - [0x1F97] = {0x1F27, 0x03B9}, - [0x1F98] = {0x1F20, 0x03B9}, + [0x1F90] = { 0x1F20, 0x03B9 }, + [0x1F91] = { 0x1F21, 0x03B9 }, + [0x1F92] = { 0x1F22, 0x03B9 }, + [0x1F93] = { 0x1F23, 0x03B9 }, + [0x1F94] = { 0x1F24, 0x03B9 }, + [0x1F95] = { 0x1F25, 0x03B9 }, + [0x1F96] = { 0x1F26, 0x03B9 }, + [0x1F97] = { 0x1F27, 0x03B9 }, + [0x1F98] = { 0x1F20, 0x03B9 }, --1F98; S;0x1F90, ; # GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI - [0x1F99] = {0x1F21, 0x03B9}, + [0x1F99] = { 0x1F21, 0x03B9 }, --1F99; S;0x1F91, ; # GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI - [0x1F9A] = {0x1F22, 0x03B9}, + [0x1F9A] = { 0x1F22, 0x03B9 }, --1F9A; S;0x1F92, ; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI - [0x1F9B] = {0x1F23, 0x03B9}, + [0x1F9B] = { 0x1F23, 0x03B9 }, --1F9B; S;0x1F93, ; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI - [0x1F9C] = {0x1F24, 0x03B9}, + [0x1F9C] = { 0x1F24, 0x03B9 }, --1F9C; S;0x1F94, ; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI - [0x1F9D] = {0x1F25, 0x03B9}, + [0x1F9D] = { 0x1F25, 0x03B9 }, --1F9D; S;0x1F95, ; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI - [0x1F9E] = {0x1F26, 0x03B9}, + [0x1F9E] = { 0x1F26, 0x03B9 }, --1F9E; S;0x1F96, ; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI - [0x1F9F] = {0x1F27, 0x03B9}, + [0x1F9F] = { 0x1F27, 0x03B9 }, --1F9F; S;0x1F97, ; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI - [0x1FA0] = {0x1F60, 0x03B9}, - [0x1FA1] = {0x1F61, 0x03B9}, - [0x1FA2] = {0x1F62, 0x03B9}, - [0x1FA3] = {0x1F63, 0x03B9}, - [0x1FA4] = {0x1F64, 0x03B9}, - [0x1FA5] = {0x1F65, 0x03B9}, - [0x1FA6] = {0x1F66, 0x03B9}, - [0x1FA7] = {0x1F67, 0x03B9}, - [0x1FA8] = {0x1F60, 0x03B9}, + [0x1FA0] = { 0x1F60, 0x03B9 }, + [0x1FA1] = { 0x1F61, 0x03B9 }, + [0x1FA2] = { 0x1F62, 0x03B9 }, + [0x1FA3] = { 0x1F63, 0x03B9 }, + [0x1FA4] = { 0x1F64, 0x03B9 }, + [0x1FA5] = { 0x1F65, 0x03B9 }, + [0x1FA6] = { 0x1F66, 0x03B9 }, + [0x1FA7] = { 0x1F67, 0x03B9 }, + [0x1FA8] = { 0x1F60, 0x03B9 }, --1FA8; S;0x1FA0, ; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI - [0x1FA9] = {0x1F61, 0x03B9}, + [0x1FA9] = { 0x1F61, 0x03B9 }, --1FA9; S;0x1FA1, ; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI - [0x1FAA] = {0x1F62, 0x03B9}, + [0x1FAA] = { 0x1F62, 0x03B9 }, --1FAA; S;0x1FA2, ; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI - [0x1FAB] = {0x1F63, 0x03B9}, + [0x1FAB] = { 0x1F63, 0x03B9 }, --1FAB; S;0x1FA3, ; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI - [0x1FAC] = {0x1F64, 0x03B9}, + [0x1FAC] = { 0x1F64, 0x03B9 }, --1FAC; S;0x1FA4, ; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI - [0x1FAD] = {0x1F65, 0x03B9}, + [0x1FAD] = { 0x1F65, 0x03B9 }, --1FAD; S;0x1FA5, ; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI - [0x1FAE] = {0x1F66, 0x03B9}, + [0x1FAE] = { 0x1F66, 0x03B9 }, --1FAE; S;0x1FA6, ; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI - [0x1FAF] = {0x1F67, 0x03B9}, + [0x1FAF] = { 0x1F67, 0x03B9 }, --1FAF; S;0x1FA7, ; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI - [0x1FB2] = {0x1F70, 0x03B9}, - [0x1FB3] = {0x03B1, 0x03B9}, - [0x1FB4] = {0x03AC, 0x03B9}, - [0x1FB6] = {0x03B1, 0x0342}, - [0x1FB7] = {0x03B1, 0x0342, 0x03B9}, + [0x1FB2] = { 0x1F70, 0x03B9 }, + [0x1FB3] = { 0x03B1, 0x03B9 }, + [0x1FB4] = { 0x03AC, 0x03B9 }, + [0x1FB6] = { 0x03B1, 0x0342 }, + [0x1FB7] = { 0x03B1, 0x0342, 0x03B9 }, [0x1FB8] = 0x1FB0, [0x1FB9] = 0x1FB1, [0x1FBA] = 0x1F70, [0x1FBB] = 0x1F71, - [0x1FBC] = {0x03B1, 0x03B9}, + [0x1FBC] = { 0x03B1, 0x03B9 }, --1FBC; S;0x1FB3, ; # GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI [0x1FBE] = 0x03B9, - [0x1FC2] = {0x1F74, 0x03B9}, - [0x1FC3] = {0x03B7, 0x03B9}, - [0x1FC4] = {0x03AE, 0x03B9}, - [0x1FC6] = {0x03B7, 0x0342}, - [0x1FC7] = {0x03B7, 0x0342, 0x03B9}, + [0x1FC2] = { 0x1F74, 0x03B9 }, + [0x1FC3] = { 0x03B7, 0x03B9 }, + [0x1FC4] = { 0x03AE, 0x03B9 }, + [0x1FC6] = { 0x03B7, 0x0342 }, + [0x1FC7] = { 0x03B7, 0x0342, 0x03B9 }, [0x1FC8] = 0x1F72, [0x1FC9] = 0x1F73, [0x1FCA] = 0x1F74, [0x1FCB] = 0x1F75, - [0x1FCC] = {0x03B7, 0x03B9}, + [0x1FCC] = { 0x03B7, 0x03B9 }, --1FCC; S;0x1FC3, ; # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI - [0x1FD2] = {0x03B9, 0x0308, 0x0300}, - [0x1FD3] = {0x03B9, 0x0308, 0x0301}, - [0x1FD6] = {0x03B9, 0x0342}, - [0x1FD7] = {0x03B9, 0x0308, 0x0342}, + [0x1FD2] = { 0x03B9, 0x0308, 0x0300 }, + [0x1FD3] = { 0x03B9, 0x0308, 0x0301 }, + [0x1FD6] = { 0x03B9, 0x0342 }, + [0x1FD7] = { 0x03B9, 0x0308, 0x0342 }, [0x1FD8] = 0x1FD0, [0x1FD9] = 0x1FD1, [0x1FDA] = 0x1F76, [0x1FDB] = 0x1F77, - [0x1FE2] = {0x03C5, 0x0308, 0x0300}, - [0x1FE3] = {0x03C5, 0x0308, 0x0301}, - [0x1FE4] = {0x03C1, 0x0313}, - [0x1FE6] = {0x03C5, 0x0342}, - [0x1FE7] = {0x03C5, 0x0308, 0x0342}, + [0x1FE2] = { 0x03C5, 0x0308, 0x0300 }, + [0x1FE3] = { 0x03C5, 0x0308, 0x0301 }, + [0x1FE4] = { 0x03C1, 0x0313 }, + [0x1FE6] = { 0x03C5, 0x0342 }, + [0x1FE7] = { 0x03C5, 0x0308, 0x0342 }, [0x1FE8] = 0x1FE0, [0x1FE9] = 0x1FE1, [0x1FEA] = 0x1F7A, [0x1FEB] = 0x1F7B, [0x1FEC] = 0x1FE5, - [0x1FF2] = {0x1F7C, 0x03B9}, - [0x1FF3] = {0x03C9, 0x03B9}, - [0x1FF4] = {0x03CE, 0x03B9}, - [0x1FF6] = {0x03C9, 0x0342}, - [0x1FF7] = {0x03C9, 0x0342, 0x03B9}, + [0x1FF2] = { 0x1F7C, 0x03B9 }, + [0x1FF3] = { 0x03C9, 0x03B9 }, + [0x1FF4] = { 0x03CE, 0x03B9 }, + [0x1FF6] = { 0x03C9, 0x0342 }, + [0x1FF7] = { 0x03C9, 0x0342, 0x03B9 }, [0x1FF8] = 0x1F78, [0x1FF9] = 0x1F79, [0x1FFA] = 0x1F7C, [0x1FFB] = 0x1F7D, - [0x1FFC] = {0x03C9, 0x03B9}, + [0x1FFC] = { 0x03C9, 0x03B9 }, --1FFC; S;0x1FF3, ; # GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI [0x2126] = 0x03C9, [0x212A] = 0x006B, @@ -1493,18 +1507,18 @@ utf8_case_conv_utl = { [0xABBD] = 0x13ED, [0xABBE] = 0x13EE, [0xABBF] = 0x13EF, - [0xFB00] = {0x0066, 0x0066}, - [0xFB01] = {0x0066, 0x0069}, - [0xFB02] = {0x0066, 0x006C}, - [0xFB03] = {0x0066, 0x0066, 0x0069}, - [0xFB04] = {0x0066, 0x0066, 0x006C}, - [0xFB05] = {0x0073, 0x0074}, - [0xFB06] = {0x0073, 0x0074}, - [0xFB13] = {0x0574, 0x0576}, - [0xFB14] = {0x0574, 0x0565}, - [0xFB15] = {0x0574, 0x056B}, - [0xFB16] = {0x057E, 0x0576}, - [0xFB17] = {0x0574, 0x056D}, + [0xFB00] = { 0x0066, 0x0066 }, + [0xFB01] = { 0x0066, 0x0069 }, + [0xFB02] = { 0x0066, 0x006C }, + [0xFB03] = { 0x0066, 0x0066, 0x0069 }, + [0xFB04] = { 0x0066, 0x0066, 0x006C }, + [0xFB05] = { 0x0073, 0x0074 }, + [0xFB06] = { 0x0073, 0x0074 }, + [0xFB13] = { 0x0574, 0x0576 }, + [0xFB14] = { 0x0574, 0x0565 }, + [0xFB15] = { 0x0574, 0x056B }, + [0xFB16] = { 0x057E, 0x0576 }, + [0xFB17] = { 0x0574, 0x056D }, [0xFF21] = 0xFF41, [0xFF22] = 0xFF42, [0xFF23] = 0xFF43, @@ -1757,6 +1771,6 @@ utf8_case_conv_utl = { [0x1E920] = 0x1E942, [0x1E921] = 0x1E943, } -for k,v in pairs(utf8_case_conv_utl) do utf8_case_conv_ltu[v] = k end +for k, v in pairs(utf8_case_conv_utl) do utf8_case_conv_ltu[v] = k end -return UTFString \ No newline at end of file +return UTFString