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)
|
||||
for i = 1, #text do
|
||||
local ch = text:sub(i, i)
|
||||
write(ch)
|
||||
io.write(ch)
|
||||
timer.sleep(delay)
|
||||
end
|
||||
print()
|
||||
|
|
|
@ -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 })
|
||||
|
||||
|
@ -36,7 +33,7 @@ term.setForeground(colours.white)
|
|||
|
||||
while bRunning do
|
||||
term.setForeground(colours.yellow)
|
||||
write("> ")
|
||||
io.write("> ")
|
||||
term.setForeground(colours.white)
|
||||
|
||||
local s = io.read(nil, tCommandHistory)
|
||||
|
@ -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
|
|
@ -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,17 +98,21 @@ 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
|
||||
|
@ -124,7 +120,7 @@ while not exit do
|
|||
else
|
||||
term.setForeground(colors.red)
|
||||
end
|
||||
write("$ ")
|
||||
io.write("$ ")
|
||||
|
||||
term.setForeground(colors.white)
|
||||
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)
|
||||
end
|
||||
|
||||
local response, err = http.get(args[1])
|
||||
local response <close>, err = http.get(args[1], nil, {
|
||||
binary = true,
|
||||
})
|
||||
if not response then
|
||||
error(err, 0)
|
||||
end
|
||||
|
||||
local file = fs.open(outputPath, "w")
|
||||
local file <close> = fs.open(outputPath, "w")
|
||||
file:write(response:readAll())
|
||||
file: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
|
||||
|
||||
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)
|
|
@ -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 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)
|
|
@ -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)
|
||||
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
|
|
@ -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
|
||||
|
||||
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;
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
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()
|
||||
if evt == "char" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. par1
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
-- Accept autocomplete
|
||||
acceptCompletion()
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
|
||||
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 evt == "paste" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. par1
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
-- Down
|
||||
if nHistoryPos == #_tHistory then
|
||||
nHistoryPos = nil
|
||||
elseif nHistoryPos ~= nil then
|
||||
nHistoryPos = nHistoryPos + 1
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 ..
|
||||
buffer:sub(-cursor_pos + (#par1 - 1))
|
||||
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
|
||||
if nHistoryPos then
|
||||
sLine = _tHistory[nHistoryPos]
|
||||
nPos, nScroll = #sLine, 0
|
||||
|
||||
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
|
||||
sLine = ""
|
||||
nPos, nScroll = 0, 0
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||
end
|
||||
uncomplete()
|
||||
redraw()
|
||||
cursor_pos = cursor_pos - 1
|
||||
|
||||
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
|
||||
|
||||
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 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 param1 == "home" then
|
||||
-- Home
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = 0
|
||||
recomplete()
|
||||
redraw()
|
||||
elseif par2 == "down" then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id < #completions then
|
||||
comp_id = comp_id + 1
|
||||
else
|
||||
comp_id = 1
|
||||
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 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 == "end" then
|
||||
-- End
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
nPos = #sLine
|
||||
recomplete()
|
||||
redraw()
|
||||
elseif par2 == "left" then
|
||||
if cursor_pos < #buffer then
|
||||
clearCompletion()
|
||||
cursor_pos = cursor_pos + 1
|
||||
end
|
||||
|
||||
elseif param1 == "tab" then
|
||||
-- Tab (accept autocomplete)
|
||||
acceptCompletion()
|
||||
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 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()
|
||||
elseif par2 == "tab" then
|
||||
if comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
|
||||
elseif sEvent == "term_resize" then
|
||||
-- Terminal resized
|
||||
w = term.getSize()
|
||||
redraw()
|
||||
elseif par2 == "home" then
|
||||
cursor_pos = #buffer
|
||||
|
||||
end
|
||||
end
|
||||
elseif par2 == "end" then
|
||||
cursor_pos = 0
|
||||
|
||||
local _, cy = term.getPos()
|
||||
term.setBlink(false)
|
||||
term.setPos(w + 1, cy)
|
||||
elseif par2 == "enter" then
|
||||
clearCompletion()
|
||||
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
|
||||
|
||||
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(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
|
||||
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])
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue