summaryrefslogtreecommitdiff
path: root/lualibs-util-tab.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lualibs-util-tab.lua')
-rw-r--r--lualibs-util-tab.lua307
1 files changed, 258 insertions, 49 deletions
diff --git a/lualibs-util-tab.lua b/lualibs-util-tab.lua
index 7a2da29..ecf36b1 100644
--- a/lualibs-util-tab.lua
+++ b/lualibs-util-tab.lua
@@ -10,24 +10,53 @@ utilities = utilities or {}
utilities.tables = utilities.tables or { }
local tables = utilities.tables
-local format, gmatch, rep, gsub = string.format, string.gmatch, string.rep, string.gsub
+local format, gmatch, gsub = string.format, string.gmatch, string.gsub
local concat, insert, remove = table.concat, table.insert, table.remove
local setmetatable, getmetatable, tonumber, tostring = setmetatable, getmetatable, tonumber, tostring
-local type, next, rawset, tonumber, loadstring = type, next, rawset, tonumber, loadstring
-local lpegmatch, P, Cs = lpeg.match, lpeg.P, lpeg.Cs
+local type, next, rawset, tonumber, tostring, load, select = type, next, rawset, tonumber, tostring, load, select
+local lpegmatch, P, Cs, Cc = lpeg.match, lpeg.P, lpeg.Cs, lpeg.Cc
+local serialize, sortedkeys, sortedpairs = table.serialize, table.sortedkeys, table.sortedpairs
+local formatters = string.formatters
-function tables.definetable(target) -- defines undefined tables
- local composed, t, n = nil, { }, 0
- for name in gmatch(target,"([^%.]+)") do
- n = n + 1
+local splitter = lpeg.tsplitat(".")
+
+function tables.definetable(target,nofirst,nolast) -- defines undefined tables
+ local composed, shortcut, t = nil, nil, { }
+ local snippets = lpegmatch(splitter,target)
+ for i=1,#snippets - (nolast and 1 or 0) do
+ local name = snippets[i]
if composed then
- composed = composed .. "." .. name
+ composed = shortcut .. "." .. name
+ shortcut = shortcut .. "_" .. name
+ t[#t+1] = formatters["local %s = %s if not %s then %s = { } %s = %s end"](shortcut,composed,shortcut,shortcut,composed,shortcut)
else
composed = name
+ shortcut = name
+ if not nofirst then
+ t[#t+1] = formatters["%s = %s or { }"](composed,composed)
+ end
end
- t[n] = format("%s = %s or { }",composed,composed)
end
- return concat(t,"\n")
+ if nolast then
+ composed = shortcut .. "." .. snippets[#snippets]
+ end
+ return concat(t,"\n"), composed
+end
+
+-- local t = tables.definedtable("a","b","c","d")
+
+function tables.definedtable(...)
+ local t = _G
+ for i=1,select("#",...) do
+ local li = select(i,...)
+ local tl = t[li]
+ if not tl then
+ tl = { }
+ t[li] = tl
+ end
+ t = tl
+ end
+ return t
end
function tables.accesstable(target,root)
@@ -98,35 +127,131 @@ end
-- experimental
+local escape = Cs(Cc('"') * ((P('"')/'""' + P(1))^0) * Cc('"'))
+
+function table.tocsv(t,specification)
+ if t and #t > 0 then
+ local result = { }
+ local r = { }
+ specification = specification or { }
+ local fields = specification.fields
+ if type(fields) ~= "string" then
+ fields = sortedkeys(t[1])
+ end
+ local separator = specification.separator or ","
+ if specification.preamble == true then
+ for f=1,#fields do
+ r[f] = lpegmatch(escape,tostring(fields[f]))
+ end
+ result[1] = concat(r,separator)
+ end
+ for i=1,#t do
+ local ti = t[i]
+ for f=1,#fields do
+ local field = ti[fields[f]]
+ if type(field) == "string" then
+ r[f] = lpegmatch(escape,field)
+ else
+ r[f] = tostring(field)
+ end
+ end
+ result[#result+1] = concat(r,separator)
+ end
+ return concat(result,"\n")
+ else
+ return ""
+ end
+end
+
+-- local nspaces = utilities.strings.newrepeater(" ")
+-- local escape = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P(1))^0)
+--
+-- local function toxml(t,d,result,step)
+-- for k, v in sortedpairs(t) do
+-- local s = nspaces[d]
+-- local tk = type(k)
+-- local tv = type(v)
+-- if tv == "table" then
+-- if tk == "number" then
+-- result[#result+1] = format("%s<entry n='%s'>",s,k)
+-- toxml(v,d+step,result,step)
+-- result[#result+1] = format("%s</entry>",s,k)
+-- else
+-- result[#result+1] = format("%s<%s>",s,k)
+-- toxml(v,d+step,result,step)
+-- result[#result+1] = format("%s</%s>",s,k)
+-- end
+-- elseif tv == "string" then
+-- if tk == "number" then
+-- result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,lpegmatch(escape,v),k)
+-- else
+-- result[#result+1] = format("%s<%s>%s</%s>",s,k,lpegmatch(escape,v),k)
+-- end
+-- elseif tk == "number" then
+-- result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,tostring(v),k)
+-- else
+-- result[#result+1] = format("%s<%s>%s</%s>",s,k,tostring(v),k)
+-- end
+-- end
+-- end
+--
+-- much faster
+
+local nspaces = utilities.strings.newrepeater(" ")
+
local function toxml(t,d,result,step)
- for k, v in table.sortedpairs(t) do
- if type(v) == "table" then
- if type(k) == "number" then
- result[#result+1] = format("%s<entry n='%s'>",d,k)
- toxml(v,d..step,result,step)
- result[#result+1] = format("%s</entry>",d,k)
+ for k, v in sortedpairs(t) do
+ local s = nspaces[d] -- inlining this is somewhat faster but gives more formatters
+ local tk = type(k)
+ local tv = type(v)
+ if tv == "table" then
+ if tk == "number" then
+ result[#result+1] = formatters["%s<entry n='%s'>"](s,k)
+ toxml(v,d+step,result,step)
+ result[#result+1] = formatters["%s</entry>"](s,k)
+ else
+ result[#result+1] = formatters["%s<%s>"](s,k)
+ toxml(v,d+step,result,step)
+ result[#result+1] = formatters["%s</%s>"](s,k)
+ end
+ elseif tv == "string" then
+ if tk == "number" then
+ result[#result+1] = formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
else
- result[#result+1] = format("%s<%s>",d,k)
- toxml(v,d..step,result,step)
- result[#result+1] = format("%s</%s>",d,k)
+ result[#result+1] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
end
- elseif type(k) == "number" then
- result[#result+1] = format("%s<entry n='%s'>%s</entry>",d,k,v,k)
+ elseif tk == "number" then
+ result[#result+1] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
else
- result[#result+1] = format("%s<%s>%s</%s>",d,k,tostring(v),k)
+ result[#result+1] = formatters["%s<%s>%S</%s>"](s,k,v,k)
end
end
end
-function table.toxml(t,name,nobanner,indent,spaces)
+-- function table.toxml(t,name,nobanner,indent,spaces)
+-- local noroot = name == false
+-- local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+-- local indent = rep(" ",indent or 0)
+-- local spaces = rep(" ",spaces or 1)
+-- if noroot then
+-- toxml( t, inndent, result, spaces)
+-- else
+-- toxml( { [name or "root"] = t }, indent, result, spaces)
+-- end
+-- return concat(result,"\n")
+-- end
+
+function table.toxml(t,specification)
+ specification = specification or { }
+ local name = specification.name
local noroot = name == false
- local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
- local indent = rep(" ",indent or 0)
- local spaces = rep(" ",spaces or 1)
+ local result = (specification.nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+ local indent = specification.indent or 0
+ local spaces = specification.spaces or 1
if noroot then
- toxml( t, inndent, result, spaces)
+ toxml( t, indent, result, spaces)
else
- toxml( { [name or "root"] = t }, indent, result, spaces)
+ toxml( { [name or "data"] = t }, indent, result, spaces)
end
return concat(result,"\n")
end
@@ -144,7 +269,7 @@ function tables.encapsulate(core,capsule,protect)
end
for key, value in next, core do
if capsule[key] then
- print(format("\ninvalid inheritance '%s' in '%s': %s",key,tostring(core)))
+ print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
os.exit()
else
capsule[key] = value
@@ -158,7 +283,7 @@ function tables.encapsulate(core,capsule,protect)
__index = capsule,
__newindex = function(t,key,value)
if capsule[key] then
- print(format("\ninvalid overload '%s' in '%s'",key,tostring(core)))
+ print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
os.exit()
else
rawset(t,key,value)
@@ -168,7 +293,7 @@ function tables.encapsulate(core,capsule,protect)
end
end
-local function serialize(t,r,outer) -- no mixes
+local function fastserialize(t,r,outer) -- no mixes
r[#r+1] = "{"
local n = #t
if n > 0 then
@@ -176,27 +301,27 @@ local function serialize(t,r,outer) -- no mixes
local v = t[i]
local tv = type(v)
if tv == "string" then
- r[#r+1] = format("%q,",v)
+ r[#r+1] = formatters["%q,"](v)
elseif tv == "number" then
- r[#r+1] = format("%s,",v)
+ r[#r+1] = formatters["%s,"](v)
elseif tv == "table" then
- serialize(v,r)
+ fastserialize(v,r)
elseif tv == "boolean" then
- r[#r+1] = format("%s,",tostring(v))
+ r[#r+1] = formatters["%S,"](v)
end
end
else
for k, v in next, t do
local tv = type(v)
if tv == "string" then
- r[#r+1] = format("[%q]=%q,",k,v)
+ r[#r+1] = formatters["[%q]=%q,"](k,v)
elseif tv == "number" then
- r[#r+1] = format("[%q]=%s,",k,v)
+ r[#r+1] = formatters["[%q]=%s,"](k,v)
elseif tv == "table" then
- r[#r+1] = format("[%q]=",k)
- serialize(v,r)
+ r[#r+1] = formatters["[%q]="](k)
+ fastserialize(v,r)
elseif tv == "boolean" then
- r[#r+1] = format("[%q]=%s,",k,tostring(v))
+ r[#r+1] = formatters["[%q]=%S,"](k,v)
end
end
end
@@ -208,15 +333,67 @@ local function serialize(t,r,outer) -- no mixes
return r
end
-function table.fastserialize(t,prefix)
- return concat(serialize(t,{ prefix or "return" },true))
+-- local f_hashed_string = formatters["[%q]=%q,"]
+-- local f_hashed_number = formatters["[%q]=%s,"]
+-- local f_hashed_table = formatters["[%q]="]
+-- local f_hashed_true = formatters["[%q]=true,"]
+-- local f_hashed_false = formatters["[%q]=false,"]
+--
+-- local f_indexed_string = formatters["%q,"]
+-- local f_indexed_number = formatters["%s,"]
+-- ----- f_indexed_true = formatters["true,"]
+-- ----- f_indexed_false = formatters["false,"]
+--
+-- local function fastserialize(t,r,outer) -- no mixes
+-- r[#r+1] = "{"
+-- local n = #t
+-- if n > 0 then
+-- for i=1,n do
+-- local v = t[i]
+-- local tv = type(v)
+-- if tv == "string" then
+-- r[#r+1] = f_indexed_string(v)
+-- elseif tv == "number" then
+-- r[#r+1] = f_indexed_number(v)
+-- elseif tv == "table" then
+-- fastserialize(v,r)
+-- elseif tv == "boolean" then
+-- -- r[#r+1] = v and f_indexed_true(k) or f_indexed_false(k)
+-- r[#r+1] = v and "true," or "false,"
+-- end
+-- end
+-- else
+-- for k, v in next, t do
+-- local tv = type(v)
+-- if tv == "string" then
+-- r[#r+1] = f_hashed_string(k,v)
+-- elseif tv == "number" then
+-- r[#r+1] = f_hashed_number(k,v)
+-- elseif tv == "table" then
+-- r[#r+1] = f_hashed_table(k)
+-- fastserialize(v,r)
+-- elseif tv == "boolean" then
+-- r[#r+1] = v and f_hashed_true(k) or f_hashed_false(k)
+-- end
+-- end
+-- end
+-- if outer then
+-- r[#r+1] = "}"
+-- else
+-- r[#r+1] = "},"
+-- end
+-- return r
+-- end
+
+function table.fastserialize(t,prefix) -- so prefix should contain the =
+ return concat(fastserialize(t,{ prefix or "return" },true))
end
function table.deserialize(str)
if not str or str == "" then
return
end
- local code = loadstring(str)
+ local code = load(str)
if not code then
return
end
@@ -233,7 +410,7 @@ function table.load(filename)
if filename then
local t = io.loaddata(filename)
if t and t ~= "" then
- t = loadstring(t)
+ t = load(t)
if type(t) == "function" then
t = t()
if type(t) == "table" then
@@ -244,6 +421,10 @@ function table.load(filename)
end
end
+function table.save(filename,t,n,...)
+ io.savedata(filename,serialize(t,n == nil and true or n,...))
+end
+
local function slowdrop(t)
local r = { }
local l = { }
@@ -252,11 +433,11 @@ local function slowdrop(t)
local j = 0
for k, v in next, ti do
j = j + 1
- l[j] = format("%s=%q",k,v)
+ l[j] = formatters["%s=%q"](k,v)
end
- r[i] = format(" {%s},\n",concat(l))
+ r[i] = formatters[" {%t},\n"](l)
end
- return format("return {\n%s}",concat(r))
+ return formatters["return {\n%st}"](r)
end
local function fastdrop(t)
@@ -265,7 +446,7 @@ local function fastdrop(t)
local ti = t[i]
r[#r+1] = " {"
for k, v in next, ti do
- r[#r+1] = format("%s=%q",k,v)
+ r[#r+1] = formatters["%s=%q"](k,v)
end
r[#r+1] = "},\n"
end
@@ -273,7 +454,7 @@ local function fastdrop(t)
return concat(r)
end
-function table.drop(t,slow)
+function table.drop(t,slow) -- only { { a=2 }, {a=3} }
if #t == 0 then
return "return { }"
elseif slow == true then
@@ -282,3 +463,31 @@ function table.drop(t,slow)
return fastdrop(t) -- some 15% faster
end
end
+
+function table.autokey(t,k)
+ local v = { }
+ t[k] = v
+ return v
+end
+
+local selfmapper = { __index = function(t,k) t[k] = k return k end }
+
+function table.twowaymapper(t)
+ if not t then
+ t = { }
+ else
+ for i=0,#t do
+ local ti = t[i] -- t[1] = "one"
+ if ti then
+ local i = tostring(i)
+ t[i] = ti -- t["1"] = "one"
+ t[ti] = i -- t["one"] = "1"
+ end
+ end
+ t[""] = t[0] or ""
+ end
+ -- setmetatableindex(t,"key")
+ setmetatable(t,selfmapper)
+ return t
+end
+