summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua')
-rw-r--r--Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua373
1 files changed, 373 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua b/Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua
new file mode 100644
index 00000000000..5cb1e29ab31
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua
@@ -0,0 +1,373 @@
+--% Kale Ewasiuk (kalekje@gmail.com)
+--% 2025-01-05
+--% Copyright (C) 2025 Kale Ewasiuk
+--%
+--% Permission is hereby granted, free of charge, to any person obtaining a copy
+--% of this software and associated documentation files (the "Software"), to deal
+--% in the Software without restriction, including without limitation the rights
+--% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+--% copies of the Software, and to permit persons to whom the Software is
+--% furnished to do so, subject to the following conditions:
+--%
+--% The above copyright notice and this permission notice shall be included in
+--% all copies or substantial portions of the Software.
+--%
+--% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+--% ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+--% TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+--% PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+--% SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+--% ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+--% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+--% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+--% OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+local luatbls = {}
+
+luatbls._luakeys = require'luakeys'()
+
+luatbls._rec_tbl = ''
+luatbls._rec_tbl_opts = {}
+
+luatbls._xysep = '%s+' -- spaces separate x-y coordinates
+luatbls._tblv = '<v>'
+luatbls._tblk = '<k>'
+luatbls._cstemp = 'ltbl<t><k>'
+luatbls._debug = false
+
+function luatbls._dprint(s1, s2)
+ if luatbls._debug then
+ penlight.wrth(s1, s1)
+ end
+end
+
+
+setmetatable(luatbls, {__call=function(t,s)return t._get_tbl_item(s) end})
+
+function luatbls._get_tbl_name(s)
+ s = s:strip()
+ if s == '' then
+ return luatbls._rec_tbl
+ end
+ for _, delim in ipairs{'.', '/', '|'} do
+ s = s:split(delim)[1] -- if tbl reference had a . | or /, an indexer was used
+ end
+ if luatbls[s] == nil then
+ local validtbls = penlight.List(penlight.tablex.kkeys(luatbls)):filter(function(s) return not s:startswith('_') end):join(', ')
+ penlight.tex.pkgerror('luatbls', 'Tried to access undefined tbl: "'..s..'". Valid tbls are: '..validtbls)
+ return luatbls._rec_tbl
+ end
+ return s
+end
+
+function luatbls._get_tbl(s)
+ s = luatbls._get_tbl_name(s)
+ return luatbls[s]
+end
+
+function luatbls._get_tbl_index(s, undec)
+ undec = undec or false -- flag for allowing undeclared indexing
+ local tbl = ''
+ local key = ''
+ local s_raw = s
+ if s:find('%.') then
+ local tt = s:split('.')
+ tbl = tt[1]
+ key = tt[2]
+ elseif s:find('/') then
+ local tt = s:split('/')
+ tbl = tt[1]
+ if tbl == '' then tbl = luatbls._rec_tbl end
+ key = tonumber(tonumber(tt[2]))
+ if key < 0 then key = #luatbls[tbl]+1+key end
+ else
+ tbl = luatbls._rec_tbl
+ key = tonumber(s) or s
+ if type(key) == 'number' and key < 0 then key = #luatbls[tbl]+1+key end
+ end
+ if tbl == '' then tbl = luatbls._rec_tbl end
+
+ if (luatbls[tbl] == nil) or ((not undec) and (luatbls[tbl][key] == nil)) then
+ penlight.tex.pkgerror('luatbls', 'Invalid tbl index attempt using: "'..s_raw..'". We tried to use tbl="' ..tbl..'" and key="'..key..'"'..
+ 'Note that "|" is forbidden here. The recent table is: '..luatbls._rec_tbl)
+ end
+ return tbl, key
+end
+
+function luatbls._get_tbl_seq(s)
+ local tblseq = s:split('|')
+ local tbl = nil
+ local seq = nil
+ if #tblseq == 1 then
+ tbl = luatbls._get_tbl_name('')
+ seq = tblseq[1]
+ if seq == '' then seq = ':,*' end
+ elseif #tblseq == 2 then
+ tbl = luatbls._get_tbl_name(tblseq[1])
+ seq = tblseq[2]
+ if seq == '' then seq = ':,*' end
+ end
+ return tbl, seq
+end
+
+
+
+function luatbls._get_tbl_item(s, p) -- get item with string, p means print value
+ p = p or false
+ local tbl, key = luatbls._get_tbl_index(s)
+ local itm = luatbls[tbl][key]
+ if p then
+ tex.sprint(tostring(itm))
+ end
+ return itm
+end
+
+
+function luatbls._set_tbl_item(s, v)
+ tbl, key = luatbls._get_tbl_index(s)
+ luatbls[tbl][key] = v
+end
+
+function luatbls._check_recent_tbl_undefault()
+ local undefaults = {}
+ if luatbls._rec_tbl_opts ~= nil then
+ local defaults = penlight.tablex.union(
+ luatbls._rec_tbl_opts.defs or {},
+ luatbls._rec_tbl_opts.defaults or {}
+ )
+ for k, v in pairs(luatbls[luatbls._rec_tbl]) do
+ if defaults[k] == nil then
+ undefaults[#undefaults+1] = k
+ end
+ end
+ if penlight.hasval(undefaults) then
+ penlight.tex.pkgerror('luatbls',
+ 'Invalid keys passed to tbl keyval: ' .. (', '):join(undefaults) ..
+ ' ; choices are: ' .. (', '):join(penlight.tablex.keys(defaults))
+ )
+ end
+ end
+end
+
+function luatbls._make_alpha_key(k)
+ if tonumber(k) ~= nil then
+ k = penlight.Char(tonumber(k))
+ end
+ return k
+end
+
+function luatbls._make_def_name(t, k, temp)
+ if temp == penlight.tex.xNoValue then
+ temp = luatbls._cstemp
+ end
+ k = luatbls._make_alpha_key(k)
+ return temp:gsub('<t>',t):gsub('<k>',k)
+end
+
+function luatbls._def_tbl(ind, def, g)
+ local _tbl, _key = luatbls._get_tbl_index(ind)
+ def = luatbls._make_def_name(_tbl, _key, def)
+ luatbls._def_tbl_one(luatbls[_tbl][_key], def, g)
+end
+
+
+function luatbls._def_tbl_some(Ind, def, g)
+ for t, k, v in luatbls._iter_tbls_vals(Ind) do
+ local newdef = luatbls._make_def_name(t, k, def)
+ luatbls._def_tbl_one(v, newdef, g)
+ end
+end
+
+
+function luatbls._def_tbl_one(v, cs, g)
+ if type(v) == 'table' then
+ for kk, vv in pairs(v) do
+ token.set_macro(cs..luatbls._make_alpha_key(kk), tostring(vv), g)
+ end
+ else
+ token.set_macro(cs, tostring(v), g)
+ end
+end
+
+function luatbls._def_tbl_coords(ind, def)
+ local tbl, key = luatbls._get_tbl_index(ind)
+ local str = luatbls[tbl][key]
+ def = luatbls._make_def_name(tbl, key, def)
+ local x, y = str:strip():splitv(luatbls._xysep)
+ if (not penlight.hasval(x)) or (not penlight.hasval(y)) then
+ penlight.tex.pkgerror('luatbls', '_def_tbl_coords function could not parse coordiantes given as "'..str..'" ensure two numbers separated by space are given!', '', true)
+ end
+ token.set_macro(def..'x', tostring(x))
+ token.set_macro(def..'y', tostring(y))
+end
+
+
+
+function luatbls._make_one_toggle(def, v, g)
+ tex.sprint(g..'\\providetoggle{'..def..'}')
+ tex.sprint(g..'\\toggle'..tostring(v)..'{'..def..'}')
+end
+
+function luatbls._make_toggle_tbl(ind, def, g)
+ g = g or ''
+ local t, k = luatbls._get_tbl_index(ind)
+ local v = luatbls[t][k]
+ def = luatbls._make_def_name(t, k, def)
+ luatbls._make_one_toggle(def, penlight.hasval(v), g)
+end
+
+function luatbls._make_toggles_tbl(Ind, def, g)
+ g = g or ''
+ for t, k, v in luatbls._iter_tbls_vals(Ind) do
+ if type(v) == 'boolean' then
+ local newdef = luatbls._make_def_name(t, k, def)
+ luatbls._make_one_toggle(newdef, v, g)
+ end
+ end
+end
+
+
+function luatbls._make_one_length(def, v, g)
+ tex.sprint(g..'\\providenewlength{\\'..def..'}')
+ tex.sprint(g..'\\deflength{\\'..def..'}{'..v..'}')
+end
+
+function luatbls._make_length_tbl(ind, def, g)
+ g = g or ''
+ local t, k = luatbls._get_tbl_index(ind)
+ local v = luatbls[t][k]
+ if type(v) == 'number' then
+ v = tostring(v)..'sp'
+ end
+ local def = luatbls._make_def_name(t, k, def)
+ luatbls._make_one_length(def, v, g)
+end
+
+function luatbls._make_lengths_tbl(Ind, def, g)
+ g = g or ''
+ for t, k, v in luatbls._iter_tbls_vals(Ind) do
+ if type(v) == 'number' then
+ v = tostring(v)..'sp'
+ end
+ if v:istexdim() then
+ local newdef = luatbls._make_def_name(t, k, def)
+ luatbls._make_one_length(newdef, v, g)
+ end
+ end
+end
+
+
+
+function luatbls._for_tbl_prt(k, v,cmd)
+ local cmd_new = cmd:gsub(luatbls._tblv, tostring(v)):gsub(luatbls._tblk, tostring(k)):gsub('(\\%w+) ', '%1') -- for some reason a space gets added to \cs, maybe
+ luatbls._dprint(cmd_new, '_for_tbl replacement')
+ tex.sprint(cmd_new)
+end
+
+function luatbls._for_tbl(Ind, cmd)
+ for t, k, v in luatbls._iter_tbls_vals(Ind) do
+ luatbls._for_tbl_prt(k, v,cmd)
+ end
+end
+
+function luatbls._for_tbl_e(tbl, cmd)
+ for k, v in pairs(tbl) do
+ luatbls._for_tbl_prt(k, v,cmd)
+ end
+end
+
+
+function luatbls._iter_tbls_vals(s)
+ if s:find('|') or ((s:find('%.') == nil) and (s:find('/') == nil)) then
+ local tbl, seq = luatbls._get_tbl_seq(s)
+ local keyval = {}
+ for key, val in penlight.seq.tbltrain(luatbls._get_tbl(tbl), seq) do
+ -- todo this should check validity of keys for sequences
+ keyval[#keyval+1] = {key, val}
+ end
+ luatbls._dprint(keyval, 'luatbls._iter_tbls_vals is iterating through tbl: '..tbl..' with sequence: '..seq)
+ local i = 0
+ return function()
+ i = i + 1
+ if i <= #keyval then
+ return tbl, keyval[i][1], keyval[i][2]
+ end
+ end
+ else
+ local tbl, key = luatbls._get_tbl_index(s)
+ local val = luatbls[tbl][key]
+ luatbls._dprint(keyval, 'luatbls._iter_tbls_vals is using tbl: '..tbl..' with key: '..key)
+ local i = 1
+ return function()
+ if i == 1 then
+ i = i + 1 -- only return the single value
+ return tbl, key, val
+ end
+ end
+ end
+end
+
+function luatbls._make_args(s, key, val)
+ local args = {val}
+ if s == nil then
+ return args
+ end
+ s = s:split(')')[1]
+ args = luatbls._luakeys.parse(s, {naked_as_value=true})
+ for i, v in ipairs(args) do
+ if v == luatbls._tblv then
+ args[i] = val
+ elseif v == luatbls._tblk then
+ args[i] = key
+ end
+ end
+ return args
+end
+
+function luatbls._make_func(f, key, val)
+ f = f:strip()
+ fargs = f:split('(')
+ f = fargs[1]
+ args = luatbls._make_args(fargs[2], key, val)
+ if f:startswith(':') then
+ f = f:sub(2,-1)
+ if type(val) == 'string' then
+ f = string[f]
+ elseif type(val) == 'table' then
+ f = penlight.tablex[f]
+ end
+ else
+ f = penlight._Gdot(f)
+ end
+ --luatbls._dprint() -- todo more debug printing
+ return f, args
+end
+
+
+
+function luatbls._make_newtbl(tblind, newtbl)
+ if tblind == '' then tblind = luatbls._rec_tbl..'|' end
+ if newtbl ~= '' then -- determine if new tbl is needed
+ local ogtbl = luatbls._get_tbl_name(tblind)
+ luatbls[newtbl] = penlight.tablex.deepcopy(luatbls[ogtbl])
+ luatbls._rec_tbl = newtbl
+ tblind, _ = string.gsub(tblind, ogtbl, newtbl, 1)
+ end
+ return tblind
+end
+
+function luatbls._apply_tbl(tblind, func, newtbl)
+ tblind = luatbls._make_newtbl(tblind, newtbl)
+ for _, f in pairs(func:split('|')) do
+ for tbl, key, val in luatbls._iter_tbls_vals(tblind) do
+ local thefunc, args = luatbls._make_func(f, key, val)
+ if thefunc == nil then
+ penlight.tex.pkgerror('luatbls', 'Tried to apply function: "'..f..'" to tbl value. It yielded no function. Ensure : is used for self-methods')
+ end
+ luatbls[tbl][key] = thefunc(penlight.utils.unpack(args))
+ end
+ end
+end
+
+return luatbls \ No newline at end of file