diff options
author | Karl Berry <karl@freefriends.org> | 2023-08-23 21:18:46 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2023-08-23 21:18:46 +0000 |
commit | b6c4ff7317347993f624897008ca7c59bf443198 (patch) | |
tree | 2d7c18b114ed2912fdc06024b08bfd2aadf54320 /Master/texmf-dist | |
parent | a800017648ea42670cf384c2386d0673b0e71860 (diff) |
penlightplus
git-svn-id: svn://tug.org/texlive/trunk@68037 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r-- | Master/texmf-dist/doc/luatex/penlightplus/README.md | 28 | ||||
-rw-r--r-- | Master/texmf-dist/doc/luatex/penlightplus/penlightplus.pdf | bin | 0 -> 55459 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/luatex/penlightplus/penlightplus.tex | 227 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua | 927 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty | 119 |
5 files changed, 1301 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/luatex/penlightplus/README.md b/Master/texmf-dist/doc/luatex/penlightplus/README.md new file mode 100644 index 00000000000..cc80374347d --- /dev/null +++ b/Master/texmf-dist/doc/luatex/penlightplus/README.md @@ -0,0 +1,28 @@ +# penlightplus + +Additions to the Penlight Lua Libraries. + + +# License + +Copyright (C) 2021-2023 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/Master/texmf-dist/doc/luatex/penlightplus/penlightplus.pdf b/Master/texmf-dist/doc/luatex/penlightplus/penlightplus.pdf Binary files differnew file mode 100644 index 00000000000..89b8236d668 --- /dev/null +++ b/Master/texmf-dist/doc/luatex/penlightplus/penlightplus.pdf diff --git a/Master/texmf-dist/doc/luatex/penlightplus/penlightplus.tex b/Master/texmf-dist/doc/luatex/penlightplus/penlightplus.tex new file mode 100644 index 00000000000..50f87fa38b0 --- /dev/null +++ b/Master/texmf-dist/doc/luatex/penlightplus/penlightplus.tex @@ -0,0 +1,227 @@ +% Kale Ewasiuk (kalekje@gmail.com) +% 2023-07-22 +% Copyright (C) 2021-2023 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} +\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{xcolor} +\usepackage{showexpl} +\lstset{explpreset={justification=\raggedright,pos=r,wide=true}} +\setlength\ResultBoxRule{0mm} +\lstset{ + language=[LaTeX]TeX, + basicstyle=\ttfamily\small, + commentstyle=\ttfamily\small\color{gray}, + frame=none, + numbers=left, + numberstyle=\ttfamily\small\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} + + +\RequirePackage{penlightplus} + +\title{penlightplus} +\subtitle{Additions to the Penlight Lua Libraries} + +\begin{document} +% + +\maketitle + +This package first loads the \cmd{[import]penlight} package.\\ +The \texttt{pl} option may be passed to this package to create an alias for \cmd{penlight}.\\ +\texttt{globals} option may be used to make several of the functions global (as discussed below). + +\subsection*{texlua usage} +If you want to use penlightplus.lua with the \texttt{texlua} interpreter +(no document is made, but useful for testing your Lua code), +you can access it by setting \cmd{__SKIP_TEX__ = true} before loading. For example: + \begin{verbatim} +package.path = package.path .. ';'..'path/to/texmf/tex/latex/penlightplus/?.lua' +package.path = package.path .. ';'..'path/to/texmf/tex/latex/penlight/?.lua' +penlight = require'penlight' + +__SKIP_TEX__ = true --only required if you want to use + --penlightplus without a LaTeX run +__PL_GLOBALS__ = true -- optional, include global definitions +require'penlightplus' +\end{verbatim} + + + + + + +\subsection*{} + +The following global variables are defined: + +\llcmd{__SKIP_TEX__} If using the \cmd{penlightplus} package with \cmd{texlua} (good for troubleshooting), set this global before loading \cmd{penlight}\\ +The gloals flags below are taken care of in the package options:\\ +\llcmd{__PL_}\cmd{GLOBALS__} If using package with \cmd{texlua} and you don't want to set some globals (described in next sections), set this global before to \cmd{true} loading \cmd{penlight}\\ +\cmd{__PL_NO_HYPERREF__} a flag used to change the behaviour of a function, depending on if you don't use the hyperref package\\ + +\subsubsection*{penlight additions} + +Some functionality is added to penlight/lua. + +\llcmd{pl.hasval(x)} Python-like boolean testing\\ +\llcmd{COMP'xyz'()} Python-like comprehensions:\\\url{https://lunarmodules.github.io/Penlight/libraries/pl.comprehension.html}\\ +\llcmd{math.mod(n,d)}, \cmd{math.mod2(n)} math modulous\\ +\llcmd{string.}\cmd{totable(s)} string a table of characters\\ +\llcmd{string.}\cmd{delspace(s)} clear spaces from string\\ +\llcmd{pl.}\cmd{char(n)} return letter corresponding to 1=a, 2=b, etc.\\ +\llcmd{pl.}\cmd{Char(n)} return letter corresponding to 1=A, 2=B, etc.\\ + +\llcmd{pl.utils.}\cmd{filterfiles}\cmd{(dir,filt,rec)} Get files from dir and apply glob-like filters. Set rec to \cmd{true} to include sub directories\\ + + +\subsubsection*{A \cmd{pl.tex.} module is added} +\llcmd{add_bkt}\cmd{_cnt(n), }\cmd{close_bkt_cnt(n), reset_bkt_cnt} functions to keep track of adding curly brackets as strings. \cmd{add} will return \cmd{n} (default 1) \{'s and increment a counter. \cmd{close} will return \cmd{n} \}'s (default will close all brackets) and decrement.\\ +\llcmd{_NumBkts} internal integer for tracking the number of brackets\\ +\llcmd{opencmd(cs)} prints \cmd{\cs}\{ and adds to the bracket counters.\\ +\\ +\llcmd{_xNoValue,}\cmd{_xTrue,_xFalse}: \cmd{xparse} equivalents for commands\\ +\\ +\llcmd{prt(x),prtn(x)} print without or with a newline at end. Tries to help with special characters or numbers printing.\\ +\llcmd{prtl(l),prtt(t)} print a literal string, or table\\ +\llcmd{wrt(x), wrtn(x)} write to log\\ +\llcmd{help_wrt}\cmd{(s1, s2)} pretty-print something to console. S2 is a flag to help you find.\\ +\llcmd{prt_array2d(tt)} pretty print a 2d array\\ +\\ +\llcmd{pkgwarn}\cmd{(pkg, msg1, msg2)} throw a package warning\\ +\llcmd{pkgerror}\cmd{(pkg, msg1, msg2, stop)} throw a package error. If stop is true, immediately ceases compile.\\ +\\ +\llcmd{defcmd}\cmd{(cs, val)} like \cmd{\gdef}\\ +\llcmd{newcmd}\cmd{(cs, val)} like \cmd{\newcommand}\\ +\llcmd{renewcmd}\cmd{(cs, val)} like \cmd{\renewcommand}\\ +\llcmd{prvcmd}\cmd{(cs, val)} like \cmd{\providecommand}\\ +\llcmd{deccmd}\cmd{(cs, dft, overwrite)} declare a command. If \cmd{dft} (default) is \cmd{nil}, \cmd{cs} is set +to a package warning saying \cmd{'cs' was declared and used in document, but never set}. If \cmd{overwrite} +is true, it will overwrite an existing command (using \cmd{defcmd}), otherwise, it will throw error like \cmd{newcmd}.\\ +\llcmd{get_ref_info(l)}accesses the \cmd{\r@label} and returns a table\\ + + + +\subsubsection*{global extras} +If \cmd{extrasglobals} is used and NOT \cmd{extras}, many additional globals are set for shortcuts\\ +All \cmd{pl.tex} modules are made global.\\ +\cmd{pl.hasval}, \cmd{pl.COMP}, \cmd{pl.utils.kpairs}, \cmd{pl.utils.npairs} become globals with the function name.\\ + + + +\subsection*{Macro helpers} +\cmd{\MakeluastringCommands[def]{spec}} will let \cmd{\plluastring(A|B|C..)} be \cmd{\luastring(N|O|T|F)} +based on the letters that \cmd{spec} is set to (or \cmd{def} if nothing is provided) +This is useful if you want to write a command with flexibility on argument expansion. +The user can specify \cmd{n}, \cmd{o}, \cmd{t}, and \cmd{f} (case insensitve) if they want +no, once, twice, or full expansion. + +% BELOW IS FOR TROUBLESHOOTING ABOVE +%\def\NOTexp{\ONEexp} +%\def\ONEexp{\TWOexp} +%\def\TWOexp{\TREexp} +%\def\TREexp{Fully expanded} +% +%\NewDocumentCommand{\luastringExpTest}{m m}{ +% \MakeluastringCommands{#1} +% \luadirect{texio.write_nl('VVVVVVVVVVVVVVVVVVVVVVVVVVVVV')} +% \luadirect{texio.write_nl(\plluastringA{#2}..' | Not')} +% \luadirect{texio.write_nl(\plluastringB{#2}..' | Once')} +% \luadirect{texio.write_nl(\plluastringC{#2}..' | Twice')} +% \luadirect{texio.write_nl(\plluastringD{#2}..' | Full')} +% \luadirect{texio.write_nl('VVVVVVVVVVVVVVVVVVVVVVVVVVVVV')} +%} +% +%\luastringExpTest{ n o t f }{\NOTexp} + + + + +\subsection*{Split stuff} +Splitting text (or a cmd) into oxford comma format via: +\cmd{\splitToComma[expansion level]{text}{text to split on}}: + +\begin{LTXexample} + +-\splitToComma{ j doe }{\and}-\\ +-\splitToComma{ j doe \and s else }{\and}-\\ +-\splitToComma{ j doe \and s else \and a per }{\and}-\\ +-\splitToComma{ j doe \and s else \and a per \and f guy}{\and}- + +\def\authors{j doe \and s else \and a per \and f guy} +\splitToComma[o]{\authors}{\and} +\end{LTXexample} + +The expansion level is up to two characters, \cmd{n|o|t|f}, to control teh expasion of each argument. + +\begin{LTXexample} +spliToItems: +\begin{itemize} + \splitToItems{kale\and john}{\and} + \splitToItems{kale -john -someone else}{-} +\end{itemize} +\end{LTXexample} + + + + + + +% \tblfrkv{tbl_def}{kale=cool,paul=gay,craig=fun} +% \tblfrkv{tbl}{kale,paul=gay} %[naked_as_value=true] +% +% \tblget{tbl}{kale}% +% \tblget{tbl}{paul}% +% \tblget{tbl}{craig}% +% +% \tblupd{tbl_def}{tbl}% +% +% \tblfrkvII{tbl}{kale=cool,paul=gay,craig=fun}{kale=weak,paul=sad} +% \tblget{tbl}{craig}% +% \tblget{tbl}{paul}% +% \tblget{tbl}{kale}% +% +% \NewDocumentCommand{\THINg}{ O{} m}{% +% \tblfrkvII{setti}{color=red,size=small}{#1} % make settings and update based on [arg=] +% {\color{\tblget{setti}{color}}\tblget{setti}{size} #2} +% } +% +% \THINg[color=blue,size=tiny]{Kale} + +\end{document}
\ No newline at end of file diff --git a/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua new file mode 100644 index 00000000000..ffdbe89bc58 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua @@ -0,0 +1,927 @@ +--% Kale Ewasiuk (kalekje@gmail.com) +--% 2023-07-22 +--% Copyright (C) 2021-2023 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. + + + + +__PL_PLUS__ = true +__PL_SKIP_TEX__ = __PL_SKIP_TEX__ or false --if declared true before here, it will use regular print functions +-- (for troubleshooting with texlua instead of actual use in lua latex) +__PL_GLOBALS__ = __PL_GLOBALS__ or false +__PL_NO_HYPERREF__ = __PL_NO_HYPERREF__ or false + + +penlight.luakeys = require'luakeys'() + + +penlight.COMP = require'pl.comprehension'.new() -- for comprehensions + +-- http://lua-users.org/wiki/SplitJoin -- todo read me!! + +penlight.tex = {} -- adding a sub-module for tex related stuff + +local bind = bind or penlight.func.bind + + +function penlight.hasval(x) -- if something has value + if (type(x) == 'function') or (type(x) == 'CFunction') or (type(x) == 'userdata') then + return true + elseif (x == nil) or (x == false) or (x == 0) or (x == '') or (x == {}) then + return false + elseif (type(x) ~= 'boolean') and (type(x) ~= 'number') and (type(x) ~= 'string') then -- something else? maybe ths not needed + if #x == 0 then -- one more check, probably no needed though, I was trying to cover other classes but they all tables + return false + else + return true + end + end + return true +end + + +-- Some simple and helpful LaTeX functions -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + +-- xparse defaults +penlight.tex._xTrue = '\\BooleanTrue ' +penlight.tex._xFalse = '\\BooleanFalse ' +penlight.tex._xNoValue = '-NoValue-' + +--Generic LuaLaTeX utilities for print commands or environments + +if not __PL_SKIP_TEX__ then + local function check_special_chars(s) -- todo extend to toher special chars? + if type(s) == 'string' then + if string.find(s, '[\n\r\t\0]') then + penlight.tex.pkgwarn('penlight', 'printing string with special (eg. newline) char, possible unexpected behaviour on string: '..s) + end + end + end + + -- NOTE: usage is a bit different than default. If number is first arg, you CANT change catcode. + -- We don't need that under normal use, use tex.print or tex.sprint if you need + function penlight.tex.prt(s, ...) -- print something, no new line after + check_special_chars(s) + if type(s) == 'number' then s = tostring(s) end + tex.sprint(s, ...) --can print lists as well, but will NOT put new line between them or anything printed + end + + function penlight.tex.prtn(s, ...) -- print with new line after, can print lists or nums. C-function not in Lua, apparantly + s = s or '' + check_special_chars(s) + if type(s) == 'number' then s = tostring(s) end + tex.print(s, ...) + end + + penlight.tex.wrt = texio.write + penlight.tex.wrtn = texio.write_nl +else + penlight.tex.prt = io.write + penlight.tex.prtn = print --print with new line + penlight.tex.wrt = io.write + penlight.tex.wrtn = io.write_nl +end + +function penlight.tex.prtl(str) -- prints a literal/lines string in latex, adds new line between them + for line in str:gmatch"[^\n]*" do -- gets all characters up to a new line + penlight.tex.prtn(line) + end +end + +-- todo option to specify between character? one for first table, on for recursives? +function penlight.tex.prtt(tab, d1, d2) -- prints a table with new line between each item + d1 = d1 or '' + d2 = d2 or '\\leavevmode\\\\' + for _, t in pairs(tab) do -- + if type(t) ~= 'table' then + if d1 == '' then + penlight.tex.prtn(t) + else + penlight.tex.prt(t, d1) + end + else + penlight.tex.prtn(d2) + penlight.tex.prtt(t,d1,d2) + end + end +end + +function penlight.tex.help_wrt(s1, s2) -- helpful printing, makes it easy to debug, s1 is object, s2 is note + local wrt2 = wrt or texio.write_nl or print + s2 = s2 or '' + wrt2('\nvvvvv '..s2..'\n') + if type(s1) == 'table' then + wrt2(penlight.pretty.write(s1)) + else + wrt2(tostring(s1)) + end + wrt2('\n^^^^^\n') +end +penlight.help_wrt = penlight.tex.help_wrt + +function penlight.tex.prt_array2d(t) + for _, r in ipairs(t) do + local s = '' + for _, v in ipairs(r) do + s = s.. tostring(v)..', ' + end + penlight.tex.prt(s) + penlight.tex.prt('\n') + end +end + +-- -- -- -- -- + +function penlight.tex.pkgwarn(pkg, msg1, msg2) + pkg = pkg or '' + msg1 = msg1 or '' + msg2 = msg2 or '' + tex.sprint('\\PackageWarning{'..pkg..'}{'..msg1..'}{'..msg2..'}') +end + +function penlight.tex.pkgerror(pkg, msg1, msg2, stop) + pkg = pkg or '' + msg1 = msg1 or '' + msg2 = msg2 or '' + stop = penlight.hasval(stop) + tex.sprint('\\PackageError{'..pkg..'}{'..msg1..'}{'..msg2..'}') + if stop then tex.sprint('\\stop') end -- stop on the spot (say that 10 times) +end + + +--definition helpers -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + +function penlight.tex.defmacro(cs, val) -- , will not work if val contains undefined tokens (so pre-define them if using..) + val = val or '' -- however this works for arbitrary command names (\@hello-123 etc allowed) + token.set_macro(cs, val, 'global') +end + + +function penlight.tex.defcmd(cs, val) -- fixes above issue, but only chars allowed in cs (and no @) + val = val or '' + tex.sprint('\\gdef\\'..cs..'{'..val..'}') +end + +function penlight.tex.defcmdAT(cs, val) -- allows @ in cs, + --however should only be used in preamble. I avoid \makeatother because I've ran into issues with cls and sty files using it. + val = val or '' + tex.sprint('\\makeatletter\\gdef\\'..cs..'{'..val..'}') +end + + + +function penlight.tex.prvcmd(cs, val) -- provide command via lua + if token.is_defined(cs) then + -- do nothing if token is defined already --pkgwarn('penlight', 'Definition '..cs..' is being overwritten') + else + penlight.tex.defcmd(cs, val) + end +end + +function penlight.tex.newcmd(cs, val) -- provide command via lua + if token.is_defined(cs) then + penlight.tex.pkgerror('penlight: newcmd',cs..' already defined') + else + penlight.tex.defcmd(cs, val) + end +end + +function penlight.tex.renewcmd(cs, val) -- provide command via lua + if token.is_defined(cs) then + penlight.tex.defcmd(cs, val) + else + penlight.tex.pkgerror('penlight: renewcmd',cs..' not defined') + end +end + +function penlight.tex.deccmd(cs, def, overwrite) -- declare a definition, placeholder throws an error if it used but not set! + overwrite = penlight.hasval(overwrite) + local decfun + if overwrite then decfun = penlight.tex.defcmd else decfun = penlight.tex.newcmd end + if def == nil then + decfun(cs, pkgerror('penlight', cs..' was declared and used in document, but never set')) + else + decfun(cs, def) + end +end + + +-- +-- -- todo add and improve this, options for args? +--local function defcmd_nest(cs) -- for option if you'd like your commands under a parent ex. \csparent{var} +-- tex.print('\\gdef\\'..cs..'#1{\\csname '..var..'--#1--\\endcsname}') +--end +-- +-- +--local function defcmd(cs, val, nargs) +-- if (nargs == nil) or (args == 0) then +-- token.set_macro(cs, tostring(val), 'global') +-- else +-- local args = '#1' +-- tex.print('\\gdef\\'..cs..args..'{'..val..'}') +-- -- todo https://tex.stackexchange.com/questions/57551/create-a-capitalized-macro-token-using-csname +-- -- \expandafter\gdef\csname Two\endcsname#1#2{1:#1, two:#2} --todo do it like this +-- end +--end + +-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + + + +-- when nesting commands, this makes it helpful to not worry about brackets +penlight.tex._NumBkts = 0 +--prt(opencmd('textbf')..opencmd('texttt')..'bold typwriter'..close_bkt_cnt()) + +function penlight.tex.opencmd(cmd) + return '\\'..cmd..add_bkt_cnt() +end + +function penlight.tex.reset_bkt_cnt(n) + n = n or 0 + _NumBkts = n +end + +function penlight.tex.add_bkt_cnt(n) + -- add open bracket n times, returns brackets + n = n or 1 + _NumBkts = _NumBkts + n + return ('{'):rep(n) +end + +function penlight.tex.close_bkt_cnt(n) + n = n or _NumBkts + local s = ('}'):rep(n) + _NumBkts = _NumBkts - n + return s +end + + + +function penlight.tex.aliasluastring(s, d) + s = s:delspace():upper():tolist() + d = d:delspace():upper():tolist() + for i, S in penlight.seq.enum(d:slice_assign(1,#s,s)) do + if (S == 'E') or (S == 'F') then S = '' end -- E or F is fully expanded + penlight.tex.prtn('\\let\\plluastring'..penlight.Char(i)..'\\luastring'..S) + end +end + + + + + +function penlight.tex.get_ref_info(l) + local n = 5 + if __PL_NO_HYPERREF__ then + local n = 2 + end + local r = token.get_macro('r@'..l) + local t = {} + if r == nil then + t = penlight.tablex.new(n, 0) -- make all 0s + r = '-not found-' + else + t = {r:match(("(%b{})"):rep(n))} + t = penlight.tablex.map(string.trimfl, t) + end + t[#t+1] = r -- add the og return of label + --penlight.help_wrt(t, 'ref info') + return t +end + +-- todo add regex pattern for cref info +--function penlight.tex.get_ref_info_all_cref(l) +-- local r = token.get_macro('r@'..l..'@cref') +-- if r == nil then +-- return r, 0, 0 +-- end +-- local sec, page = r:match("{([^}]*)}{([^}]*)}") +-- return r, sec, page +--end + + +-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + + + + + + +-- -- -- -- math stuff +function math.mod(a, b) -- math modulo, return remainder only + return a - (math.floor(a/b)*b) +end + +function math.mod2(a) -- math modulo 2 + return math.mod(a,2) +end + + + +-- -- -- -- string stuff +local lpeg = require"lpeg" +local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V + +local number = P{"number", + number = (V"int" * V"frac"^-1 * V"exp"^-1) / tonumber, + int = V"sign"^-1 * (R"19" * V"digits" + V"digit"), + digits = V"digit" * V"digits" + V"digit", + digit = R"09", + sign = S"+-", + frac = P"." * V"digits", + exp = S"eE" * V"sign"^-1 * V"digits", + } + + + + +function penlight.char(num) + return string.char(string.byte("a")+num-1) +end + +function penlight.Char(num) + return string.char(string.byte("A")+num-1) +end + + +local str_mt = getmetatable("") -- register functions with str + +function str_mt.__index.gnum(s) + return number:match(s) +end + +function str_mt.__index.gextract(s, pat) --extract a pattern from string, returns both + local s_extr = '' + local s_rem = s + for e in s:gmatch(pat) do + s_extr = s_extr..e + s_rem = s_rem:gsub(e,'') + end + return s_extr, s_rem +end + +function str_mt.__index.gfirst(s, t) -- get the first pattern found from a table of pattern + for _, pat in pairs(t) do + if string.find(s, pat) then + return pat + end + end +end + +function str_mt.__index.appif(S, W, B, O) --append W ord to S tring if B oolean true, otherwise O ther + --append Word to String + if B then --if b is true + S = S .. W + else --consider Other word + O = O or '' + S = S .. O + end + return S +end + + + function str_mt.__index.containsany(s, exp) + if type(exp) ~= 'table' then exp = {exp} end + for _, e in ipairs(exp) do + if s:find(e) then return true end + end + return false +end + +function str_mt.__index.containsanycase(s, exp) + if type(exp) ~= 'table' then exp = {exp} end + for _, e in ipairs(exp) do + if s:lower():find(e:lower()) then return true end + end + return false +end + +function str_mt.__index.totable(str) + local t = {} + for i = 1, #str do + t[i] = str:sub(i, i) + end + return t +end + +function str_mt.__index.tolist(str) + return penlight.List(str) +end + + +function str_mt.__index.upfirst(str) + return str:gsub('%a', function(x) return x:upper() end, 1) +end + +function str_mt.__index.delspace(str) + return str:gsub('%s','') +end + +function str_mt.__index.trimfl(str) + return str:sub(2,-2) +end + + + +-- -- -- -- function stuff + +function penlight.clone_function(fn) + local dumped = string.dump(fn) + local cloned = loadstring(dumped) + local i = 1 + while true do + local name = debug.getupvalue(fn, i) + if not name then + break + end + debug.upvaluejoin(cloned, i, fn, i) + i = i + 1 + end + return cloned +end + + + + +-- -- -- -- -- -- -- -- -- -- -- -- functions below extend the operator module + +function penlight.operator.strgt(a,b) return tostring(a) > tostring(b) end +function penlight.operator.strlt(a,b) return tostring(a) < tostring(b) end + + + +-- -- -- -- functions below are helpers for arrays and 2d + +local function compare_elements(a, b, op, ele) + op = op or penlight.oper.gt + ele = ele or 1 + return op(a[ele], b[ele]) +end + +local function comp_2ele_func(op, ele) -- make a 2 element comparison function, + --sort with function on element nnum + return bind(compare_elements, _1, _2, op, ele) +end + + + + + +-- table stuff below + + +function penlight.tablex.map_slice(func, T, j1, j2) + if type(j1) == 'string' then + return penlight.array2d.map_slice(func, {T}, ','..j1)[1] + else + return penlight.array2d.map_slice(func, {T}, 1, j1, 1, j2)[1] + end +end + +penlight.array2d.map_slice1 = penlight.tablex.map_slice + + +-- todo option for multiple filters with AND logic, like the filter files?? +function penlight.tablex.filterstr(t, exp, case) + -- case = case sensitive + case = penlight.hasval(case) + -- apply lua patterns to a table to filter iter + -- str or table of str's can be passed, OR logic is used if table is passed + if case then + return penlight.tablex.filter(t, bind(string.containsany,_1,exp)) + else + return penlight.tablex.filter(t, bind(string.containsanycase,_1,exp)) + end +end + + +function penlight.utils.filterfiles(...) + -- f1 is a series of filtering patterns, or condition + -- f2 is a series of filtering patters, or condition + -- (f1_a or f2_...) and (f2 .. ) must match + local args = table.pack(...) + -- todo -- check where boolean is for recursive or not, set starting argument + -- this could allow one to omit dir + -- todo if no boolean at all, assume dir = '.' and r = false + -- if boolean given, assume dir = '.' + local nstart = 3 + local r = args[2] + local dir = args[1] + if type(args[1]) == 'boolean' then + dir = '.' + r = args[1] + nstart = 2 + elseif type(args[2]) ~= 'boolean' then + dir = '.' + r = false + nstart = 1 + end + + local files + if r then files = penlight.dir.getallfiles(dir) + else files = penlight.dir.getfiles(dir) + end + for i=nstart,args.n do + files = penlight.tablex.filter(files, penlight.func.compose(bind(string.containsanycase,_1, args[i]), penlight.path.basename)) + end + return files +end + + + + +-- -- -- -- -- -- -- -- functions below extend the array2d module + + +function penlight.array2d.map_slice(func, M, i1, j1, i2, j2) -- map a function to a slice of a Matrix + func = penlight.utils.function_arg(1, func) + for i,j in penlight.array2d.iter(M, true, i1, j1, i2, j2) do + M[i][j] = func(M[i][j]) + end + return M +end + +penlight.array2d.map_slice2 = penlight.array2d.map_slice + +function penlight.array2d.map_cols(func, M, j1, j2) -- map function to columns of matrix + if type(j1) == 'string' then + return penlight.array2d.map_slice(func, M, ','..j1) + else + j2 = j2 or -1 + return penlight.array2d.map_slice(func, M, 1, j1, -1, j2) + end +end + +penlight.array2d.map_columns = penlight.array2d.map_cols + +function penlight.array2d.map_rows(func, M, i1, i2) -- map function to rows of matrix + if type(i1) == 'string' then + return penlight.array2d.map_slice(func, M, i1) + else + i2 = i2 or -1 + return penlight.array2d.map_slice(func, M, i1, 1, i2, -1) + end +end + + +-- -- -- -- -- -- -- -- + +function penlight.array2d.sortOP(M, op, ele) -- sort a 2d array based on operator criteria, ele is column, ie sort on which element + M_new = {} + for row in penlight.seq.sort(M, comp_2ele_func(op, ele)) do + M_new[#M_new+1] = row + end + return M_new +end + +function penlight.array2d.like(M1, v) + v = v or 0 + r, c = penlight.array2d.size(M1) + return penlight.array2d.new(r,c,v) +end + +function penlight.array2d.from_table(t) -- turns a labelled table to a 2d, label-free array + t_new = {} + for k, v in pairs(t) do + if type(v) == 'table' then + t_new_row = {k} + for _, v_ in ipairs(v) do + t_new_row[#t_new_row+1] = v_ + end + t_new[#t_new+1] = t_new_row + else + t_new[#t_new+1] = {k, v} + end + end + return t_new +end + +function penlight.array2d.toTeX(M, EL) --puts & between columns, can choose to end line with \\ if EL is true (end-line) + EL = EL or false + if EL then EL = '\\\\' else EL = '' end + return penlight.array2d.reduce2(_1..EL.._2, _1..'&'.._2, M)..EL +end + + +local function parse_numpy1d(i1, i2, iS) + i1 = tonumber(i1) + i2 = tonumber(i2) + if iS == ':' then + if i1 == nil then i1 = 1 end + if i2 == nil then i2 = -1 end + else + if i1 == nil then + i1 = 1 + i2 = -1 + else + i2 = i1 + end + end + return i1, i2 +end + +function penlight.array2d.parse_numpy2d_str(s) + s = s:gsub('%s+', '') + _, _, i1, iS, i2, j1, jS, j2 = string.find(s, "(%-?%d*)(:?)(%-?%d*),?(%-?%d*)(:?)(%-?%d*)") + i1, i2 = parse_numpy1d(i1, i2, iS) + j1, j2 = parse_numpy1d(j1, j2, jS) + return i1, j1, i2, j2 +end + + +local _parse_range = penlight.clone_function(penlight.array2d.parse_range) + +function penlight.array2d.parse_range(s) -- edit parse range to do numpy string if no letter passed + penlight.utils.assert_arg(1,s,'string') + if not s:find'%a' then + return penlight.array2d.parse_numpy2d_str(s) + end + return _parse_range(s) +end + + + + + + +-- https://tex.stackexchange.com/questions/38150/in-lualatex-how-do-i-pass-the-content-of-an-environment-to-lua-verbatim +penlight.tex.recordedbuf = "" +function penlight.tex.readbuf(buf) + i,j = string.find(buf, '\\end{%w+}') + if i==nil then -- if not ending an environment + penlight.tex.recordedbuf = penlight.tex.recordedbuf .. buf .. "\n" + return "" + else + return nil + end +end + +function penlight.tex.startrecording() + penlight.tex.recordedbuf = "" + luatexbase.add_to_callback('process_input_buffer', penlight.tex.readbuf, 'readbuf') +end + +function penlight.tex.stoprecording() + luatexbase.remove_from_callback('process_input_buffer', 'readbuf') + penlight.tex.recordedbuf = penlight.tex.recordedbuf:gsub("\\end{%w+}\n","") +end + + + +__PDFmetadata__ = {} +penlight.tex.add_xspace_intext = true + + +function penlight.tex.updatePDFtable(k, v, o) -- key val overwrite + k = k:upfirst() + if penlight.hasval(o) or (__PDFmetadata__[k] == nil) then + __PDFmetadata__[k] = v + end +end + +penlight.tex.writePDFmetadata = function(t) -- write PDF metadata to xmpdata file + t = t or __PDFmetadata__ + local str = '' + for k, v in pairs(t) do + k = k:upfirst() + str = str..'\\'..k..'{'..v..'}'..'\n' + end + penlight.utils.writefile(tex.jobname..'.xmpdata', str) +end + + + +function penlight.tex.clear_cmds_str(s) + return s:gsub('%s+', ' '):gsub('\\\\',' '):gsub('\\%a+',''):gsub('{',' '):gsub('}',' '):gsub('%s+',' '):strip() +end + +function penlight.tex.makePDFvarstr(s) + s = s:gsub('%s*\\sep%s+','\0'):gsub('%s*\\and%s+','\0') -- turn \and into \sep + s = penlight.tex.clear_cmds_str(s) + s = s:gsub('\0','\\sep ') + --penlight.tex.help_wrt(s,'PDF var string') + return s +end + +function penlight.tex.makeInTextstr(s) + local s, c_and = s:gsub('%s*\\and%s+','\0') + s = penlight.tex.clear_cmds_str(s) + if penlight.tex.add_xspace_intext then + s = s..'\\xspace' + end + if c_and == 1 then + s = s:gsub('\0',' and ') + elseif c_and > 1 then + s = s:gsub('\0',', ', c_and - 1) + s = s:gsub('\0',', and ') + end + --penlight.tex.help_wrt(s,'in text var string') + return s +end + +--todo decide on above or below + +function penlight.tex.list2comma(t) + local s = '' + if #t == 1 then + s = t[1] + elseif #t == 2 then + s = t:join(' and ') + elseif #t >= 3 then + s = t:slice(1,#t-1):join(', ')..', and '..t[#t] + end + return s +end + +function penlight.tex.split2comma(s, d) + local t = penlight.List(s:split(d)):map(string.strip) + penlight.tex.prt(penlight.tex.list2comma(t)) +end + +function penlight.tex.split2items(s, d) + local t = penlight.List(s:split(d)):map(string.strip) + for n, v in ipairs(t) do + penlight.tex.prtn('\\item '..v) + end +end + + + +if penlight.hasval(__PL_GLOBALS__) then + -- iterators + kpairs = penlight.utils.kpairs + npairs = penlight.utils.npairs + --enum = utils.enum + + for k,v in pairs(penlight.tablex) do -- extend the table table to contain tablex functions + if k == 'sort' then + table.sortk = v + elseif k == 'move' then + table.xmove = v + else + _G['table'][k] = v + end + end + table.join = table.concat -- alias + -- todo should tablex have all table functions + + penlight.tablex.concat = table.concat + penlight.tablex.insert = table.insert + penlight.tablex.maxn = table.maxn + penlight.tablex.remove = table.remove + penlight.tablex.sort = table.sort + + hasval = penlight.hasval + COMP = penlight.COMP + + -- shortcuts +-- http://stevedonovan.github.io/Penlight/api/libraries/penlight.utils.html + penlight.writefile = penlight.utils.writefile + penlight.readfile = penlight.utils.readfile + penlight.readlines = penlight.utils.readfile + penlight.filterfiles = penlight.utils.filterfiles + + penlight.a2 = penlight.array2d + penlight.tbl = penlight.tablex + + + for k,v in pairs(penlight.tex) do -- make tex functions global + _G[k] = v + end + +end + + + + + + --_xTrue = penlight.tex._xTrue + --_xFalse = penlight.tex._xFalse + --_xNoValue = penlight.tex._xNoValue + -- + --prt = penlight.tex.prt + --prtn = penlight.tex.prtn + --wrt = penlight.tex.wrt + --wrtn = penlight.tex.wrtn + -- + --prtl = penlight.tex.prtl + --prtt = penlight.tex.prtt + -- + --help_wrt = penlight.tex.help_wrt + --prt_array2d = penlight.tex.prt_array2d + -- + --pkgwarn = penlight.tex.pkgwarn + --pkgerror = penlight.tex.pkgerror + -- + --defcmd = penlight.tex.defcmd + --prvcmd = penlight.tex.prvcmd + --newcmd = penlight.tex.newcmd + --renewcmd = penlight.tex.renewcmd + --deccmd = penlight.tex.deccmd + -- + --_NumBkts = penlight.tex._NumBkts + --opencmd = penlight.tex.opencmd + --reset_bkt_cnt = penlight.tex.reset_bkt_cnt + --add_bkt_cnt = penlight.tex.add_bkt_cnt + --close_bkt_cnt = penlight.tex.close_bkt_cnt + + +-- graveyard + + +-- luakeys parses individual keys as ipairs, this changes the list to a pure map +--function penlight.luakeystomap(t) +-- local t_new = {} +-- for k, v in pairs(t) do +-- if type(k) == 'number' then +-- t_new[v] = true +-- else +-- t_new[k] = v +-- end +-- end +-- return t_new +--end +--if luakeys then -- if luakeys is already loaded +-- function luakeys.parseN(s, ...) +-- local t = luakeys.parse(s,...) +-- t = penlight.luakeystomap(t) +-- return t +-- end +--end +-- might not be needed + + + --local func = check_func(func) +--local function check_func(func) -- check if a function is a PE, if so, make it a function +-- if type(func) ~= 'function' then +-- return I(func) +-- end +-- return func +--end + +-- -- -- -- -- -- -- +-- -- -- -- functions below extend the array2d module + + +--function penlight.array2d.map_slice1(func, L, i1, i2) -- map a function to a slice of an array, can use PlcExpr +-- i2 = i2 or i1 +-- local len = #L +-- i1 = check_index(i1, len) +-- i2 = check_index(i2, len) +-- func = check_func(func) +-- for i in penlight.seq.range(i1,i2) do +-- L[i] = func(L[i]) +-- end +-- return L +--end + + -- used this below when iter was not working.. + --i1, j1, i2, j2 = check_slice(M, i1, j1, i2, j2) + --for i in penlight.seq.range(i1,i2) do + -- for j in penlight.seq.range(j1,j2) do + --end + -- penlight may have fixed this +--local function check_index(ij, rc) -- converts array index to positive value if negative +-- if type(ij) ~= 'number' then +-- return 1 +-- else +-- if ij < 0 then +-- ij = rc + ij + 1 +-- elseif ij > rc then +-- ij = rc +-- elseif ij == 0 then +-- ij = 1 +-- end +-- return ij +-- end +--end +--local function check_slice(M, i1, j1, i2, j2) -- ensure a slice is valid; i.e. all positive numbers +-- r, c = penlight.array2d.size(M) +-- i1 = check_index(i1 or 1, r) +-- i2 = check_index(i2 or r, r) +-- j1 = check_index(j1 or 1, c) +-- j2 = check_index(j2 or c, c) +-- return i1, j1, i2, j2 +--end + diff --git a/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty new file mode 100644 index 00000000000..0d49ff90d04 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty @@ -0,0 +1,119 @@ +% Kale Ewasiuk (kalekje@gmail.com) +% 2023-07-22 +% Copyright (C) 2021-2023 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. + +\ProvidesPackage{penlightplus}[2023-07-22] + +\RequirePackage{luacode} +\RequirePackage{luakeys} +\RequirePackage[import]{penlight} + +\DeclareOption{globals}{\luadirect{__PL_GLOBALS__ = true}} +\DeclareOption{pl}{\directlua{pl = penlight}} +\ProcessOptions*\relax + +\luadirect{require'penlightplus'} + + + + +\global\newcommand{\writePDFmetadata}{\luadirect{penlight.tex.writePDFmetadata()}} + +\NewDocumentCommand{\writePDFmetadatakv}{ s m }{ +\IfBooleanTF{#1}{% if *, overwrite everything + \luadirect{ + __PDFmetadata__ = penlight.luakeys.parse(\luastring{#2}) + penlight.tex.writePDFmetadata() + }}{ + \luadirect{ + __PDFmetadata__ = __PDFmetadata__ or {} + penlight.tablex.update(__PDFmetadata__, penlight.luakeys.parse(\luastring{#2})) + penlight.tex.writePDFmetadata() + }} +} + + +\gdef\luastringT#1{\luastring{\unexpanded\expandafter\expandafter\expandafter{#1}}} % expand luastring twice +\global\let\luastringF\luastring % fully expanded luastring + +% allow control over expansion of arguments to a latex function +\NewDocumentCommand{\MakeluastringCommands}{O{} m }{% #1 the desired commands #2 defaults + \luadirect{penlight.tex.aliasluastring(\luastring{#2},\luastring{#1})}% +} + +\NewDocumentCommand{\splitToComma}{ O{nn} m m }{% + \MakeluastringCommands[nn]{#1}% + \luadirect{penlight.tex.split2comma(\plluastringA{#2},\plluastringB{#3})}% +} + +\NewDocumentCommand{\splitToItems}{ O{NN} m m }{% + \MakeluastringCommands[nn]{#1}% + \luadirect{penlight.tex.split2items(\plluastringA{#2},\plluastringB{#3})}% +} + + +% +% +%\NewDocumentCommand{\tblnew}{m}{\luadirect{ +% _G[\luastring{#1}] = {} +%}} +% +%\NewDocumentCommand{\tblfrkv}{m m O{} }{\luadirect{ +% _G[\luastring{#1}] = penlight.luakeys.parse(\luastring{#2}, penlight.luakeys.parse(\luastring{#3})) +%}} +% +%\NewDocumentCommand{\tblset}{m m m}{\luadirect{ +% _G[\luastring{#1}][\luastring{#2}] = \luastring{#3} +%}} +% +%\NewDocumentCommand{\tblget}{m m}{\luadirect{ +% tex.sprint(tostring(_G[\luastring{#1}][\luastring{#2}])) +%}} +% +%\NewDocumentCommand{\tblidx}{m m}{\luadirect{ +% tex.sprint(tostring(_G[\luastring{#1}][#2])) +%}} +% +% +%\NewDocumentCommand{\tblupd}{ m m}{\luadirect{ +% penlight.tablex.update(_G[\luastring{#1}], _G[\luastring{#2}]) +%}} +% +%\NewDocumentCommand{\tblupdkv}{ m m }{\luadirect{ +% penlight.tablex.update(_G[\luastring{#1}], penlight.luakeys.parse(\luastring{#2})) +%}} +% +%% todo need a get table function. If blank is passed, the last table referenced is used. +%\NewDocumentCommand{\tblfrkvII}{ m m m }{\luadirect{ +% _G[\luastring{#1}] = penlight.tablex.update(penlight.luakeys.parse(\luastring{#2}), luakeys.parse(\luastring{#3})) +%}} + + +%\NewDocumentCommand{\tbldo}{m m O{} }{\luadirect{ +% % tex.sprint(tostring(_G[\luastring{#1}][#2])) +% % todo allow one to use pl tablex function here, +% % {tbl}{update}[arg1][arg2] +% penlight.tablex[\luastring[#1]](_G[\luastring{}], _G[\luastring{}]) +%}} + + |