itte/itteutil.lua

296 lines
7.4 KiB
Lua

-- ---------------------------------------------------------------------------
-- Itte Util
-- ---------------------------------------------------------------------------
--[[
-- A collection of helper functions used by Itte.
--]]
local itteutil = {}
itteutil.docs = {}
itteutil.docs.get_docs = [===[ (docstring_table [, func_str [, printd_bool]])
Given a table of docstrings and a function name, return the description for
the function name. If `name` is unspecified, return descriptions for all
functions. If `printd` is unspecified, print to stdout, or if set to false,
return results as a string.
]===]
function itteutil.get_docs(docstr, name, printd)
local docs = ""
if name ~= nil then
-- 2nd-level nested objects, e.g. x.y.func()
if itteutil.is_substr(name, "%.") then
local sep, _ = string.find(name, "%.")
local dk = name:sub(1, sep - 1)
local nk = name:sub(sep + 1)
docs = "\n" .. name .. " " .. string.gsub(docstr[dk][nk]:sub(3),
" ", "") .. "\n"
else
docs = "\n" .. name .. " " .. string.gsub(docstr[name]:sub(3), " ", "")
.. "\n"
end
else
-- Sort on-site since associative arrays have no fixed order
local dk = itteutil.table_keys(docstr)
docs = "\n"
for c = 1, #dk do
-- 2nd-level nested objects, e.g. x.y.func()
if type(docstr[dk[c]]) == "table" then
local n_dk = itteutil.table_keys(docstr[dk[c]])
for nc = 1, #n_dk do
docs = docs .. dk[c] .. "." .. n_dk[nc] .. " " ..
string.gsub(docstr[dk[c]][n_dk[nc]]:sub(3), " ", "") .. "\n"
end
else
docs = docs .. dk[c] .. " " .. string.gsub(docstr[dk[c]]:sub(3),
" ", "") .. "\n"
end
end
end
if printd == false then
return docs
else
print(docs)
end
end
itteutil.docs.help = [[ ([func_str])
Given a function name, print a corresponding description.
]]
function itteutil.help(name)
itteutil.get_docs(itteutil.docs, name)
end
itteutil.docs.is_substr = [[ (str, find_str)
Check if a string is a substring of another string. Return true if it is a
substring, or false otherwise.
]]
function itteutil.is_substr(str, search)
if (string.find(str, search) ~= nil) then
return true
else
return false
end
end
itteutil.docs.first_upper = [[ (str)
Return a string with the first character in uppercase.
]]
function itteutil.first_upper(str)
return str:sub(1, 1):upper() .. str:sub(2)
end
itteutil.docs.first_lower = [[ (str)
Return a string with the first character in lowercase.
]]
function itteutil.first_lower(str)
return str:sub(1, 1):lower() .. str:sub(2)
end
itteutil.docs.split_str = [[ (str, [, pattern_str])
Split a string on a pattern separator and return a table of string values,
e.g. split_str("Hello world", "%S+"). If no pattern is specified, split
string on the space character.
]]
function itteutil.split_str(str, sep)
local tbl = {}
local n = 0
-- Other patterns: https://www.lua.org/pil/20.2.html
if sep == nil then sep = "%S+" end
for sub in string.gmatch(str, sep) do
n = n + 1
tbl[n] = sub
end
return tbl
end
itteutil.docs.a_or_an = [[ (str)
Return the corresponding indefinite article for a word.
Does *not* apply to acronyms, e.g. FTP.
]]
function itteutil.a_or_an(str)
local article = "a"
if itteutil.is_substr("a e i o u", str:sub(1, 1)) or
itteutil.is_substr("ho", str:sub(1, 2)) then
article = "an"
end
if (str == "one") or (itteutil.is_substr("eu", str:sub(1, 2))) or
(itteutil.is_substr("uni usa use usi usu", str:sub(1, 3))) then
article = "a"
end
return article
end
itteutil.docs.table_keys = [[ (table)
Return a sorted table of keys for an associative table.
]]
function itteutil.table_keys(tbl)
local keys = {}
local n = 0
for k, v in pairs(tbl) do
n = n + 1
keys[n] = k
end
table.sort(keys)
return keys
end
itteutil.docs.has_key = [[ (table, find_str)
Given a table and string, check whether the string is a key in the table.
Return true if found, or false otherwise.
]]
function itteutil.has_key(tbl, str)
local keys = itteutil.table_keys(tbl)
-- Primitive check for array-like table with numerical keys
if (keys[1] == 1) and (keys[#keys] == #keys) then
for k = 1, #keys do
if str == tbl[k] then
do return true end
end
end
-- Associative table
else
for k = 1, #keys do
if str == keys[k] then
do return true end
end
end
end
return false
end
itteutil.docs.find_key = [[ (table, find_str)
Given a table and string, check whether the string is a key in the table.
Return the key if found, or nil otherwise.
]]
function itteutil.find_key(tbl, str)
local keys = itteutil.table_keys(tbl)
-- Primitive check for array-like table with numerical keys
if (keys[1] == 1) and (keys[#keys] == #keys) then
for k = 1, #keys do
if str == tbl[k] then
do return k end
end
end
-- Associative table
else
for k = 1, #keys do
if str == keys[k] then
do return k end
end
end
end
return nil
end
itteutil.docs.is_entry = [[ (table, key_str, val_str)
Given an associative table, a key and value pair as strings, check whether
the pair is in the table. Return true if it exists in the table, or false
otherwise.
]]
function itteutil.is_entry(tbl, key, val)
for k, v in pairs(tbl) do
if (key == k) and (val == v) then
do return true end
end
end
return false
end
itteutil.docs.pick = [===[ (table [, num_int [, unique_bool ]])
Pseudo-randomly pick entries from a non-associative table and return the
entries in a table. If `num` is unspecified, it will return one entry.
If `unique` is unspecified, it will return non-recurring values.
]===]
function itteutil.pick(tbl, num, unique)
local picks = {}
if num == nil then num = 1 end
if unique == nil then unique = true end
picks[1] = tbl[math.random(1, #tbl)]
if (num > 1) and (num <= #tbl) then
for n = 2, num do
local p = tbl[math.random(1, #tbl)]
-- Check for duplicate
if unique then
local c = 1
-- Avoid endless loop if all values in the table are identical
while (itteutil.has_key(picks, p)) and (c <= #tbl) do
p = tbl[math.random(1, #tbl)]
c = c + 1
end
end
picks[n] = p
end
end
return picks
end
itteutil.docs.sleep = [[ (seconds_int)
Set a delay in seconds.
]]
function itteutil.sleep(s)
local timer = os.clock()
while (os.clock() - timer < s) do end
end
itteutil.docs.debug = [[ (tag_str, debug_str [, enabled_bool])
Format and print debug output if `enabled` is true.
]]
function itteutil.debug(tag, str, enabled)
if enabled then
local ts = os.date("%Y-%m-%d %H:%M:%S", os.time())
print("[" .. ts .. "][" .. tag .. "] " .. str)
end
end
itteutil.docs.source_file = [[ (filename_str)
Load a source file.
]]
function itteutil.source_file(str)
local func, err = loadfile(str)
if func then
return func()
else
itteutil.debug("source_file", "Error: " .. err, true)
do return end
end
end
itteutil.docs.read_file = [[ (filename_str)
Open a file and return the contents as a string.
]]
function itteutil.read_file(str)
local file = io.open(str)
if io.type(file) == "file" then
local contents = file:read("*a")
file:close()
return contents
else
itteutil.debug("read_file", "Error: cannot read file.", true)
end
end
return itteutil