diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/l-table.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/l-table.lua | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/l-table.lua b/Master/texmf-dist/tex/context/base/l-table.lua new file mode 100644 index 00000000000..ad2f1100186 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/l-table.lua @@ -0,0 +1,646 @@ +-- filename : l-table.lua +-- comment : split off from luat-lib +-- author : Hans Hagen, PRAGMA-ADE, Hasselt NL +-- copyright: PRAGMA ADE / ConTeXt Development Team +-- license : see context related readme files + +if not versions then versions = { } end versions['l-table'] = 1.001 + +table.join = table.concat + +function table.strip(tab) + local lst = { } + for k, v in ipairs(tab) do + -- s = string.gsub(v, "^%s*(.-)%s*$", "%1") + s = v:gsub("^%s*(.-)%s*$", "%1") + if s == "" then + -- skip this one + else + lst[#lst+1] = s + end + end + return lst +end + +--~ function table.sortedkeys(tab) +--~ local srt = { } +--~ for key,_ in pairs(tab) do +--~ srt[#srt+1] = key +--~ end +--~ table.sort(srt) +--~ return srt +--~ end + +function table.sortedkeys(tab) + local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed + for key,_ in pairs(tab) do + srt[#srt+1] = key + if kind == 3 then + -- no further check + else + local tkey = type(key) + if tkey == "string" then + -- if kind == 2 then kind = 3 else kind = 1 end + kind = (kind == 2 and 3) or 1 + elseif tkey == "number" then + -- if kind == 1 then kind = 3 else kind = 2 end + kind = (kind == 1 and 3) or 2 + else + kind = 3 + end + end + end + if kind == 0 or kind == 3 then + table.sort(srt,function(a,b) return (tostring(a) < tostring(b)) end) + else + table.sort(srt) + end + return srt +end + +function table.append(t, list) + for _,v in pairs(list) do + table.insert(t,v) + end +end + +function table.prepend(t, list) + for k,v in pairs(list) do + table.insert(t,k,v) + end +end + +function table.merge(t, ...) -- first one is target + t = t or {} + local lst = {...} + for i=1,#lst do + for k, v in pairs(lst[i]) do + t[k] = v + end + end + return t +end + +function table.merged(...) + local tmp, lst = { }, {...} + for i=1,#lst do + for k, v in pairs(lst[i]) do + tmp[k] = v + end + end + return tmp +end + +function table.imerge(t, ...) + local lst = {...} + for i=1,#lst do + local nst = lst[i] + for j=1,#nst do + t[#t+1] = nst[j] + end + end + return t +end + +function table.imerged(...) + local tmp, lst = { }, {...} + for i=1,#lst do + local nst = lst[i] + for j=1,#nst do + tmp[#tmp+1] = nst[j] + end + end + return tmp +end + +if not table.fastcopy then do + + local type, pairs, getmetatable, setmetatable = type, pairs, getmetatable, setmetatable + + local function fastcopy(old) -- fast one + if old then + local new = { } + for k,v in pairs(old) do + if type(v) == "table" then + new[k] = fastcopy(v) -- was just table.copy + else + new[k] = v + end + end + local mt = getmetatable(old) + if mt then + setmetatable(new,mt) + end + return new + else + return { } + end + end + + table.fastcopy = fastcopy + +end end + +if not table.copy then do + + local type, pairs, getmetatable, setmetatable = type, pairs, getmetatable, setmetatable + + local function copy(t, tables) -- taken from lua wiki, slightly adapted + tables = tables or { } + local tcopy = {} + if not tables[t] then + tables[t] = tcopy + end + for i,v in pairs(t) do -- brrr, what happens with sparse indexed + if type(i) == "table" then + if tables[i] then + i = tables[i] + else + i = copy(i, tables) + end + end + if type(v) ~= "table" then + tcopy[i] = v + elseif tables[v] then + tcopy[i] = tables[v] + else + tcopy[i] = copy(v, tables) + end + end + local mt = getmetatable(t) + if mt then + setmetatable(tcopy,mt) + end + return tcopy + end + + table.copy = copy + +end end + +-- rougly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack) + +function table.sub(t,i,j) + return { unpack(t,i,j) } +end + +function table.replace(a,b) + for k,v in pairs(b) do + a[k] = v + end +end + +-- slower than #t on indexed tables (#t only returns the size of the numerically indexed slice) + +function table.is_empty(t) + return not t or not next(t) +end + +function table.one_entry(t) + local n = next(t) + return n and not next(t,n) +end + +function table.starts_at(t) + return ipairs(t,1)(t,0) +end + +do + + -- one of my first exercises in lua ... + + -- 34.055.092 32.403.326 arabtype.tma + -- 1.620.614 1.513.863 lmroman10-italic.tma + -- 1.325.585 1.233.044 lmroman10-regular.tma + -- 1.248.157 1.158.903 lmsans10-regular.tma + -- 194.646 153.120 lmtypewriter10-regular.tma + -- 1.771.678 1.658.461 palatinosanscom-bold.tma + -- 1.695.251 1.584.491 palatinosanscom-regular.tma + -- 13.736.534 13.409.446 zapfinoextraltpro.tma + + -- 13.679.038 11.774.106 arabtype.tmc + -- 886.248 754.944 lmroman10-italic.tmc + -- 729.828 466.864 lmroman10-regular.tmc + -- 688.482 441.962 lmsans10-regular.tmc + -- 128.685 95.853 lmtypewriter10-regular.tmc + -- 715.929 582.985 palatinosanscom-bold.tmc + -- 669.942 540.126 palatinosanscom-regular.tmc + -- 1.560.588 1.317.000 zapfinoextraltpro.tmc + + table.serialize_functions = true + table.serialize_compact = true + table.serialize_inline = true + + local function key(k) + if type(k) == "number" then -- or k:find("^%d+$") then + return "["..k.."]" + elseif noquotes and k:find("^%a[%a%d%_]*$") then + return k + else + return '["'..k..'"]' + end + end + + local function simple_table(t) + if #t > 0 then + local n = 0 + for _,v in pairs(t) do + n = n + 1 + end + if n == #t then + local tt = { } + for i=1,#t do + local v = t[i] + local tv = type(v) + if tv == "number" or tv == "boolean" then + tt[#tt+1] = tostring(v) + elseif tv == "string" then + tt[#tt+1] = ("%q"):format(v) + else + tt = nil + break + end + end + return tt + end + end + return nil + end + + local function serialize(root,name,handle,depth,level,reduce,noquotes,indexed) + handle = handle or print + reduce = reduce or false + if depth then + depth = depth .. " " + if indexed then + handle(("%s{"):format(depth)) + else + handle(("%s%s={"):format(depth,key(name))) + end + else + depth = "" + local tname = type(name) + if tname == "string" then + if name == "return" then + handle("return {") + else + handle(name .. "={") + end + elseif tname == "number" then + handle("[" .. name .. "]={") + elseif tname == "boolean" then + if name then + handle("return {") + else + handle("{") + end + else + handle("t={") + end + end + if root and next(root) then + local compact = table.serialize_compact + local inline = compact and table.serialize_inline + local first, last = nil, 0 -- #root cannot be trusted here + if compact then + for k,v in ipairs(root) do -- NOT: for k=1,#root do (why) + if not first then first = k end + last = last + 1 + end + end + for _,k in pairs(table.sortedkeys(root)) do + local v = root[k] + local t = type(v) + if compact and first and type(k) == "number" and k >= first and k <= last then + if t == "number" then + handle(("%s %s,"):format(depth,v)) + elseif t == "string" then + if reduce and (v:find("^[%-%+]?[%d]-%.?[%d+]$") == 1) then + handle(("%s %s,"):format(depth,v)) + else + handle(("%s %q,"):format(depth,v)) + end + elseif t == "table" then + if not next(v) then + handle(("%s {},"):format(depth)) + elseif inline then + local st = simple_table(v) + if st then + handle(("%s { %s },"):format(depth,table.concat(st,", "))) + else + serialize(v,k,handle,depth,level+1,reduce,noquotes,true) + end + else + serialize(v,k,handle,depth,level+1,reduce,noquotes,true) + end + elseif t == "boolean" then + handle(("%s %s,"):format(depth,tostring(v))) + elseif t == "function" then + if table.serialize_functions then + handle(('%s loadstring(%q),'):format(depth,string.dump(v))) + else + handle(('%s "function",'):format(depth)) + end + else + handle(("%s %q,"):format(depth,tostring(v))) + end + elseif k == "__p__" then -- parent + if false then + handle(("%s __p__=nil,"):format(depth)) + end + elseif t == "number" then + handle(("%s %s=%s,"):format(depth,key(k),v)) + elseif t == "string" then + if reduce and (v:find("^[%-%+]?[%d]-%.?[%d+]$") == 1) then + handle(("%s %s=%s,"):format(depth,key(k),v)) + else + handle(("%s %s=%q,"):format(depth,key(k),v)) + end + elseif t == "table" then + if not next(v) then + handle(("%s %s={},"):format(depth,key(k))) + elseif inline then + local st = simple_table(v) + if st then + handle(("%s %s={ %s },"):format(depth,key(k),table.concat(st,", "))) + else + serialize(v,k,handle,depth,level+1,reduce,noquotes) + end + else + serialize(v,k,handle,depth,level+1,reduce,noquotes) + end + elseif t == "boolean" then + handle(("%s %s=%s,"):format(depth,key(k),tostring(v))) + elseif t == "function" then + if table.serialize_functions then + handle(('%s %s=loadstring(%q),'):format(depth,key(k),string.dump(v))) + else + handle(('%s %s="function",'):format(depth,key(k))) + end + else + handle(("%s %s=%q,"):format(depth,key(k),tostring(v))) + -- handle(('%s %s=loadstring(%q),'):format(depth,key(k),string.dump(function() return v end))) + end + end + if level > 0 then + handle(("%s},"):format(depth)) + else + handle(("%s}"):format(depth)) + end + else + handle(("%s}"):format(depth)) + end + end + + --~ name: + --~ + --~ true : return { } + --~ false : { } + --~ nil : t = { } + --~ string : string = { } + --~ 'return' : return { } + --~ number : [number] = { } + + function table.serialize(root,name,reduce,noquotes) + local t = { } + local function flush(s) + t[#t+1] = s + end + serialize(root, name, flush, nil, 0, reduce, noquotes) + return table.concat(t,"\n") + end + + function table.tohandle(handle,root,name,reduce,noquotes) + serialize(root, name, handle, nil, 0, reduce, noquotes) + end + + -- sometimes tables are real use (zapfino extra pro is some 85M) in which + -- case a stepwise serialization is nice; actually, we could consider: + -- + -- for line in table.serializer(root,name,reduce,noquotes) do + -- ...(line) + -- end + -- + -- so this is on the todo list + + table.tofile_maxtab = 2*1024 + + function table.tofile(filename,root,name,reduce,noquotes) + local f = io.open(filename,'w') + if f then + local concat = table.concat + local maxtab = table.tofile_maxtab + if maxtab > 1 then + local t = { } + local function flush(s) + t[#t+1] = s + if #t > maxtab then + f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice + t = { } + end + end + serialize(root, name, flush, nil, 0, reduce, noquotes) + f:write(concat(t,"\n"),"\n") + else + local function flush(s) + f:write(s,"\n") + end + serialize(root, name, flush, nil, 0, reduce, noquotes) + end + f:close() + end + end + +end + +--~ t = { +--~ b = "123", +--~ a = "x", +--~ c = 1.23, +--~ d = "1.23", +--~ e = true, +--~ f = { +--~ d = "1.23", +--~ a = "x", +--~ b = "123", +--~ c = 1.23, +--~ e = true, +--~ f = { +--~ e = true, +--~ f = { +--~ e = true +--~ }, +--~ }, +--~ }, +--~ g = function() end +--~ } + +--~ print(table.serialize(t), "\n") +--~ print(table.serialize(t,"name"), "\n") +--~ print(table.serialize(t,false), "\n") +--~ print(table.serialize(t,true), "\n") +--~ print(table.serialize(t,"name",true), "\n") +--~ print(table.serialize(t,"name",true,true), "\n") + +do + + local function flatten(t,f,complete) + for i=1,#t do + local v = t[i] + if type(v) == "table" then + if complete or type(v[1]) == "table" then + flatten(v,f,complete) + else + f[#f+1] = v + end + else + f[#f+1] = v + end + end + end + + function table.flatten(t) + local f = { } + flatten(t,f,true) + return f + end + + function table.unnest(t) -- bad name + local f = { } + flatten(t,f,false) + return f + end + + table.flatten_one_level = table.unnest + +end + +function table.insert_before_value(t,value,str) + for i=1,#t do + if t[i] == value then + table.insert(t,i,str) + return + end + end + table.insert(t,1,str) +end + +function table.insert_after_value(t,value,str) + for i=1,#t do + if t[i] == value then + table.insert(t,i+1,str) + return + end + end + t[#t+1] = str +end + +function table.are_equal(a,b,n,m) + if #a == #b then + n = n or 1 + m = m or #a + for i=n,m do + local ai, bi = a[i], b[i] + if (ai==bi) or (type(ai)=="table" and type(bi)=="table" and table.are_equal(ai,bi)) then + -- continue + else + return false + end + end + return true + else + return false + end +end + +function table.compact(t) + if t then + for k,v in pairs(t) do + if not next(v) then + t[k] = nil + end + end + end +end + +function table.tohash(t) + local h = { } + for _, v in pairs(t) do -- no ipairs here + h[v] = true + end + return h +end + +function table.fromhash(t) + local h = { } + for k, v in pairs(t) do -- no ipairs here + if v then h[#h+1] = k end + end + return h +end + +function table.contains(t, v) + if t then + for i=1, #t do + if t[i] == v then + return true + end + end + end + return false +end + +function table.count(t) + local n, e = 0, next(t) + while e do + n, e = n + 1, next(t,e) + end + return n +end + +function table.swapped(t) + local s = { } + for k, v in pairs(t) do + s[v] = k + end + return s +end + +--~ function table.are_equal(a,b) +--~ return table.serialize(a) == table.serialize(b) +--~ end + +function table.clone(t,p) -- t is optional or nil or table + if not p then + t, p = { }, t or { } + elseif not t then + t = { } + end + setmetatable(t, { __index = function(_,key) return p[key] end }) + return t +end + + +function table.hexed(t,seperator) + local tt = { } + for i=1,#t do tt[i] = string.format("0x%04X",t[i]) end + return table.concat(tt,seperator or " ") +end + +function table.reverse_hash(h) + local r = { } + for k,v in pairs(h) do + r[v] = (k:gsub(" ","")):lower() + end + return r +end + +function table.reverse(t) + local tt = { } + if #t > 0 then + for i=#t,1,-1 do + tt[#tt+1] = t[i] + end + end + return tt +end |