diff options
Diffstat (limited to 'Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua')
-rw-r--r-- | Master/texmf-dist/tex/lualatex/luatbls/luatbls.lua | 373 |
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 |