diff options
author | Norbert Preining <norbert@preining.info> | 2025-01-02 03:04:34 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2025-01-02 03:04:34 +0000 |
commit | 2eea71934e853f5e45633ccf0f7915b4f133919a (patch) | |
tree | b5d38f640b78d108926cd12a371f857bdecbb202 /macros/luatex/latex | |
parent | a22c35c9797ac178b8552f754a91fa976d850a53 (diff) |
CTAN sync 202501020304
Diffstat (limited to 'macros/luatex/latex')
-rw-r--r-- | macros/luatex/latex/luatbls/README.md | 27 | ||||
-rw-r--r-- | macros/luatex/latex/luatbls/luatbls.lua | 310 | ||||
-rw-r--r-- | macros/luatex/latex/luatbls/luatbls.pdf | bin | 0 -> 73500 bytes | |||
-rw-r--r-- | macros/luatex/latex/luatbls/luatbls.sty | 163 | ||||
-rw-r--r-- | macros/luatex/latex/luatbls/luatbls.tex | 410 |
5 files changed, 910 insertions, 0 deletions
diff --git a/macros/luatex/latex/luatbls/README.md b/macros/luatex/latex/luatbls/README.md new file mode 100644 index 0000000000..b2fc6681de --- /dev/null +++ b/macros/luatex/latex/luatbls/README.md @@ -0,0 +1,27 @@ +# luatbls + +Lua tables made accessible in LaTeX + +# License + +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. diff --git a/macros/luatex/latex/luatbls/luatbls.lua b/macros/luatex/latex/luatbls/luatbls.lua new file mode 100644 index 0000000000..a14310fc6d --- /dev/null +++ b/macros/luatex/latex/luatbls/luatbls.lua @@ -0,0 +1,310 @@ +--% Kale Ewasiuk (kalekje@gmail.com) +--% 2025-01-01 +--% 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._cspfx = 'dtbl' +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._def_tbl(ind, def, g) + local _tbl, _key = luatbls._get_tbl_index(ind) + if def == '' then def = luatbls._cspfx.._tbl..luatbls._make_alpha_key(_key) end + luatbls._def_tbl_one(luatbls[_tbl][_key], def, g) +end + + +function luatbls._def_tbl_some(tblseq, def, g) + for t, k, v in luatbls._iter_tbls_vals(tblseq) do + if def == '' then def = luatbls._cspfx..t end + local def2 = def .. luatbls._make_alpha_key(k) + luatbls._def_tbl_one(v, def2, g) + end +end + +function luatbls._def_tbl_one(v, cs, g) + if type(v) == 'table' then + for kk, vv in pairs(v) do + kk = luatbls._make_alpha_key(kk) + token.set_macro(cs..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] + if def == '' then def = luatbls._cspfx..tbl..key end + 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._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(tblseq, cmd) + for t, k, v in luatbls._iter_tbls_vals(tblseq) 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 diff --git a/macros/luatex/latex/luatbls/luatbls.pdf b/macros/luatex/latex/luatbls/luatbls.pdf Binary files differnew file mode 100644 index 0000000000..e8bc8a1694 --- /dev/null +++ b/macros/luatex/latex/luatbls/luatbls.pdf diff --git a/macros/luatex/latex/luatbls/luatbls.sty b/macros/luatex/latex/luatbls/luatbls.sty new file mode 100644 index 0000000000..cbbd5377a1 --- /dev/null +++ b/macros/luatex/latex/luatbls/luatbls.sty @@ -0,0 +1,163 @@ +% Kale Ewasiuk (kalekje@gmail.com) +% 2025-01-01 +% 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. + +\RequirePackage{luacode} +\RequirePackage{luakeys} +\RequirePackage{penlightplus} + + +\luadirect{luatbls = require'luatbls'} + + +\NewDocumentCommand{\tblnew}{m}{\luadirect{% initialize a tbl and set empty + luatbls[\luastring{#1}] = {} + luatbls._rec_tbl = \luastring{#1} +}} + + +\NewDocumentCommand{\tblchg}{ m }{\luadirect{% change recent table + luatbls._rec_tbl = luatbls._get_tbl_name(\luastring{#1}) +}} + +\NewDocumentCommand{\tblfrkv}{m +m O{}}{\luadirect{% parse a tbl from key-vals, naked are set to boolean + luatbls._rec_tbl_opts = luatbls._luakeys.parse(\luastring{#3}) + luatbls[\luastring{#1}] = luatbls._luakeys.parse(string.subpar(\luastring{#2}), luatbls._rec_tbl_opts) + luatbls._rec_tbl = \luastring{#1} +}} + +\NewDocumentCommand{\tblfrkvN}{m +m O{}}{\luadirect{% + luatbls._rec_tbl_opts = luatbls._luakeys.parse(\luastring{#3}) + luatbls[\luastring{#1}] = luatbls._luakeys.parse(string.subpar(\luastringN{#2}), luatbls._rec_tbl_opts) + luatbls._rec_tbl = \luastring{#1} +}} + +\NewDocumentCommand{\tblfrcsv}{m +m O{}}{\tblfrkv{#1}{#2}[naked_as_value=true,#3]}% naked are set to array + +\NewDocumentCommand{\tblfrcsvN}{m +m O{}}{\tblfrkvN{#1}{#2}[naked_as_value=true,#3]} + +\NewDocumentCommand{\tblkvundefcheck}{}{\luadirect{luatbls._check_recent_tbl_undefault()}}% check defaults list and throw error if foreign keys were used + + + +\NewDocumentCommand{\tblapp}{m m}{\luadirect{% append to a table (ie using integer index) with a value (second arg) + __tbl__ = luatbls._get_tbl_name(\luastring{#1}) + table.insert(luatbls[__tbl__], \luastring{#2}) +}} + + +\NewDocumentCommand{\tblappN}{m m}{\luadirect{% append to a table (ie using integer index) with a value (second arg) # + __tbl__ = luatbls._get_tbl_name(\luastring{#1}) + table.insert(luatbls[__tbl__], \luastringN{#2}) +}} + + +\NewDocumentCommand{\tblcon}{m m}{\luadirect{% concatenate to a table (ie using integer index) with a list of comma separated values (second arg) # + __tbl__ = luatbls._get_tbl_name(\luastring{#1}) + for k, v in ipairs(luatbls._luakeys.parse(string.subpar(\luastring{#2}), {naked_as_value=true})) do + table.insert(luatbls[__tbl__], v) + end +}} + + +\NewDocumentCommand{\tblconN}{m m}{\luadirect{% concatenate to a table (ie using integer index) with a list of comma separated values (second arg) # + __tbl__ = luatbls._get_tbl_name(\luastring{#1}) + for k, v in ipairs(luatbls._luakeys.parse(string.subpar(\luastringN{#2}), {naked_as_value=true})) do + table.insert(luatbls[__tbl__], v) + end +}} + + +\NewDocumentCommand{\tbladd}{m m}{\luadirect{% add a kv pair to a table + __tbl__, __key__ = luatbls._get_tbl_index(\luastring{#1}, true) + luatbls[__tbl__][__key__] = \luastring{#2} +}} + +\NewDocumentCommand{\tbladdN}{m m}{\luadirect{% add a kv pair to a table + __tbl__, __key__ = luatbls._get_tbl_index(\luastring{#1}, true) + luatbls[__tbl__][__key__] = \luastringN{#2} +}} + + +\NewDocumentCommand{\tblupd}{m m}{\luadirect{% update to a table (ie using integer index) with a list of comma separated values (second arg) # + __tbl__ = luatbls._get_tbl_name(\luastring{#1}) + __tbl_new__ = luatbls._luakeys.parse(string.subpar(\luastring{#2}) + penlight.tablex.update(luatbls[__tbl__], __tbl_new__) +}} + + +\NewDocumentCommand{\tblupdN}{m m}{\luadirect{% update to a table (ie using integer index) with a list of comma separated values (second arg) # + __tbl__ = luatbls._get_tbl_name(\luastring{#1}) + __tbl_new__ = luatbls._luakeys.parse(string.subpar(\luastringN{#2}) + penlight.tablex.update(luatbls[__tbl__], __tbl_new__) +}} + + + + +\NewDocumentCommand{\tblget}{m}{\luadirect{% get an item using i syntax + luatbls._get_tbl_item(\luastring{#1}, true) +}} + +\NewDocumentCommand{\tblgetE}{m}{\luadirect{% get an item explicitly + tex.sprint(tostring(luatbls.#1)) +}} + +\NewDocumentCommand{\tblset}{m m}{\luadirect{% set item with {value} + luatbls._set_tbl_item(\luastring{#1}, \luastring{#2}) +}} + +\NewDocumentCommand{\tblsetN}{m m}{\luadirect{% set item with {value} + luatbls._set_tbl_item(\luastring{#1}, \luastringN{#2}) +}} + + +\NewDocumentCommand{\tblif}{ m m O{}}{\ifluax{luatbls._get_tbl_item(\luastring{#1})}{#2}[#3]} + +\NewDocumentCommand{\tblifv}{m m O{}}{\ifluaxv{luatbls._get_tbl_item(\luastring{#1})}{#2}[#3]} + +\NewDocumentCommand{\tblifeq}{m m m O{}}{\ifluax{luatbls._get_tbl_item(\luastring{#1}) == #2}{#3}[#4]} + + +\NewDocumentCommand{\tblfor}{ m m }{\luadirect{luatbls._for_tbl(\luastring{#1}, \luastring{#2})}} +\NewDocumentCommand{\tblforN}{ m m }{\luadirect{luatbls._for_tbl(\luastring{#1}, \luastringN{#2})}} + +\NewDocumentCommand{\tblforE}{ m m }{\luadirect{luatbls._for_tbl_e(luatbls.#1, \luastring{#2})}} +\NewDocumentCommand{\tblforEN}{ m m }{\luadirect{luatbls._for_tbl_e(luatbls.#1, \luastringN{#2})}} + + +\NewDocumentCommand{\tbldef}{ m m }{\luadirect{luatbls._def_tbl(\luastring{#1}, \luastring{#2})}} +\NewDocumentCommand{\tblgdef}{ m m }{\luadirect{luatbls._def_tbl(\luastring{#1}, \luastring{#2}, 'global')}} + +\NewDocumentCommand{\tbldefs}{ m O{} }{\luadirect{luatbls._def_tbl_some(\luastring{#1}, \luastring{#2})}} +\NewDocumentCommand{\tblgdefs}{ m O{} }{\luadirect{luatbls._def_tbl_some(\luastring{#1}, \luastring{#2}, 'global')}} + +\NewDocumentCommand{\tbldefxy}{ m O{} }{\luadirect{luatbls._def_tbl_coords(\luastring{#1}, \luastring{#2})}}% define #2x and #2y from a space delimited x-y pair + +\NewDocumentCommand{\tblapply}{m m O{}}{\luadirect{luatbls._apply_tbl(\luastring{#1}, \luastring{#2}, \luastring{#3})}} + +\NewDocumentCommand{\tblprt}{s m}{% + \luadirect{penlight.wrth(luatbls._get_tbl(\luastring{#2}),'luatbls: '..\luastring{#2})}% + \IfBooleanTF{#1}{\PackageError{luatbls}{Program terminated by tblprt*}{}\stop}{}% +} + diff --git a/macros/luatex/latex/luatbls/luatbls.tex b/macros/luatex/latex/luatbls/luatbls.tex new file mode 100644 index 0000000000..13e9dbf30b --- /dev/null +++ b/macros/luatex/latex/luatbls/luatbls.tex @@ -0,0 +1,410 @@ +% Kale Ewasiuk (kalekje@gmail.com) +% 2025-01-01 +% 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. + + +\documentclass[11pt,parskip=half]{scrartcl} +\usepackage[left=0.75in,right=0.75in,top=1in,bottom=1in]{geometry} +\setlength{\parindent}{0ex} +\newcommand{\llcmd}[1]{\leavevmode\llap{\texttt{\detokenize{#1}}}} +\newcommand{\cmd}[1]{\texttt{\detokenize{#1}}} +\newcommand{\qcmd}[1]{``\cmd{#1}''} +\usepackage{url} +\usepackage[svgnames]{xcolor} +\usepackage{showexpl} +\lstset{explpreset={justification=\raggedright,pos=r,hsep=1cm,preset={\color{Navy}\small}}} +\setlength\ResultBoxRule{0mm} +\lstset{ + language=[LaTeX]TeX, + basicstyle=\ttfamily\footnotesize\color{blue}, + commentstyle=\ttfamily\footnotesize\color{gray}, + frame=none, + numbers=left, + numberstyle=\ttfamily\footnotesize\color{gray}, + prebreak=\raisebox{0ex}[0ex][0ex]{\color{gray}\ensuremath{\hookleftarrow}}, + extendedchars=true, + breaklines=true, + tabsize=4, +} +\addtokomafont{title}{\raggedright} +\addtokomafont{author}{\raggedright} +\addtokomafont{date}{\raggedright} +\author{Kale Ewasiuk (\url{kalekje@gmail.com})} +\usepackage[yyyymmdd]{datetime}\renewcommand{\dateseparator}{--} +\date{\today} + +\usepackage{enumitem} + +\RequirePackage{luatbls} + +\title{luatbls} +\subtitle{Create, modify, and use Lua tables from within LaTeX} + + +\begin{document} +% + +\maketitle + + +\subsection*{Introduction} + +This package provides a Lua-table interface based on the \cmd{luakeys} package:\\ +\url{https://mirror.quantum5.ca/CTAN/macros/luatex/generic/luakeys/luakeys.pdf}\\ + +A global table called \cmd{luatbls} is created by loading this package. This table contains all user-defined tables +as well as internal package functions and settings. +User tables are stored directly under the module's table, so you can access a table within Lua by using: +\cmd{luatbls['mytable']} or \cmd{luatbls.mytable}. Further, \cmd{luatbls} can be called directly to obtain a table item by \cmd{luatbls'i'}, +where \cmd{i} is a ``flexible'' indexing system discussed in the next paragraphs. + +If you want to change the \cmd{luakeys} global parser options, you can adjust them by:\\ +\cmd{\directlua{luatbls._luakeys.opts.OPTION = VALUE}}\\ +For debugging, set \cmd{\directlua{luatbls._debug = true}} + + +In this documentation, arguments are represented as follows:\\ +\llcmd{t }: table name. If none provided, the most recent is used.\\ +\llcmd{k }: a string key.\\ +\llcmd{n }: an integer index.\\ +\llcmd{v }: a value.\\ +\llcmd{i }: the flexible indexer to get a single item.\\ +\llcmd{I }: the flexible indexer to get a single or multiple items.\\ +\llcmd{keyval }: a key-value string for the table. Standalone values are set to boolean.\\ +\llcmd{csv }: a key-value string where standalone values are parsed as array-like. + +There are a few ways to use the index (placeholder \cmd{i}.\\ +\cmd{t.k} where \cmd{t} is the table name and \cmd{k} is a string key (i.e. uses \cmd{luatbls.t.k}),\\ +\cmd{t/n} where \cmd{n} is an integer index (i.e. uses \cmd{t.k[n]}); note that negative indexes are allowed where -1 is the last element. +Alternatively, \cmd{t} and the symbol can be omitted, and simply pass the element without the table name as a prefix, +where the assumed table is the last one that was created or changed to (i.e. the most 'recent' table). +In this case, passing a number will assume an integer index. + +To use a \cmd{I}, +you can explicitly select tables and groups of keys by \cmd{t|seq}, or \cmd{t.k}, or \cmd{t/n}. +If no \cmd{|./} is provided, the recent table is used and the argument is assumed to be a sequence of keys. +\cmd{penlightplus}'s command \cmd{penlight.seq.tbltrain()} syntax is used for \cmd{seq}uences. +To summarize what \cmd{seq} can be, a comma-separated list of numbers or keys are used to specify which elements are iterated over. +NumPy-like slicing is possibly with \cmd{:} to choose integer ranges. +If \cmd{*} is provided, +all string keys are iterated. +If \cmd{I} is entirely blank, all elements of the recent table are used, which is equivalent to \cmd{t|*,:}.\\ + +Note: nested tables are currently not fully supported. Some variations of commands have an \cmd{E} suffix which indicates +that nested elements can be explicitly indexed. +The table name must be specified, and the validity of table names and keys are not checked. + +The \cmd{tbl} commands fully expand the +\cmd{t}, \cmd{k}, \cmd{n}, \cmd{i}, and \cmd{I} arguments. However a variation with an \cmd{N}-appended is usually provided +which will not expand the \cmd{v}, \cmd{keyval}, or \cmd{csv} args. + + + +%%% +\subsection*{Creating Tables} + +%\tblfrkv{kale}{kale=ewasiuk} +%\luadirect{penlight.wrth(luatbls'kale.kale')} + +\cmd{\tblnew{t}} declares a new table with name \cmd{t}\\ +\cmd{\tblchg{t}} changes the 'recent' table\\ +\\ +\cmd{\tblfrkv{t}{keyval}[opts]} new table from key-vals using \cmd{luakeys} \\ +\cmd{\tblfrkvN{t}{keyval}[opts]} does not expand key-val string \cmd{luakeys} \\ + +\cmd{\tblkvundefcheck} will throw an error if you use define a table from key-values +and use a key that was not specified in the luakeys parse options via \cmd{opts.defaults} or \cmd{opts.defs}.\\ +\\ + \cmd{\tblfrcsv{t}{csv}[opts]} a shorthand \cmd{\tblfrkv{t}{csv}[naked_as_value=true,opts]}, a good way to convert a comma-separated list to an array\\ + \cmd{\tblfrcsvN{t}{csv}[opts]} same as above, but the csv is not expanded.\\ + + +\subsection*{Setting, getting, and modifying} + + +\cmd{\tblset{i}{v}} sets a value of the table/index \cmd{i} to \cmd{v}\\ +\cmd{\tblsetN{i}{v}} same as above, but the value is not expanded.\\ +\\ +\cmd{\tblget{i}} gets the value and \cmd{tex.sprint()}s it\\ +\cmd{\tblgetE{t.k}} An 'explicit' version of tbl get. Use this for nested tables. The tbl name must be specified. +The validity of table names and keys are not checked.\\ + +\begin{LTXexample} +\tblfrkv{ex}{a,b,c,first=john,last=smith}% + [defaults={x=0,1=one,n=false,y=yes}] +\tblget{ex.a}\\ +\tblset{a}{tRuE!!} +\tblget{a}\\ +\tblget{ex.x}\\ +\tblget{.x}\\ +\tbladd{ex.newkey}{val}\tblget{newkey}\\ +\tbladd{nk}{VAL}\tblget{nk}\\ +\end{LTXexample} + +\begin{LTXexample} +\tblfrcsv{EX}{x={1,2,{1,2,3}},name=me} +\tblgetE{EX.x[1]}\\ +\tblgetE{EX.x[3][3]}\\ +\tblgetE{EX.name}\\ +\end{LTXexample} + + + +\cmd{\tbladd{i}{v}} add a new value to a table using index method\\ +\cmd{\tbladdN{i}{v}} above, but don't expand the value argument\\ +\\ +\cmd{\tblapp{t}{v}} append a \cmd{v}alue (integer-wise) to a \cmd{t}able\\ +\cmd{\tblappN{t}{v}}\\ +\\ +\cmd{\tblupd{t}{keyval}} update a \cmd{t}able with more \cmd{keyval}s\\ +\cmd{\tblupdN{t}{keyval}}\\ +\\ +\cmd{\tblcon{t}{csv}} concatenate array-style \cmd{csv} at the end of \cmd{t}\\ +\cmd{\tblconN{t}{csv}}\\ + + + +\subsection*{Conditionals} + +\cmd{\tblif{i}{tr}[fa]} runs code \cmd{tr} if the item is true else \cmd{fa}\\ +\cmd{\tblifv{i}{tr}[fa]} runs code \cmd{tr} if the item is truth-y (using \cmd{pl.hasval}) else \cmd{fa}\\ +\cmd{\tblifeq{i}{v}{tr}[fa]} checks the equivalency of to a user-specified value. Quotes must be used to indicate strings. + +\begin{LTXexample} +\tblfrcsv{x}{n=false,y=true, + k0="",kv=val,k2=6} +\tblif{n}{tr}[FA]\\ +\tblif{k0}{TR}[fa]\\ +\tblifv{k0}{tr}[FA]\\ +\tblifeq{kv}{'val'}{TR}[fa]\\ +\tblifeq{k2}{6}{TR}[fa]\\ +\end{LTXexample} + + +\subsection*{Iterating} +%TODO use PIPE SYMBOLZ \dsadsa + +\cmd{\tblfor{I}{template}} and \cmd{\tblforN} By default, iterates over all elements (\cmd{seq = *,:}), +but arbitrary indices/keys can be iterated over as per \cmd{penlight.seq.tbltrain} syntax. +\cmd{<k>} and \cmd{<v>} are placeholders in the template that are replaced by the keys and vals and can be changed by: +\cmd{\luadirect{luatbls._tblv = '<v>'}} + +If you want to iterate over a second-level tabel, you must use:\\ +\cmd{\tblforE} and \cmd{\tblforEN}, and explicitly provide the table and element. + +\begin{LTXexample} +\tblfrcsv{x}{n1,k1=v1,n2,n3,n4, + k2=v2,k3=v3,n5,n6} +1> \tblfor{:}{<k> = <v>; }\\ +2> \tblfor{*}{<k> = <v>; }\\ +3> \tblfor{1,*,2::2}{<k> = <v>; }\\ +4> \tblfor{ x | 1,*,2::2}{<k> = <v>; }\\ +\tblfrcsv{x}{a,{a,b,c}} +5> \tblforE{x[2]}{<k> = <v>; } +\end{LTXexample} + + + + +\subsection*{Definitions} +\cmd{\tbldef{i}[cs]} pushes the value to macro \cmd{\cs}. If cs is not provided, a default cs name of \cmd{dtbl<t><k>} is used.\\ +\cmd{\tblgdef{i}[cs]} like above but global definition is used.\\ + +\cmd{\tbldefs{I}[cspfx]} and \cmd{\tblgdefs{I}[cspfx]} defines items in table \cmd{t} (use recent if blank) with format \cmd{<cspfx><key>} where \cmd{cspfx} is a command sequence prefix. +If \cmd{cspfx} is blank, keys will be defined as \cmd{dtbl<t><k>}. +The default \cmd{cspfx} is changed by:\\\cmd{\luadirect{luatbls._cspfx = 'dtbl'}}\\ +Numerical keys are converted to capital letters: 1->A, 2->B. +It is recommended that tables and keys contain letters only for predictable behaviour when using this feature.\\ +If the value of a tbl's key is a table, every element in that table is defined, and the keys of that nested table is +appended to the cs: \cmd{dtbl<t><k1><k2>} (noting that numbers are converted to letters).\\ + + +\begin{LTXexample} +\tblfrcsv{EX}{n1,kA=v1,n2,n3,n4, + kB=v2,kC=v3,n5,n6} +1>\tbldef{kA}{mycs}\mycs\tbldef{kA}{}\dtblEXkA\\ +2> \tbldef{EX/1}{}\dtblEXA +\end{LTXexample} + +\begin{LTXexample} +\tblfrcsv{EX}{x={1,2,3}} +1>\tbldef{x}{mycs}\mycsA, \mycsB \\ +2>\tbldefs{}\dtblEXxA, \dtblEXxB +\end{LTXexample} + + + +\cmd{\tbldefxy{i}[cspfx]} splits the value of item by space, and creates two definitions \cmd{<cspfx>x} and \cmd{<cspfx>y}. +This might be useful for passing and using tikz coordinates, for example \cmd{xy=0 5}. An error is thrown if the values +are non-numeric.\\ + +\begin{LTXexample}[width=0.5\linewidth] +\tblfrkv{EX}{coords=12 34,other} +\tbldefxy{coords}[d]\dx, \dy \\ +\tbldefxy{coords}\dtblEXcoordsx, \dtblEXcoordsy \\ +\end{LTXexample} + + + +\subsection*{Utilities} + +\cmd{\tblapply{I}{func1(<v>,x,y)|:func2}[newtable]} apply a Lua function(s).\\If \cmd{newtable} is provided, a new table is created (and made the recent table) +and the original table is preserved.\\ The \cmd{.}, \cmd{/} or \cmd{|} indexer may be used to apply a function +to a single value or group of keys. +Multiple functions can be applied sequentially, separated by \cmd{|}. + +An arbitrary global function (including additional arguments) can be used, +but if a function is prefixed with a \cmd{:}, the class method will be called. +The \cmd{stringx} and \cmd{tablex} methods +from \cmd{penlight} are used depending on the value's type. See:\\ +\url{https://lunarmodules.github.io/Penlight/} + +Arguments can be specified with round brackets, where +\cmd{<v>} and \cmd{<k>} are used as a placeholder for the values and keys. +If no arguments are passed, it is assumed that the value is the only argument. +Note that luakeys parses the args, so quotes are not needed around strings for the args. + + +\begin{LTXexample}[width=0.5\linewidth] +\tblfrcsv{ex}{{a, b, c}} +\tblapply{}{:concat(<v>,-) | :upper}[new] +1> \tblgetE{ex[1][1]}\\ +2> \tblget{new/1}\\ +\tblfrcsv{ex}{HelloWorld} +\tblapply{}{string.sub(<v>,2,-5)}[new] +3> \tblget{new/1} +\end{LTXexample} + +%\begin{luacode*} +% penlight.wrth(penlight.tablex.list2comma({'a','b','c'}),'XYZ') +%\end{luacode*} + +%\tblprt*{ex} + + + + +\cmd{\tblprt{t}} pretty-print the table in console. Using \cmd{\tblprt*{}} will terminate the LaTeX program immediately after and issue an error, which could be useful for debugging. + + + +\luadirect{luatbls._debug = true} + +\subsubsection*{An Example} +% +\begin{LTXexample}\scriptsize +\NewDocumentCommand{\Exampletbl}{m}{ + \tblfrcsv{ex}{#1}[defaults={sal=Hello}] + %\tblkvundefcheck + \tblapply{ex.auth}{:list2comma} + \tblget{sal}, \tblget{auth}! Thank you for writing such a great novel. + My favorite parts were: + \begin{description} + \tblforEN{ex.chaps}{\item[<k>] <v> } + \end{description} + It was also very cool to learn that + \tblgetE{ex.num[1]}*\tblgetE{ex.num[2]}= + \luadirect{tex.sprint(tostring(luatbls.ex.num[1]*luatbls.ex.num[2]))} +} +\Exampletbl{auth={You,Me,Dupree}, + chaps={intro=very enticing, climax=thrilling, finale=what a twist!} + num={12,13} +} +\end{LTXexample} + +\tblprt{ex} + + + + + + +\end{document} + + + + + + +%\begin{luacode*} +% function prt_pyth() +% t = pl.tbls.pyth +% if not t.a then +% pl.tex.pkgerror('must pass a= to \\pyth') +% elseif not t.b then +% t.b = (tonumber(t.c)^2 - +% tonumber(t.a)^2)^0.5 +% elseif not t.c then +% t.c = (tonumber(t.a)^2 + +% tonumber(t.b)^2)^0.5 +% end +% local t = pl.tbx.fmt(t,'.'..t.d..'f') -- format table according to d decimals +% s = 'Right-angle sides a=$a and b=$b form a hypotenuse of c=$c' +% pl.tex.prt(s:fmt(t)) +% end +%\end{luacode*} +%\NewDocumentCommand{\pyth}{m}{% +% \tblfrkv{pyth}{#1}[defaults={a=false,b=false,c=false,d=0,e=extras}] +% \luadirect{prt_pyth()}% +%} +% +%\pyth{a=3,c=5}\\ +%\pyth{a=3.2,b=4.2,d=2}\\ +%C: \tblget{c} +% +%\end{LTXexample} + +% + +% +%\begin{luacode*} +%function prttol() +% local dec = penlight.tbls.tol[4] or 1 +% penlight.wrth(dec,'??') +% penlight.tbls.tol[3] = penlight.tbls.tol[3] or 3 +% penlight.tbls.tol[4] = penlight.tbls.tol[1]*(1.0-penlight.tbls.tol[3]/100.0) + 0.0 +% penlight.tbls.tol[5] = penlight.tbls.tol[1]*(1.0+penlight.tbls.tol[3]/100.0) + 0.0 +% --penlight.tbls.tol['k'] = 'fuckboi' +% --ttt = pl.tbx.fmt(penlight.tbls.tol, '.3f') +% penlight.wrth(('$1\\$2 (\\pmpct{$3} tolerance, $4\\ndash$5\\$2)'):fmt(penlight.tbls.tol, '4=.'..dec..'f, 5=.'..dec..'f'), 'XYZ') +%end +%\end{luacode*} +%\NewDocumentCommand{\prttol}{ m }{\tblfrcsv{tol}{#1}\luadirect{prttol()}}% {50.0,kV,3,P} % 50\us (\pmpct{20} tolerance, 40=--60\us), P is optional and precision of the range (number of decimals) +% +%\prttol{50,kV,3} +% +%\begin{luacode*} +% pl.wrth(pl.filterfiles('.',true,'.*%.tex'), 'FF') +%\end{luacode*} + + + + +%\begin{luacode*} +% for t, k, v in luatbls._iter_tbls_vals('my|*,:') do +% --for t, k, v in luatbls._iter_tbls_vals('my/1') do +% penlight.wrth({t,k,v}) +% end +%\end{luacode*} + + |