summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/l-table.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/l-table.lua')
-rw-r--r--tex/context/base/mkiv/l-table.lua159
1 files changed, 142 insertions, 17 deletions
diff --git a/tex/context/base/mkiv/l-table.lua b/tex/context/base/mkiv/l-table.lua
index 552097e1c..3c1ce6daf 100644
--- a/tex/context/base/mkiv/l-table.lua
+++ b/tex/context/base/mkiv/l-table.lua
@@ -478,7 +478,7 @@ function table.fromhash(t)
return hsh
end
-local noquotes, hexify, handle, compact, inline, functions
+local noquotes, hexify, handle, compact, inline, functions, metacheck
local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key
'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
@@ -486,7 +486,7 @@ local reserved = table.tohash { -- intercept a language inconvenience: no reserv
'NaN', 'goto',
}
--- local function simple_table(t)
+-- local function is_simple_table(t)
-- if #t > 0 then
-- local n = 0
-- for _,v in next, t do
@@ -520,29 +520,67 @@ local reserved = table.tohash { -- intercept a language inconvenience: no reserv
-- return nil
-- end
-local function simple_table(t)
+-- local function is_simple_table(t)
+-- local nt = #t
+-- if nt > 0 then
+-- local n = 0
+-- for _,v in next, t do
+-- n = n + 1
+-- -- if type(v) == "table" then
+-- -- return nil
+-- -- end
+-- end
+-- if n == nt then
+-- local tt = { }
+-- for i=1,nt do
+-- local v = t[i]
+-- local tv = type(v)
+-- if tv == "number" then
+-- if hexify then
+-- tt[i] = format("0x%X",v)
+-- else
+-- tt[i] = tostring(v) -- tostring not needed
+-- end
+-- elseif tv == "string" then
+-- tt[i] = format("%q",v)
+-- elseif tv == "boolean" then
+-- tt[i] = v and "true" or "false"
+-- else
+-- return nil
+-- end
+-- end
+-- return tt
+-- end
+-- end
+-- return nil
+-- end
+
+local function is_simple_table(t,hexify) -- also used in util-tab so maybe public
local nt = #t
if nt > 0 then
local n = 0
- for _,v in next, t do
+ for _, v in next, t do
n = n + 1
- -- if type(v) == "table" then
- -- return nil
- -- end
+ if type(v) == "table" then
+ return nil
+ end
end
+ -- local haszero = t[0]
+ local haszero = rawget(t,0) -- don't trigger meta
if n == nt then
local tt = { }
for i=1,nt do
local v = t[i]
local tv = type(v)
if tv == "number" then
+ -- tt[i] = v -- not needed tostring(v)
if hexify then
tt[i] = format("0x%X",v)
else
- tt[i] = tostring(v) -- tostring not needed
+ tt[i] = v -- not needed tostring(v)
end
elseif tv == "string" then
- tt[i] = format("%q",v)
+ tt[i] = format("%q",v) -- f_string(v)
elseif tv == "boolean" then
tt[i] = v and "true" or "false"
else
@@ -550,11 +588,35 @@ local function simple_table(t)
end
end
return tt
+ elseif haszero and (n == nt + 1) then
+ local tt = { }
+ for i=0,nt do
+ local v = t[i]
+ local tv = type(v)
+ if tv == "number" then
+ -- tt[i+1] = v -- not needed tostring(v)
+ if hexify then
+ tt[i+1] = format("0x%X",v)
+ else
+ tt[i+1] = v -- not needed tostring(v)
+ end
+ elseif tv == "string" then
+ tt[i+1] = format("%q",v) -- f_string(v)
+ elseif tv == "boolean" then
+ tt[i+1] = v and "true" or "false"
+ else
+ return nil
+ end
+ end
+ tt[1] = "[0] = " .. tt[1]
+ return tt
end
end
return nil
end
+table.is_simple_table = is_simple_table
+
-- Because this is a core function of mkiv I moved some function calls
-- inline.
--
@@ -608,7 +670,8 @@ local function do_serialize(root,name,depth,level,indexed)
if compact then
last = #root
for k=1,last do
- if root[k] == nil then
+ -- if root[k] == nil then
+ if rawget(root,k) == nil then
last = k - 1
break
end
@@ -636,7 +699,7 @@ local function do_serialize(root,name,depth,level,indexed)
if next(v) == nil then
handle(format("%s {},",depth))
elseif inline then -- and #t > 0
- local st = simple_table(v)
+ local st = is_simple_table(v,hexify)
if st then
handle(format("%s { %s },",depth,concat(st,", ")))
else
@@ -673,6 +736,8 @@ local function do_serialize(root,name,depth,level,indexed)
else
handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) -- %.99g
end
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
if hexify then
handle(format("%s %s=0x%X,",depth,k,v))
@@ -695,6 +760,8 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif tk == "boolean" then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%q,",depth,k,v))
else
@@ -710,13 +777,15 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif tk == "boolean" then
handle(format("%s [%s]={},",depth,k and "true" or "false"))
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s={},",depth,k))
else
handle(format("%s [%q]={},",depth,k))
end
elseif inline then
- local st = simple_table(v)
+ local st = is_simple_table(v,hexify)
if st then
if tk == "number" then
if hexify then
@@ -726,6 +795,8 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif tk == "boolean" then
handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
else
@@ -746,6 +817,8 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif tk == "boolean" then
handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
else
@@ -763,6 +836,8 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif tk == "boolean" then
handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=load(%q),",depth,k,f))
else
@@ -778,6 +853,8 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif tk == "boolean" then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
+ elseif tk ~= "string" then
+ -- ignore
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%q,",depth,k,tostring(v)))
else
@@ -803,6 +880,7 @@ local function serialize(_handle,root,name,specification) -- handle wins
functions = specification.functions
compact = specification.compact
inline = specification.inline and compact
+ metacheck = specification.metacheck
if functions == nil then
functions = true
end
@@ -812,6 +890,9 @@ local function serialize(_handle,root,name,specification) -- handle wins
if inline == nil then
inline = compact
end
+ if metacheck == nil then
+ metacheck = true
+ end
else
noquotes = false
hexify = false
@@ -819,6 +900,7 @@ local function serialize(_handle,root,name,specification) -- handle wins
compact = true
inline = true
functions = true
+ metacheck = true
end
if tname == "string" then
if name == "return" then
@@ -843,8 +925,9 @@ local function serialize(_handle,root,name,specification) -- handle wins
end
if root then
-- The dummy access will initialize a table that has a delayed initialization
- -- using a metatable. (maybe explicitly test for metatable)
- if getmetatable(root) then -- todo: make this an option, maybe even per subtable
+ -- using a metatable. (maybe explicitly test for metatable). This can crash on
+ -- metatables that check the index against a number.
+ if metacheck and getmetatable(root) then
local dummy = root._w_h_a_t_e_v_e_r_
root._w_h_a_t_e_v_e_r_ = nil
end
@@ -950,6 +1033,41 @@ end
table.flattened = flattened
+local function collapsed(t,f,h)
+ if f == nil then
+ f = { }
+ h = { }
+ end
+ for k=1,#t do
+ local v = t[k]
+ if type(v) == "table" then
+ collapsed(v,f,h)
+ elseif not h[v] then
+ f[#f+1] = v
+ h[v] = true
+ end
+ end
+ return f
+end
+
+local function collapsedhash(t,h)
+ if h == nil then
+ h = { }
+ end
+ for k=1,#t do
+ local v = t[k]
+ if type(v) == "table" then
+ collapsedhash(v,h)
+ else
+ h[v] = true
+ end
+ end
+ return h
+end
+
+table.collapsed = collapsed -- 20% faster than unique(collapsed(t))
+table.collapsedhash = collapsedhash
+
local function unnest(t,f) -- only used in mk, for old times sake
if not f then -- and only relevant for token lists
f = { } -- this one can become obsolete
@@ -1056,7 +1174,7 @@ function table.count(t)
return n
end
-function table.swapped(t,s) -- hash
+function table.swapped(t,s) -- hash, we need to make sure we don't mess up next
local n = { }
if s then
for k, v in next, s do
@@ -1069,7 +1187,14 @@ function table.swapped(t,s) -- hash
return n
end
-function table.mirrored(t) -- hash
+function table.hashed(t) -- list, add hash to index (save because we are not yet mixed
+ for i=1,#t do
+ t[t[i]] = i
+ end
+ return t
+end
+
+function table.mirrored(t) -- hash, we need to make sure we don't mess up next
local n = { }
for k, v in next, t do
n[v] = k
@@ -1165,7 +1290,7 @@ function table.has_one_entry(t)
return t and next(t,next(t)) == nil
end
--- new
+-- new (rather basic, not indexed and nested)
function table.loweredkeys(t) -- maybe utf
local l = { }