diff options
author | Karl Berry <karl@freefriends.org> | 2021-01-18 22:15:49 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-01-18 22:15:49 +0000 |
commit | 1980641bf70a8a584e59c089c20b2a1d9d67b476 (patch) | |
tree | 5dedd735d7a0d41bd143f8e3881a30ab632fad29 /Master | |
parent | 5bf26a04d067aa2c554fd16660539e464fb01cca (diff) |
luakeys (18jan21)
git-svn-id: svn://tug.org/texlive/trunk@57463 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r-- | Master/texmf-dist/doc/luatex/luakeys/README.md | 52 | ||||
-rw-r--r-- | Master/texmf-dist/doc/luatex/luakeys/luakeys-debug.tex | 33 | ||||
-rw-r--r-- | Master/texmf-dist/doc/luatex/luakeys/luakeys.pdf | bin | 0 -> 140586 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/luatex/luakeys/luakeys.tex | 544 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty | 39 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/luakeys/luakeys.lua | 483 | ||||
-rwxr-xr-x | Master/tlpkg/bin/tlpkg-ctan-check | 3 | ||||
-rwxr-xr-x | Master/tlpkg/libexec/ctan2tds | 1 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/collection-luatex.tlpsrc | 1 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/luakeys.tlpsrc | 0 |
10 files changed, 1155 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/luatex/luakeys/README.md b/Master/texmf-dist/doc/luatex/luakeys/README.md new file mode 100644 index 00000000000..cb4c429d4cd --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luakeys/README.md @@ -0,0 +1,52 @@ +# luakeys + +`luakeys` is a Lua module that can parse key-value options like the +TeX packages [keyval](https://www.ctan.org/pkg/keyval), +[kvsetkeys](https://www.ctan.org/pkg/kvsetkeys), +[kvoptions](https://www.ctan.org/pkg/kvoptions), +[xkeyval](https://www.ctan.org/pkg/xkeyval), +[pgfkeys](https://www.ctan.org/pkg/pgfkeys) etc. do. `luakeys`, +however, accompilshes this task entirely, by using the Lua language and +doesn’t rely on TeX. Therefore this package can only be used with the +TeX engine LuaTeX. Since `luakeys` uses +[LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/), the parsing +mechanism should be pretty robust. + +## License + +Copyright 2021 Josef Friedrich + +This work may be distributed and/or modified under the +conditions of the LaTeX Project Public License, either version 1.3c +of this license or (at your option) any later version. +The latest version of this license is in + +http://www.latex-project.org/lppl.txt + +and version 1.3c or later is part of all distributions of LaTeX +version 2008/05/04 or later. + +This work has the LPPL maintenance status `maintained`. + +The Current Maintainer of this work is Josef Friedrich. + +## Tasks + +### Installing + +``` +make install +``` + +### Testing + +``` +luarocks install luaunit +lua test/tests.lua +``` + +or + +``` +make test +``` diff --git a/Master/texmf-dist/doc/luatex/luakeys/luakeys-debug.tex b/Master/texmf-dist/doc/luatex/luakeys/luakeys-debug.tex new file mode 100644 index 00000000000..499b1305f67 --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luakeys/luakeys-debug.tex @@ -0,0 +1,33 @@ +%% luakeys-debug.tex +%% Copyright 2021 Josef Friedrich +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3c or later is part of all distributions of LaTeX +% version 2008/05/04 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Josef Friedrich. +% +% This work consists of the files luakeys.lua, luakeys-debug.sty +% and luakeys-debug.tex. + +\directlua{ + luakeys = require('luakeys') +} + +\def\luakeysdebug#1{ + { + \tt + \parindent=0pt + \directlua{ + local result = luakeys.parse('#1') + tex.print(luakeys.stringify(result, true)) + luakeys.print(result) + } + } +} diff --git a/Master/texmf-dist/doc/luatex/luakeys/luakeys.pdf b/Master/texmf-dist/doc/luatex/luakeys/luakeys.pdf Binary files differnew file mode 100644 index 00000000000..ceaebbee048 --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luakeys/luakeys.pdf diff --git a/Master/texmf-dist/doc/luatex/luakeys/luakeys.tex b/Master/texmf-dist/doc/luatex/luakeys/luakeys.tex new file mode 100644 index 00000000000..75ffbc8c70d --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luakeys/luakeys.tex @@ -0,0 +1,544 @@ +\documentclass{ltxdoc} + +\usepackage{hyperref} +\EnableCrossrefs +\CodelineIndex +\RecordChanges + +\usepackage{mdframed} +\usepackage{minted} +\usepackage{luakeys-debug} +\usepackage{multicol} +\usepackage{luacode} +\usepackage{syntax} +\usemintedstyle{friendly} +\BeforeBeginEnvironment{minted}{\begin{mdframed}} +\AfterEndEnvironment{minted}{\end{mdframed}} +\setminted{ + breaklines=true, + fontsize=\footnotesize, +} +\def\lua#1{\mintinline{lua}|#1|} + +\begin{document} + +\providecommand*{\url}{\texttt} + +\title{The \textsf{luakeys} package} +\author{% + Josef Friedrich\\% + \url{josef@friedrich.rocks}\\% + \href{https://github.com/Josef-Friedrich/nodetree}{github.com/Josef-Friedrich/luakeys}% +} +\date{v0.1 from 2021/01/18} + +\maketitle + +\vfill + +%\luakeysdebug{level1={level2={level3={dim=1cm,bool=true,num=-1e-03,str=lua}}}} + +\begin{minted}{lua} +local luakeys = require('luakeys') +local kv = luakeys.parse('level1={level2={level3={dim=1cm,bool=true,num=-1e-03,str=lua}}}') +luakeys.print(kv) +\end{minted} + +\noindent +Result: + +\begin{center} +\begin{minted}{lua} +{ + ['level1'] = { + ['level2'] = { + ['level3'] = { + ['dim'] = 1864679, + ['bool'] = true, + ['num'] = -0.001 + ['str'] = 'lua', + } + } + } +} +\end{minted} +\end{center} + +\vfill + +\strut + +\newpage + +\tableofcontents + +\newpage + +\section{Introduction} + +\noindent +|luakeys| is a Lua module that can parse key-value options like the +\TeX{} packages \href{https://www.ctan.org/pkg/keyval}{keyval}, +\href{https://www.ctan.org/pkg/kvsetkeys}{kvsetkeys}, +\href{https://www.ctan.org/pkg/kvoptions}{kvoptions}, +\href{https://www.ctan.org/pkg/xkeyval}{xkeyval}, +\href{https://www.ctan.org/pkg/pgfkeys}{pgfkeys} etc. do. |luakeys|, +however, accompilshes this task entirely, by using the Lua language and +doesn’t rely on \TeX{}. Therefore this package can only be used with the +\TeX{} engine Lua\TeX{}. Since |luakeys| uses +\href{http://www.inf.puc-rio.br/~roberto/lpeg/}{LPeg}, the parsing +mechanism should be pretty robust. + +The TUGboat article +\href{http://www.tug.org/tugboat/tb30-1/tb94wright-keyval.pdf} +{“Implementing key–value input: An introduction” (Volume 30 (2009), No. 1)} +by Joseph Wright and Christian Feuersänger gives a good overview of the +available key-value packages. + +This package would not be possible without the article +\href{https://tug.org/TUGboat/tb40-2/tb125menke-lpeg.pdf} +{Parsing complex data formats in LuaTEX with LPEG (Volume 40 (2019), No. 2)}. + +%----------------------------------------------------------------------- +% +%----------------------------------------------------------------------- + +\clearpage + +\section{Syntax of the recognized key-value format} + +%% +% +%% + +\subsection{A attempt to put the syntax into words} + +A key-value pair is definied by an equal sign (\texttt{key=value}). +Several key-value pairs or values without keys are lined up with commas +(\texttt{key=value,value}) and build a key-value list. Curly brackets +can be used to create a recursive data structure of nested key-value +lists (\texttt{level1=\{level2=\{key=value,value\}\}}). + +%% +% +%% + +\subsection{An (incomplete) attempt to put the syntax into the (Extended) Backus-Naur form} + +\begin{grammar} +<list> ::= <list-item> | <list-item> <list> + +<list-item> ::= ( <key-value-pair> | <value-without-key> ) [ `,' ] + +<list-container> ::== `{' <list> `}' + +<value> ::= <boolean> + \alt <dimension> + \alt <number> + \alt <string-quoted> + \alt <string-unquoted> +\end{grammar} + +... to be continued + +%% +% +%% + +\subsection{Recognized data types} + +\subsubsection{boolean} + +The strings \texttt{true}, \texttt{TRUE} and \texttt{True} are converted +into Lua’s boolean type \lua{true}, the strings \texttt{false}, +\texttt{FALSE} and \texttt{False} into \lua{false}. + +\begin{multicols}{2} +\begin{minted}{latex} +\luakeysdebug{ + lower case true = true, + upper case true = TRUE, + title case true = True + lower case false = false, + upper case false = FALSE, + title case false = False, +} +\end{minted} +\begin{minted}{lua} +{ + ['lower case true'] = true, + ['upper case true'] = true, + ['title case true'] = true, + ['lower case false'] = false, + ['upper case false'] = false + ['title case false'] = false, +} +\end{minted} +\end{multicols} + +%% +% +%% + +\clearpage + +\subsubsection{number} + +\begin{multicols}{2} +\begin{minted}{latex} +\luakeysdebug{ + num1 = 4, + num2 = -4, + num3 = 0.4, + num4 = 4.57e-3, + num5 = 0.3e12, + num6 = 5e+20 +} +\end{minted} +\begin{minted}{lua} +{ + ['num1'] = 4, + ['num2'] = -4, + ['num3'] = 0.4, + ['num4'] = 0.00457, + ['num5'] = 300000000000.0, + ['num6'] = 5e+20 +} +\end{minted} +\end{multicols} + +%% +% +%% + +\clearpage + +\subsubsection{dimension} + +|luakeys| detects \TeX{} dimensions and automatically converts the +dimensions into scaled points using the function \lua{tex.sp(dim)}. Use +the option \lua{convert_dimensions} of the function +\lua{parse(kv_string, options)} to disalbe the automatic conversion. + +\begin{minted}{lua} +local result = parse('dim=1cm', { + convert_dimensions = false, +}) +\end{minted} + +If you want to convert a scale point into a unit string you can used the module +\href{https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua}{lualibs-util-dim.lua}. + +\begin{minted}{latex} +\begin{luacode} +require('lualibs') +tex.print(number.todimen(tex.sp('1cm'), 'cm', '%0.0F%s')) +\end{luacode} +\end{minted} + +\begin{center} +\begin{tabular}{rl} +\textbf{Unit name} & \textbf{Description} \\\hline +bp & big point \\ +cc & cicero \\ +cm & centimeter \\ +dd & didot \\ +em & horizontal measure of \emph{M} \\ +ex & vertical measure of \emph{x} \\ +in & inch \\ +mm & milimeter \\ +nc & new cicero \\ +nd & new didot \\ +pc & pica \\ +pt & point \\ +sp & scaledpoint \\ +\end{tabular} +\end{center} + +\begin{multicols}{2} +\begin{minted}{latex} +\luakeysdebug{ + bp = 1bp, + cc = 1cc, + cm = 1cm, + dd = 1dd, + em = 1em, + ex = 1ex, + in = 1in, + mm = 1mm, + nc = 1nc, + nd = 1nd, + pc = 1pc, + pt = 1pt, + sp = 1sp, +} +\end{minted} +\begin{minted}{lua} +{ + ['bp'] = 65781, + ['cc'] = 841489, + ['cm'] = 1864679, + ['dd'] = 70124, + ['em'] = 655360, + ['ex'] = 282460, + ['in'] = 4736286, + ['mm'] = 186467, + ['nc'] = 839105, + ['nd'] = 69925, + ['pc'] = 786432, + ['pt'] = 65536, + ['sp'] = 1, +} +\end{minted} +\end{multicols} + +%% +% +%% + +\subsubsection{string} + +There are two ways to specify strings: With or without quotes. If the +text have to contain commas or equal signs, then double quotation +marks must be used. + +\begin{multicols}{2} +\begin{minted}{latex} +\luakeysdebug{ + without quotes = no commas and equal signs are allowed, + with double quotes = ", and = are allowed", +} +\end{minted} +\begin{minted}{lua} +{ + ['without quotes'] = 'no commas and equal signs are allowed', + ['with double quotes'] = ', and = are allowed', +} +\end{minted} +\end{multicols} + +%----------------------------------------------------------------------- +% +%----------------------------------------------------------------------- + +\clearpage + +\section{Exported functions of the Lua module \texttt{luakeys.lua}} + +To learn more about the individual functions (local functions), pleasecommand +read the \href{https://josef-friedrich.github.io/luakeys/}{source code +documentation}, which was created with +\href{http://stevedonovan.github.io/ldoc/}{LDoc}. The Lua module exports +this functions: + +\begin{minted}{lua} +local luakeys = require('luakeys') +local parse = luakeys.parse +local render = luakeys.render +--local print = luakeys.print -- That would overwrite the built-in Lua function +\end{minted} + +%% +% +%% + +\subsection{\texttt{parse(kv\_string, options)}: table} + +The function \lua{parse(input_string, options)} is the main method of +this module. It parses a key-value string into a Lua table. + +\begin{minted}{latex} +\newcommand{\mykeyvalcmd}[1][]{ + \directlua{ + result = luakeys.parse('#1') + luakeys.print(result) + } + #2 +} +\mykeyvalcmd[one=1]{test} +\end{minted} + +\noindent +In plain \TeX: + +\begin{minted}{latex} +\def\mykeyvalcommand#1{ + \directlua{ + result = luakeys.parse('#1') + luakeys.print(result) + } +} +\mykeyvalcmd{one=1} +\end{minted} + +\noindent +The function can be called with a options table. This two options are +supported. + +\begin{minted}{lua} +local result = parse('one,two,three', { + convert_dimensions = false, + unpack_single_array_value = false +}) +\end{minted} + +%% +% +%% + +\subsection{\texttt{render(tbl)}: string} + +The function \lua{render(tbl)} reverses the function +\lua{parse(kv_string)}. It takes a Lua table and converts this table +into a key-value string. The resulting string usually has a different +order as the input table. + +\begin{minted}{lua} +result = luakeys.parse('one=1,two=2,tree=3,') +print(luakeys.render(result)) +--- one=1,two=2,tree=3, +--- or: +--- two=2,one=1,tree=3, +--- or: +--- ... +\end{minted} + +\noindent +In Lua only tables with 1-based consecutive integer keys (a.k.a. array +tables) can be parsed in order. + +\begin{minted}{lua} +result = luakeys.parse('one,two,three') +print(luakeys.render(result)) +--- one,two,three, (always) +\end{minted} + +%% +% +%% + +\subsection{\texttt{print(tbl): void}} + +The function \lua{print(tbl)} pretty prints a Lua table to standard +output (stdout). It is a utility function that can be used to debug and +inspect the resulting Lua table of the function \lua{parse}. You have to +compile your \TeX{} document in a console to see the terminal output. + +%\luakeysdebug{level1={level2={key=value}}} + +\begin{minted}{lua} +result = luakeys.parse('level1={level2={key=value}}') +luakeys.print(result) +\end{minted} + +\noindent +The output should look like this: + +\begin{minted}{md} +{ + ['level1'] = { + ['level2'] = { + ['key'] = 'value', + }, +} +\end{minted} + +%----------------------------------------------------------------------- +% +%----------------------------------------------------------------------- + +\clearpage + +\section{Debug packages} + +Two small debug packages are included in |luakeys|. One debug package +can be used in \LaTeX{} (luakeys-debug.sty) and one can be used in plain +\TeX{} (luakeys-debug.tex). Both packages provide only one command: +|\luakeysdebug{kv-string}| + +\begin{minted}{latex} +\luakeysdebug{one,two,three} +\end{minted} + +\noindent +Then the following output should appear in the document: +\bigskip + +\luakeysdebug{one,two,three} + +%% +% +%% + +\subsection{For plain \TeX: luakeys-debug.tex} + +An example of how to use the command in plain \TeX: + +\begin{minted}{latex} +\input luakeys-debug.tex +\luakeysdebug{one,two,three} +\bye +\end{minted} + +%% +% +%% + +\subsection{For \LaTeX: luakeys-debug.sty} + +An example of how to use the command in \LaTeX: + +\begin{minted}{latex} +\documentclass{article} +\usepackage{luakeys-debug} +\begin{document} +\luakeysdebug[ + unpack single array values=false, + convert dimensions=false +]{one,two,three} +\end{document} +\end{minted} + +%----------------------------------------------------------------------- +% +%----------------------------------------------------------------------- + +\clearpage + +\section{Implementation} + +%% +% +%% + +\subsection{luakeys.lua} + +\inputminted[linenos=true]{lua}{luakeys.lua} + +%% +% +%% + +\clearpage + +\subsection{luakeys-debug.tex} + +\inputminted[linenos=true]{latex}{luakeys-debug.tex} + +%% +% +%% + +\clearpage + +\subsection{luakeys-debug.sty} + +\inputminted[linenos=true]{latex}{luakeys-debug.sty} + +\changes{v0.1}{2020/01/17}{Inital release} + +\pagebreak +\PrintChanges +\pagebreak +\PrintIndex +\end{document} diff --git a/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty b/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty new file mode 100644 index 00000000000..77dfa0063eb --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty @@ -0,0 +1,39 @@ +%% luakeys-debug.sty +%% Copyright 2021 Josef Friedrich +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3c or later is part of all distributions of LaTeX +% version 2008/05/04 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Josef Friedrich. +% +% This work consists of the files luakeys.lua, luakeys-debug.sty +% and luakeys-debug.tex. + +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{luakeys-debug}[2021/01/18 Debug package for luakeys.] + +%\input luakeys-debug.tex + +\directlua{ + luakeys = require('luakeys') +} + +\newcommand{\luakeysdebug}[2][]{ + { + \tt + \parindent=0pt + \directlua{ + local options_raw = luakeys.parse('#1') + local result = luakeys.parse('#2', options_raw) + tex.print(luakeys.stringify(result, true)) + luakeys.print(result) + } + } +} diff --git a/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua b/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua new file mode 100644 index 00000000000..e4ecaab7f70 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua @@ -0,0 +1,483 @@ +-- luakeys-debug.tex +-- Copyright 2021 Josef Friedrich +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3c +-- of this license or (at your option) any later version. +-- The latest version of this license is in +-- http://www.latex-project.org/lppl.txt +-- and version 1.3c or later is part of all distributions of LaTeX +-- version 2008/05/04 or later. +-- +-- This work has the LPPL maintenance status `maintained'. +-- +-- The Current Maintainer of this work is Josef Friedrich. +-- +-- This work consists of the files luakeys.lua, luakeys-debug.sty +-- and luakeys-debug.tex. + +--- A key-value parser written with Lpeg. +-- +-- Explanations of some LPeg notation forms: +-- +-- * `patt ^ 0` = `expression *` +-- * `patt ^ 1` = `expression +` +-- * `patt ^ -1` = `expression ?` +-- * `patt1 * patt2` = `expression1 expression2`: Sequence +-- * `patt1 + patt2` = `expression1 / expression2`: Ordered choice +-- +-- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.org/TUGboat/tb40-2/tb125menke-lpeg.pdf) +-- +-- @module luakeys + +local lpeg = require('lpeg') + +if not tex then + tex = {} + + -- Dummy function for the tests. + tex['sp'] = function (input) + return 1234567 + end +end + +--- Generate the PEG parser using Lpeg. +-- +-- @treturn userdata The parser +local function generate_parser(options) + -- Optional whitespace + local white_space = lpeg.S(' \t\n\r')^0 + + --- Match literal string surrounded by whitespace + local ws = function(match) + return white_space * lpeg.P(match) * white_space + end + + local boolean_true = + lpeg.P('true') + + lpeg.P('TRUE') + + lpeg.P('True') + + local boolean_false = + lpeg.P('false') + + lpeg.P('FALSE') + + lpeg.P('False') + + local number = lpeg.P({'number', + number = + lpeg.V('int') * + lpeg.V('frac')^-1 * + lpeg.V('exp')^-1, + + int = lpeg.V('sign')^-1 * ( + lpeg.R('19') * lpeg.V('digits') + lpeg.V('digit') + ), + + sign = lpeg.S('+-'), + digit = lpeg.R('09'), + digits = lpeg.V('digit') * lpeg.V('digits') + lpeg.V('digit'), + frac = lpeg.P('.') * lpeg.V('digits'), + exp = lpeg.S('eE') * lpeg.V('sign')^-1 * lpeg.V('digits'), + }) + + --- Define data type dimension. + -- + -- @return Lpeg patterns + local function build_dimension_pattern() + local sign = lpeg.S('-+') + local integer = lpeg.R('09')^1 + local tex_number = (integer^1 * (lpeg.P('.') * integer^1)^0) + (lpeg.P('.') * integer^1) + local unit + -- https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua + for _, dimension_extension in ipairs({'bp', 'cc', 'cm', 'dd', 'em', 'ex', 'in', 'mm', 'nc', 'nd', 'pc', 'pt', 'sp'}) do + if unit then + unit = unit + lpeg.P(dimension_extension) + else + unit = lpeg.P(dimension_extension) + end + end + + local dimension = (sign^0 * tex_number * unit) + + if options.convert_dimensions then + return dimension / tex.sp + else + return lpeg.C(dimension) + end + end + + --- Add values to a table in a two modes: + -- + -- # Key value pair + -- + -- If arg1 and arg2 are not nil, then arg1 is the key and arg2 is the + -- value of a new table entry. + -- + -- # Index value + -- + -- If arg2 is nil, then arg1 is the value and is added as an indexed + -- (by an integer) value. + -- + -- @tparam table table + -- @tparam mixed arg1 + -- @tparam mixed arg2 + -- + -- @treturn table + local add_to_table = function(table, arg1, arg2) + if arg2 == nil then + local index = #table + 1 + return rawset(table, index, arg1) + else + return rawset(table, arg1, arg2) + end + end + + return lpeg.P({ + 'list', + + list = lpeg.Cf( + lpeg.Ct('') * lpeg.V('list_item')^0, + add_to_table + ), + + list_container = + ws('{') * lpeg.V('list') * ws('}'), + + list_item = + lpeg.Cg( + lpeg.V('key_value_pair') + + lpeg.V('value') + ) * ws(',')^-1, + + key_value_pair = + (lpeg.V('key') * ws('=')) * (lpeg.V('list_container') + lpeg.V('value')), + + -- ./ for tikz style keys + key_word = lpeg.R('az', 'AZ', '09', './'), + + key = white_space * lpeg.C( + lpeg.V('key_word')^1 * + (lpeg.P(' ')^1 * lpeg.V('key_word')^1)^0 + ) * white_space, + + value = + lpeg.V('boolean') + + lpeg.V('dimension') + + lpeg.V('number') + + lpeg.V('string_quoted') + + lpeg.V('string_unquoted'), + + boolean = + boolean_true * lpeg.Cc(true) + + boolean_false * lpeg.Cc(false), + + dimension = build_dimension_pattern(), + + string_quoted = + white_space * lpeg.P('"') * + lpeg.C((lpeg.P('\\"') + 1 - lpeg.P('"'))^0) * + lpeg.P('"') * white_space, + + string_unquoted = + white_space * + lpeg.C((1 - lpeg.S('{},='))^1) * + white_space, + + number = + white_space * (number / tonumber) * white_space, + + }) +end + +local function trim(input_string) + return input_string:gsub('^%s*(.-)%s*$', '%1') +end + +--- Get the size of an array like table `{ 'one', 'two', 'three' }` = 3. +-- +-- @tparam table value A table or any input. +-- +-- @treturn number The size of the array like table. 0 if the input is +-- no table or the table is empty. +local function get_array_size(value) + local count = 0 + if type(value) == 'table' then + for _ in ipairs(value) do count = count + 1 end + end + return count +end + +--- Get the size of a table `{ one = 'one', 'two', 'three' }` = 3. +-- +-- @tparam table value A table or any input. +-- +-- @treturn number The size of the array like table. 0 if the input is +-- no table or the table is empty. +local function get_table_size(value) + local count = 0 + if type(value) == 'table' then + for _ in pairs(value) do count = count + 1 end + end + return count +end + +--- Unpack a single valued array table like `{ 'one' }` into `one` or +-- `{ 1 }` into `into`. +-- +-- @treturn If the value is a array like table with one non table typed +-- value in it, the unpacked value, else the unchanged input. +local function unpack_single_valued_array_table(value) + if + type(value) == 'table' and + get_array_size(value) == 1 and + get_table_size(value) == 1 and + type(value[1]) ~= 'table' + then + return value[1] + end + return value +end + +--- This normalization tasks are performed on the raw input table +-- coming directly from the PEG parser: +-- +-- 1. Trim all strings: ` text \n` into `text` +-- 2. Unpack all single valued array like tables: `{ 'text' }` +-- into `text` +-- +-- @tparam table raw The raw input table coming directly from the PEG +-- parser +-- +-- @treturn table A normalized table ready for the outside world. +local function normalize(raw, options) + local function normalize_recursive(raw, result, options) + for key, value in pairs(raw) do + if options.unpack_single_array_values then + value = unpack_single_valued_array_table(value) + end + if type(value) == 'table' then + result[key] = normalize_recursive(value, {}, options) + elseif type(value) == 'string' then + result[key] = trim(value) + else + result[key] = value + end + end + return result + end + return normalize_recursive(raw, {}, options) +end + +--- The function `stringify(tbl, for_tex)` converts a Lua table into a +-- printable string. Stringify a table means to convert the table into +-- a string. This function is used to realize the `print` function. +-- `stringify(tbl, true)` (`for_tex = true`) generates a string which +-- can be embeded into TeX documents. The macro `\luakeysdebug{}` uses +-- this option. `stringify(tbl, false)` or `stringify(tbl)` generate a +-- string suitable for the terminal. +-- +-- @tparam table input A table to stringify. +-- +-- @tparam boolean for_tex Stringify the table into a text string that +-- can be embeded inside a TeX document via tex.print(). Curly braces +-- and whites spaces are escaped. +-- +-- https://stackoverflow.com/a/54593224/10193818 +local function stringify(input, for_tex) + local line_break, start_bracket, end_bracket, indent + + if for_tex then + line_break = '\\par' + start_bracket = '\\{' + end_bracket = '\\}' + indent = '\\ \\ ' + else + line_break = '\n' + start_bracket = '{' + end_bracket = '}' + indent = ' ' + end + + local function stringify_inner(input, depth) + local output = {} + depth = depth or 0; + + local function add(depth, text) + table.insert(output, string.rep(indent, depth) .. text) + end + + for key, value in pairs(input) do + if (key and type(key) == 'number' or type(key) == 'string') then + key = string.format('[\'%s\']', key); + + if (type(value) == 'table') then + if (next(value)) then + add(depth, key .. ' = ' .. start_bracket); + add(0, stringify_inner(value, depth + 1, for_tex)); + add(depth, end_bracket .. ','); + else + add(depth, key .. ' = ' .. start_bracket .. end_bracket .. ','); + end + else + if (type(value) == 'string') then + value = string.format('\'%s\'', value); + else + value = tostring(value); + end + + add(depth, key .. ' = ' .. value .. ','); + end + end + end + + return table.concat(output, line_break) + end + + return start_bracket .. line_break .. stringify_inner(input, 1) .. line_break .. end_bracket +end + +--- For the LaTeX version of the macro +-- `\luakeysdebug[options]{kv-string}`. +-- +-- @tparam table options_raw Options in a raw format. The table may be +-- empty or some keys are not set. +-- +-- @treturn table +local function normalize_parse_options (options_raw) + if options_raw == nil then + options_raw = {} + end + local options = {} + + if options_raw['unpack single array values'] ~= nil then + options['unpack_single_array_values'] = options_raw['unpack single array values'] + end + + if options_raw['convert dimensions'] ~= nil then + options['convert_dimensions'] = options_raw['convert dimensions'] + end + + if options.convert_dimensions == nil then + options.convert_dimensions = true + end + + if options.unpack_single_array_values == nil then + options.unpack_single_array_values = true + end + + return options +end + +return { + stringify = stringify, + + --- Parse a LaTeX/TeX style key-value string into a Lua table. With + -- this function you should be able to parse key-value strings like + -- this example: + -- + -- show, + -- hide, + -- key with spaces = String without quotes, + -- string="String with double quotes: ,{}=", + -- dimension = 1cm, + -- number = -1.2, + -- list = {one,two,three}, + -- key value list = {one=one,two=two,three=three}, + -- nested key = { + -- nested key 2= { + -- key = value, + -- }, + -- }, + -- + -- The string above results in this Lua table: + -- + -- { + -- 'show', + -- 'hide', + -- ['key with spaces'] = 'String without quotes', + -- string = 'String with double quotes: ,{}=', + -- dimension = 1864679, + -- number = -1.2, + -- list = {'one', 'two', 'three'}, + -- key value list = { + -- one = 'one', + -- three = 'three', + -- two = 'two' + -- }, + -- ['nested key'] = { + -- ['nested key 2'] = { + -- key = 'value' + -- } + -- }, + -- } + -- + -- @tparam string kv_string A string in the TeX/LaTeX style key-value + -- format as described above. + -- + -- @tparam table options A table containing + -- settings: `convert_dimensions` `unpack_single_array_values` + -- + -- @treturn table A hopefully properly parsed table you can do + -- something useful with. + parse = function (kv_string, options) + if kv_string == nil then + return {} + end + options = normalize_parse_options() + + local parser = generate_parser(options) + return normalize(parser:match(kv_string), options) + end, + + --- The function `render(tbl)` reverses the function + -- `parse(kv_string)`. It takes a Lua table and converts this table + -- into a key-value string. The resulting string usually has a + -- different order as the input table. In Lua only tables with + -- 1-based consecutive integer keys (a.k.a. array tables) can be + -- parsed in order. + -- + -- @tparam table tbl A table to be converted into a key-value string. + -- + -- @treturn string A key-value string that can be passed to a TeX + -- macro. + render = function (tbl) + local function render_inner(tbl) + local output = {} + local function add(text) + table.insert(output, text) + end + for key, value in pairs(tbl) do + if (key and type(key) == 'string') then + if (type(value) == 'table') then + if (next(value)) then + add(key .. '={'); + add(render_inner(value)); + add('},'); + else + add(key .. '={},'); + end + else + add(key .. '=' .. tostring(value) .. ','); + end + else + add(tostring(value) .. ',') + end + end + return table.concat(output) + end + return render_inner(tbl) + end, + + --- The function `print(tbl)` pretty prints a Lua table to standard + -- output (stdout). It is a utility function that can be used to + -- debug and inspect the resulting Lua table of the function + -- `parse`. You have to compile your TeX document in a console to + -- see the terminal output. + -- + -- @tparam table tbl A table to be printed to standard output for + -- debugging purposes. + print = function(tbl) + print(stringify(tbl, false)) + end, + +} diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index 31670019b7e..8cef87f9b5e 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -471,7 +471,8 @@ my @TLP_working = qw( lua-alt-getopt lua-check-hyphen lua-physical lua-uca lua-ul lua-uni-algos lua-visual-debug luabibentry luabidi luacode luacolor luahyphenrules - luaimageembed luaindex luainputenc luaintro lualatex-doc lualatex-doc-de + luaimageembed luaindex luainputenc luaintro luakeys + lualatex-doc lualatex-doc-de lualatex-math lualatex-truncate lualibs luamesh luamplib luaotfload luapackageloader luaprogtable luarandom luasseq luatex85 luatexbase luatexja luatexko luatextra diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds index f46ea4185cf..10232131732 100755 --- a/Master/tlpkg/libexec/ctan2tds +++ b/Master/tlpkg/libexec/ctan2tds @@ -2024,6 +2024,7 @@ $standardtex 'lua2dox', 'NULL', # .def is lua code 'luabidi', '(arabmaths|footnoterule)|' . $standardtex, 'luaintro', 'NULL', # doc pkg + 'luakeys', '\.lua|' . $standardtex, 'lualatex-math', '\.sty', # not phst-doc.cls 'luamesh', '\.sty', # not lltxdoc.cls 'luapackageloader', '\.lua|' . $standardtex, diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc index 0a62557e84c..b5da8280d51 100644 --- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc @@ -33,6 +33,7 @@ depend luaimageembed depend luaindex depend luainputenc depend luaintro +depend luakeys depend lualatex-doc depend lualatex-math depend lualatex-truncate diff --git a/Master/tlpkg/tlpsrc/luakeys.tlpsrc b/Master/tlpkg/tlpsrc/luakeys.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Master/tlpkg/tlpsrc/luakeys.tlpsrc |