mirror of
https://github.com/kepler155c/opus
synced 2024-11-05 08:26:16 +00:00
531 lines
10 KiB
Lua
531 lines
10 KiB
Lua
local Util = { }
|
|
|
|
function Util.tryTimed(timeout, f, ...)
|
|
local c = os.clock()
|
|
repeat
|
|
local ret = f(...)
|
|
if ret then
|
|
return ret
|
|
end
|
|
until os.clock()-c >= timeout
|
|
end
|
|
|
|
function Util.tryTimes(attempts, f, ...)
|
|
local result
|
|
for i = 1, attempts do
|
|
result = { f(...) }
|
|
if result[1] then
|
|
return unpack(result)
|
|
end
|
|
end
|
|
return unpack(result)
|
|
end
|
|
|
|
function Util.print(pattern, ...)
|
|
|
|
local function serialize(tbl, width)
|
|
local str = '{\n'
|
|
for k, v in pairs(tbl) do
|
|
local value
|
|
if type(v) == 'table' then
|
|
value = string.format('table: %d', Util.size(v))
|
|
else
|
|
value = tostring(v)
|
|
end
|
|
str = str .. string.format(' %s: %s\n', k, value)
|
|
end
|
|
if #str < width then
|
|
str = str:gsub('\n', '') .. ' }'
|
|
else
|
|
str = str .. '}'
|
|
end
|
|
return str
|
|
end
|
|
|
|
if type(pattern) == 'string' then
|
|
print(string.format(pattern, ...))
|
|
elseif type(pattern) == 'table' then
|
|
print(serialize(pattern, term.current().getSize()))
|
|
else
|
|
print(tostring(pattern))
|
|
end
|
|
end
|
|
|
|
function Util.runFunction(env, fn, ...)
|
|
setfenv(fn, env)
|
|
setmetatable(env, { __index = _G })
|
|
|
|
local args = { ... }
|
|
return pcall(function()
|
|
return fn(table.unpack(args))
|
|
end)
|
|
end
|
|
|
|
-- http://lua-users.org/wiki/SimpleRound
|
|
function Util.round(num, idp)
|
|
local mult = 10^(idp or 0)
|
|
return math.floor(num * mult + 0.5) / mult
|
|
end
|
|
|
|
function Util.random(max, min)
|
|
min = min or 0
|
|
return math.random(0, max-min) + min
|
|
end
|
|
|
|
--[[ Table functions ]] --
|
|
function Util.clear(t)
|
|
local keys = Util.keys(t)
|
|
for _,k in pairs(keys) do
|
|
t[k] = nil
|
|
end
|
|
end
|
|
|
|
function Util.empty(t)
|
|
return not next(t)
|
|
end
|
|
|
|
function Util.key(t, value)
|
|
for k,v in pairs(t) do
|
|
if v == value then
|
|
return k
|
|
end
|
|
end
|
|
end
|
|
|
|
function Util.keys(t)
|
|
local keys = {}
|
|
for k in pairs(t) do
|
|
keys[#keys+1] = k
|
|
end
|
|
return keys
|
|
end
|
|
|
|
function Util.merge(obj, args)
|
|
if args then
|
|
for k,v in pairs(args) do
|
|
obj[k] = v
|
|
end
|
|
end
|
|
end
|
|
|
|
function Util.deepMerge(obj, args)
|
|
if args then
|
|
for k,v in pairs(args) do
|
|
if type(v) == 'table' then
|
|
if not obj[k] then
|
|
obj[k] = { }
|
|
end
|
|
Util.deepMerge(obj[k], v)
|
|
else
|
|
obj[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Util.transpose(t)
|
|
local tt = { }
|
|
for k,v in pairs(t) do
|
|
tt[v] = k
|
|
end
|
|
return tt
|
|
end
|
|
|
|
function Util.find(t, name, value)
|
|
for k,v in pairs(t) do
|
|
if v[name] == value then
|
|
return v, k
|
|
end
|
|
end
|
|
end
|
|
|
|
function Util.findAll(t, name, value)
|
|
local rt = { }
|
|
for k,v in pairs(t) do
|
|
if v[name] == value then
|
|
table.insert(rt, v)
|
|
end
|
|
end
|
|
return rt
|
|
end
|
|
|
|
function Util.shallowCopy(t)
|
|
local t2 = {}
|
|
for k,v in pairs(t) do
|
|
t2[k] = v
|
|
end
|
|
return t2
|
|
end
|
|
|
|
function Util.deepCopy(t)
|
|
if type(t) ~= 'table' then
|
|
return t
|
|
end
|
|
--local mt = getmetatable(t)
|
|
local res = {}
|
|
for k,v in pairs(t) do
|
|
if type(v) == 'table' then
|
|
v = Util.deepCopy(v)
|
|
end
|
|
res[k] = v
|
|
end
|
|
--setmetatable(res,mt)
|
|
return res
|
|
end
|
|
|
|
-- http://snippets.luacode.org/?p=snippets/Filter_a_table_in-place_119
|
|
function Util.filterInplace(t, predicate)
|
|
local j = 1
|
|
|
|
for i = 1,#t do
|
|
local v = t[i]
|
|
if predicate(v) then
|
|
t[j] = v
|
|
j = j + 1
|
|
end
|
|
end
|
|
|
|
while t[j] ~= nil do
|
|
t[j] = nil
|
|
j = j + 1
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
function Util.filter(it, f)
|
|
local ot = { }
|
|
for k,v in pairs(it) do
|
|
if f(k, v) then
|
|
ot[k] = v
|
|
end
|
|
end
|
|
return ot
|
|
end
|
|
|
|
function Util.size(list)
|
|
if type(list) == 'table' then
|
|
local length = 0
|
|
table.foreach(list, function() length = length + 1 end)
|
|
return length
|
|
end
|
|
return 0
|
|
end
|
|
|
|
function Util.each(list, func)
|
|
for index, value in pairs(list) do
|
|
func(value, index, list)
|
|
end
|
|
end
|
|
|
|
-- http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
|
|
function Util.spairs(t, order)
|
|
local keys = Util.keys(t)
|
|
|
|
-- if order function given, sort by it by passing the table and keys a, b,
|
|
-- otherwise just sort the keys
|
|
if order then
|
|
table.sort(keys, function(a,b) return order(t[a], t[b]) end)
|
|
else
|
|
table.sort(keys)
|
|
end
|
|
|
|
-- return the iterator function
|
|
local i = 0
|
|
return function()
|
|
i = i + 1
|
|
if keys[i] then
|
|
return keys[i], t[keys[i]]
|
|
end
|
|
end
|
|
end
|
|
|
|
function Util.first(t, order)
|
|
local keys = Util.keys(t)
|
|
if order then
|
|
table.sort(keys, function(a,b) return order(t[a], t[b]) end)
|
|
else
|
|
table.sort(keys)
|
|
end
|
|
return keys[1], t[keys[1]]
|
|
end
|
|
|
|
--[[ File functions ]]--
|
|
function Util.readFile(fname)
|
|
local f = fs.open(fname, "r")
|
|
if f then
|
|
local t = f.readAll()
|
|
f.close()
|
|
return t
|
|
end
|
|
end
|
|
|
|
function Util.writeFile(fname, data)
|
|
local file = io.open(fname, "w")
|
|
if not file then
|
|
error('Unable to open ' .. fname, 2)
|
|
end
|
|
file:write(data)
|
|
file:close()
|
|
end
|
|
|
|
function Util.readLines(fname)
|
|
local file = fs.open(fname, "r")
|
|
if file then
|
|
local t = {}
|
|
local line = file.readLine()
|
|
while line do
|
|
table.insert(t, line)
|
|
line = file.readLine()
|
|
end
|
|
file.close()
|
|
return t
|
|
end
|
|
end
|
|
|
|
function Util.writeLines(fname, lines)
|
|
local file = fs.open(fname, 'w')
|
|
if file then
|
|
for _,line in ipairs(lines) do
|
|
line = file.writeLine(line)
|
|
end
|
|
file.close()
|
|
return true
|
|
end
|
|
end
|
|
|
|
function Util.readTable(fname)
|
|
local t = Util.readFile(fname)
|
|
if t then
|
|
return textutils.unserialize(t)
|
|
end
|
|
end
|
|
|
|
function Util.writeTable(fname, data)
|
|
Util.writeFile(fname, textutils.serialize(data))
|
|
end
|
|
|
|
function Util.loadTable(fname)
|
|
local fc = Util.readFile(fname)
|
|
if not fc then
|
|
return false, 'Unable to read file'
|
|
end
|
|
local s, m = loadstring('return ' .. fc, fname)
|
|
if s then
|
|
s, m = pcall(s)
|
|
if s then
|
|
return m
|
|
end
|
|
end
|
|
return s, m
|
|
end
|
|
|
|
--[[ URL functions ]] --
|
|
function Util.download(url, filename)
|
|
local h = http.get(url)
|
|
if not h then
|
|
error('Failed to download ' .. url)
|
|
end
|
|
local contents = h.readAll()
|
|
h.close()
|
|
if not contents then
|
|
error('Failed to download ' .. url)
|
|
end
|
|
|
|
if filename then
|
|
Util.writeFile(filename, contents)
|
|
end
|
|
return contents
|
|
end
|
|
|
|
function Util.loadUrl(url, env) -- loadfile equivalent
|
|
local c = Util.download(url)
|
|
return load(c, url, nil, env)
|
|
end
|
|
|
|
function Util.runUrl(env, url, ...) -- os.run equivalent
|
|
local fn, m = Util.loadUrl(url, env)
|
|
if fn then
|
|
local args = { ... }
|
|
fn, m = pcall(function() fn(unpack(args)) end)
|
|
end
|
|
if not fn and m and m ~= '' then
|
|
printError(m)
|
|
end
|
|
return fn, m
|
|
end
|
|
|
|
--[[ String functions ]] --
|
|
function Util.toBytes(n)
|
|
if n >= 1000000 then
|
|
return string.format('%sM', Util.round(n/1000000, 1))
|
|
elseif n >= 1000 then
|
|
return string.format('%sK', Util.round(n/1000, 1))
|
|
end
|
|
return tostring(n)
|
|
end
|
|
|
|
function Util.split(str, pattern)
|
|
pattern = pattern or "(.-)\n"
|
|
local t = {}
|
|
local function helper(line) table.insert(t, line) return "" end
|
|
helper((str:gsub(pattern, helper)))
|
|
return t
|
|
end
|
|
|
|
function Util.matches(str, pattern)
|
|
pattern = pattern or '%S+'
|
|
local t = { }
|
|
for s in str:gmatch(pattern) do
|
|
table.insert(t, s)
|
|
end
|
|
return t
|
|
end
|
|
|
|
function Util.widthify(s, len)
|
|
s = s or ''
|
|
local slen = #s
|
|
if slen < len then
|
|
s = s .. string.rep(' ', len - #s)
|
|
elseif slen > len then
|
|
s = s:sub(1, len)
|
|
end
|
|
return s
|
|
end
|
|
|
|
-- http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76
|
|
function Util.trim(s)
|
|
return s:find'^%s*$' and '' or s:match'^%s*(.*%S)'
|
|
end
|
|
|
|
-- trim whitespace from left end of string
|
|
function Util.triml(s)
|
|
return s:match'^%s*(.*)'
|
|
end
|
|
|
|
-- trim whitespace from right end of string
|
|
function Util.trimr(s)
|
|
return s:find'^%s*$' and '' or s:match'^(.*%S)'
|
|
end
|
|
-- end http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76
|
|
|
|
-- word wrapping based on:
|
|
-- https://www.rosettacode.org/wiki/Word_wrap#Lua and
|
|
-- http://lua-users.org/wiki/StringRecipes
|
|
local function splittokens(s)
|
|
local res = {}
|
|
for w in s:gmatch("%S+") do
|
|
res[#res+1] = w
|
|
end
|
|
return res
|
|
end
|
|
|
|
local function paragraphwrap(text, linewidth, res)
|
|
linewidth = linewidth or 75
|
|
local spaceleft = linewidth
|
|
local line = {}
|
|
|
|
for _, word in ipairs(splittokens(text)) do
|
|
if #word + 1 > spaceleft then
|
|
table.insert(res, table.concat(line, ' '))
|
|
line = { word }
|
|
spaceleft = linewidth - #word
|
|
else
|
|
table.insert(line, word)
|
|
spaceleft = spaceleft - (#word + 1)
|
|
end
|
|
end
|
|
|
|
table.insert(res, table.concat(line, ' '))
|
|
return table.concat(res, '\n')
|
|
end
|
|
-- end word wrapping
|
|
|
|
function Util.wordWrap(str, limit)
|
|
|
|
local longLines = Util.split(str)
|
|
local lines = { }
|
|
|
|
for _,line in ipairs(longLines) do
|
|
paragraphwrap(line, limit, lines)
|
|
end
|
|
|
|
return lines
|
|
end
|
|
|
|
-- http://lua-users.org/wiki/AlternativeGetOpt
|
|
local function getopt( arg, options )
|
|
local tab = {}
|
|
for k, v in ipairs(arg) do
|
|
if string.sub( v, 1, 2) == "--" then
|
|
local x = string.find( v, "=", 1, true )
|
|
if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 )
|
|
else tab[ string.sub( v, 3 ) ] = true
|
|
end
|
|
elseif string.sub( v, 1, 1 ) == "-" then
|
|
local y = 2
|
|
local l = string.len(v)
|
|
local jopt
|
|
while ( y <= l ) do
|
|
jopt = string.sub( v, y, y )
|
|
if string.find( options, jopt, 1, true ) then
|
|
if y < l then
|
|
tab[ jopt ] = string.sub( v, y+1 )
|
|
y = l
|
|
else
|
|
tab[ jopt ] = arg[ k + 1 ]
|
|
end
|
|
else
|
|
tab[ jopt ] = true
|
|
end
|
|
y = y + 1
|
|
end
|
|
end
|
|
end
|
|
return tab
|
|
end
|
|
|
|
function Util.showOptions(options)
|
|
print('Arguments: ')
|
|
for k, v in pairs(options) do
|
|
print(string.format('-%s %s', v.arg, v.desc))
|
|
end
|
|
end
|
|
|
|
function Util.getOptions(options, args, ignoreInvalid)
|
|
local argLetters = ''
|
|
for _,o in pairs(options) do
|
|
if o.type ~= 'flag' then
|
|
argLetters = argLetters .. o.arg
|
|
end
|
|
end
|
|
local rawOptions = getopt(args, argLetters)
|
|
local argCount = 0
|
|
|
|
for k,ro in pairs(rawOptions) do
|
|
local found = false
|
|
for _,o in pairs(options) do
|
|
if o.arg == k then
|
|
found = true
|
|
if o.type == 'number' then
|
|
o.value = tonumber(ro)
|
|
elseif o.type == 'help' then
|
|
Util.showOptions(options)
|
|
return false
|
|
else
|
|
o.value = ro
|
|
end
|
|
end
|
|
end
|
|
if not found and not ignoreInvalid then
|
|
print('Invalid argument')
|
|
Util.showOptions(options)
|
|
return false
|
|
end
|
|
end
|
|
return true, Util.size(rawOptions)
|
|
|
|
end
|
|
|
|
return Util
|