diff options
author | Karl Berry <karl@freefriends.org> | 2023-01-24 20:57:30 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2023-01-24 20:57:30 +0000 |
commit | ee97f6dbd2e6acf46b912ba49ad1e59892892707 (patch) | |
tree | d57f5081c212d6c288b80fa545be94c459075c4d /Master/texmf-dist/doc/latex/expkv-bundle | |
parent | 81de637794cea58fe8f1a7128ac5f305de1ae155 (diff) |
expkv-bundle (24jan23)
git-svn-id: svn://tug.org/texlive/trunk@65623 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/doc/latex/expkv-bundle')
18 files changed, 5059 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/README.md b/Master/texmf-dist/doc/latex/expkv-bundle/README.md new file mode 100644 index 00000000000..8d484d62d4e --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/README.md @@ -0,0 +1,46 @@ +------------------------------------------------------------------------------- +# `expkv-bundle` -- an expandable key=value implementation and more + +Version 2023-01-23 + +Released under the LaTeX Project Public License v1.3c or later +See http://www.latex-project.org/lppl.txt + +Hosted on the Island of TeX at https://gitlab.com/islandoftex/texmf/expkv-bundle + +------------------------------------------------------------------------------- + +Copyright (C) 2020-2023 Jonathan P. Spratte + +This work may be distributed and/or modified under the conditions of the LaTeX +Project Public License (LPPL), either version 1.3c of this license or (at your +option) any later version. The latest version of this license is in the file: + + http://www.latex-project.org/lppl.txt + +This work is "maintained" (as per LPPL maintenance status) by + Jonathan P. Spratte + +------------------------------------------------------------------------------- + +## Summary of Contents + +`expkv-bundle` is a collection of different packages that provide key=value +functionality in plainTeX, LaTeX, and ConTeXt. + +At the core the `expkv` package implements two expandable key=value parsers that +are somewhat fast and robust against common bugs in many key=value +implementations (no accidental brace stripping, no fragility for active commas +or equals signs). + +`expkv-cs` enables users to define expandable key=value macros in a comfortable +and straight forward way. + +`expkv-def` provides an interface to define common key types for `expkv` similar +to the key defining interfaces of wide spread key=value implementations. + +`expkv-opt` allows to parse package or class options in LaTeX via `expkv`. + +`expkv-pop` is a utility package to define prefix oriented parsers that allow a +somewhat natural formulation (it provides the core functionality for the +key-defining front ends of both `expkv-cs` and `expkv-def`). diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/comparison.tex b/Master/texmf-dist/doc/latex/expkv-bundle/comparison.tex new file mode 100644 index 00000000000..7736f0a4f1b --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/comparison.tex @@ -0,0 +1,246 @@ +\section{Comparisons}\label{sec:cmp} + +This section makes some basic comparison between \expkv\ and other \kv\ +packages. The comparisons are really concise, regarding speed, feature range +(without listing the features of each package, comparisons are done against the +base \expkv\ not counting other packages in \expkvbundle\ that extend it, so +\enquote{bigger feature set} might not necessarily be true if everything is +included), and bugs and misfeatures. + +Comparisons of speed are done with a very simple test key and the help of the +\pkg{l3benchmark} package. The key and its usage should be equivalent to +\begin{enverb}[no-tcb] + \protected\ekvdef{test}{height}{\def\myheight{#1}} + \ekvsetdef\expkvtest{test} + \expkvtest{ height = 6 } +\end{enverb} +and only the usage of the key, not its definition, is benchmarked. For the +impatient, the essence of these comparisons regarding speed and buggy +behaviour is contained in \autoref{tab:comp}. + +As far as I know \expkv\ is the only fully expandable \kv\ parser. I tried to +compare \expkv\ to every \kv\ package listed on +\href{https://ctan.org/topic/keyval}{CTAN}, however, one might notice that +some of those are missing from this list. That's because I didn't get the +others to work due to bugs, or because they just provide wrappers around other +packages in this list. + +In this subsection is no benchmark of |\ekvparse| and |\keyval_parse:NNn| +contained, as most other packages don't provide equivalent features to my +knowledge. |\ekvparse| is slightly faster than |\ekvset|, but keep in mind +that it does less. The same is true for |\keyval_parse:NNn| compared to +|\keys_set:nn| of \pkg{expl3} (where the difference is much bigger). Comparing +just the two, |\ekvparse| is a tad faster than |\keyval_parse:NNn| because of +two tests (for empty key names and only a single equal sign) which are +omitted. + +\paragraph{\pkg{keyval}} is the fastest \kv\ package there is and has a minimal +feature set with a slightly different way how it handles keys without values +compared to \expkv. That might be considered a drawback, as it limits the +versatility, but also as an advantage, as it might reduce doubled code. Keep in +mind that as soon as someone loads \pkg{xkeyval} the performance of \pkg{keyval} +gets replaced by \pkg{xkeyval}'s. + +Also \pkg{keyval} has a \pmso{bug}feature, which unfortunately can't really be +resolved without breaking backwards compatibility for \emph{many} documents, +namely it strips braces from the argument before stripping spaces if the +outer most braces aren't surrounded by spaces, also it might strip more than one +set of braces. Hence all of the following are equivalent in their outcome, +though the last two lines should result in something different than the first +two: +\begin{enverb}[no-tcb] + \setkeys{foo}{bar=baz} + \setkeys{foo}{bar= {baz}} + \setkeys{foo}{bar={ baz}} % should be ` baz' + \setkeys{foo}{bar={{baz}}} % should be `{baz}' +\end{enverb} + +\pkg{keyval} doesn't work with non-standard category codes of |=| and |,|. Also +if a \kv\ pair contains multiple equals signs outside of braces everything post +the first is silently ignored so the following two inputs yield identical +outputs: +\begin{enverb}[no-tcb,lst=belowskip=0pt] + \setkeys{foo}{bar=baz} + \setkeys{foo}{bar=baz=and more} +\end{enverb} + +\paragraph{\pkg{xkeyval}} is pretty slow (yet not the slowest), but it provides +more functionality, e.g., it has an interface to disable a list of keys, can +search multiple sets simultaneously, and has an intriguing mechanism it calls +\enquote{Pointers} to save the value of particular keys for later reuse. It +contains the same bug as \pkg{keyval} as it has to be compatible with it by +design (it replaces \pkg{keyval}'s frontend), but also adds even more cases in +which braces are stripped that shouldn't be stripped, worsening the situation. + +\pkg{xkeyval} does work with non-standard category codes of |=| and |,|, but the +used mechanism fails if the input contains a mix of different category codes for +the same character. Just like with \pkg{keyval} equals signs after the first and +everything after those is ignored. + +\paragraph{\pkg{ltxkeys}} is no longer compatible with the \LaTeX\ kernel +starting with the release 2020-10-01. It is by far the slowest \kv\ package I've +tested -- which is funny, because it aims to be ``[\ldots] faster [\ldots] than +these earlier packages [referring to \pkg{keyval} and \pkg{xkeyval}].'' It needs +more time to parse zero~keys than five of the packages in this comparison need +to parse 100~keys. Since it aims to have a bigger feature set than +\pkg{xkeyval}, it most definitely also has a bigger feature set than \expkv. +Also, it can't parse |\long| input, so as soon as your values contain a |\par|, +it'll throw errors. Furthermore, \pkg{ltxkeys} doesn't strip outer braces at +all by design, which, imho, is a weird design choice. Some of the more +intriguing features (e.g., the |\argpattern| mechanism) didn't work for me. In +addition \pkg{ltxkeys} loads \pkg{catoptions} which is known to introduce bugs +(e.g., see \url{https://tex.stackexchange.com/questions/461783}). Because it is +no longer compatible with the kernel, I stop benchmarking it (so the numbers +listed here and in \autoref{tab:comp} regarding \pkg{ltxkeys} were last updated +on 2020-10-05). + +\pkg{ltxkeys} works with non-standard category codes, it also silently ignores +any additional equals signs and the following tokens. + +\paragraph{\pkg{l3keys}} is at the slower end of the midfield yet not unusably +slow, but has an, imho, great interface to define keys. It strips \emph{all} +outer spaces, even if somehow multiple spaces ended up on either end. It offers +more features, but has pretty much been bound to \pkg{expl3} code before. +Nowadays the \LaTeX\ kernel has an interface with the macros |\DeclareKeys|, +|\SetKeys|, and |\ProcessKeyOptions| that provides access to \pkg{l3keys} from +the \LaTeXe\ layer as well as parsing package options with it. Because of the +|\ProcessKeyOptions| macro and its features the only two viable options to +provide \kv\ options for new projects in my opinion are the kernel's methods and +\expkvo\ as those are the only two until now up to my knowledge that support +parsing the raw options, and future options. + +\pkg{l3keys} handles active commas and equals signs fine. Multiple equals signs +lead to an error if additional equals signs aren't nested inside of braces, so +perfectly predictable behaviour here. + +\paragraph{\pkg{pgfkeys}} is among the top~4 of speed if one uses |\pgfqkeys| +over |\pgfkeys|, else the initialisation parsing the family path takes roughly +\SI{43}{\ops} and moves it two spots down the list (so in \autoref{tab:comp} +both $p_0$ and $T_0$ would be about \SI{43}{\ops} bigger if +|\pgfkeys{|\meta{path}|/.cd,|\meta{keys}|}| was used instead). It has an +\emph{enormous} feature set. It stores keys in a way that reminds one of folders +in a Unix system which allows interesting features and has other syntactic +sugars. It is another package that implements something like the \expnotation\ +with less different options though. To get the best performance |\pgfqkeys| was +used in the benchmark. It has the same or a very similar bug \pkg{keyval} has. +The brace bug (and also the category fragility) can be fixed by \pkg{pgfkeyx}, +but this package was last updated in 2012 and it slows down |\pgfkeys| by +factor~\num{8}. Also \pkg{pgfkeyx} is no longer compatible with versions of +\pkg{pgfkeys} newer than 2020-05-25. + +\pkg{pgfkeys} silently drops anything after the second unbraced equals sign in a +\kv\ pair. + +\paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is in the slower midfield, +but it works even if commas and equals have category codes different from 12 +(just as some other packages in this list). It has quadratic run-time unlike +most other \kv\ implementations which behave linear. The features of the +keys are equal to those of \pkg{keyval}, the parser adds handling of unknown +keys. + +\pkg{kvsetkeys} does include any additional equals sign in the value. But any +active equals sign is turned into one of category code 12 if it's not nested in +braces. Also spaces around superfluous equals signs are stripped. So the +following all result in the same: +\begin{enverb}[no-tcb,lst=belowskip=0pt] + \kvsetkeys{foo}{bar=baz=morebaz} + \kvsetkeys{foo}{bar=baz =morebaz} + \kvsetkeys{foo}{bar=baz= morebaz} + \kvsetkeys{foo}{bar=baz = morebaz} +\end{enverb} + +\paragraph{\pkg{options}} is in the midfield of speed. It is faster per +individual key than \pkg{pgfkeys} but has no shortcut like |\pgfqkeys|. +It has a much bigger feature set than \expkv. Similar to \pkg{pgfkeys} it uses a +folder like structure, makes searching multiple paths easy, incorporates package +options and more. It also features a form of expansion control, predefined +expansion kinds are limited though one can define additional ones. Unfortunately +it also suffers from the premature unbracing bug \pkg{keyval} has. + +\pkg{options} can't handle non-standard category codes and will silently ignore +superfluous equals signs and following tokens. + +\paragraph{\pkg{simplekv}} is hard to compare because I don't speak French (so +I don't understand the documentation). There was an update released on +2020-04-27 which greatly improved the package's performance and added +functionality so that it can be used more like most of the other \kv\ +packages. Speed wise it is pretty close to \expkv. Regarding unknown keys it got +a very interesting behaviour. It doesn't throw an error, but stores the \val\ in +a new entry accessible with \cs[no-index]{useKV}. Also if you omit \val\ it +stores |true| for that \key. + +\pkg{simplekv} can't correctly handle non-standard category codes. It silently +ignores any unbraced equals sign beyond the first and any following tokens. + +\paragraph{\protect\yax} is the second slowest package I've tested. It has a +pretty strange syntax for the \TeX-world, imho, and again a direct equivalent is +hard to define (don't understand me wrong, I don't say I don't like the syntax, +quite the contrary, it's just atypical). It has the premature unbracing bug, +too. \yax\ features some prefixes one can use to make an assignment use |\edef|, +|\gdef| or |\xdef| so has something that comes close to expansion control. Also +somehow loading \yax\ broke \pkg{options} for me. The tested definition was: +\begin{enverb}[no-tcb] + \usepackage{yax} + \defactiveparameter yax {\storevalue\myheight yax:height } % setup + \setparameterlist{yax}{ height = 6 } % benchmark +\end{enverb} +This seems important to state as \yax\ supports two different input syntaxes, +the tested one was the one closer to traditional \kv\ input. + +\yax\ won't handle non-standard category codes correctly. Superfluous equals +signs end up in the value in an unaltered form (just like with \expkv). + +\begin{table} + \def\fnsym{\textcolor{red!80!black}{*}}% + \sisetup{round-precision=1, round-mode=places}% + \begingroup + \centering + \newcommand*\yes{\textcolor{red!80!black} {yes}}^^A + \newcommand*\no {\textcolor{green!80!black}{no}}^^A + \caption[Comparison of \kv\ packages] + {^^A + Comparison of \kv\ packages. The packages are ordered from + fastest to slowest for one \kv\ pair. Benchmarking was done using + \pkg{l3benchmark} and the scripts in the \file{Benchmarks} folder + of \href{https://github.com/Skillmon/tex_expkv}{the original \expkv's git + repository}. + The columns $p_i$ are the polynomial coefficients of a linear fit to the + run-time, $p_0$ can be interpreted as the overhead for initialisation + and $p_1$ the cost per key. The $T_0$ column is the actual mean ops + needed for an empty list argument, as the linear fit doesn't match that + point well in general. The column ``BB'' lists whether the + parsing is affected by some sort of brace bug, ``CF'' stands for + category code fragile and lists whether the parsing breaks with active + commas or equal signs.^^A + \label{tab:comp}^^A + } + \begin{tabular} + {>{\collectcell\pkg}l<{\endcollectcell}*3{S[table-format=4.1]}ccc} + \toprule + \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\ + \midrule + keyval & 13.557 & 2.202 & 7.185 & \yes & \yes & 2022-05-29 \\ + \expkv & 16.669 & 3.132 & 5.836 & \no & \no & 2023-01-10 \\ + simplekv & 19.943 & 2.850 & 15.120 & \no & \yes & 2022-10-01 \\ + pgfkeys & 24.465 & 2.244 & 10.283 & \yes & \yes & 2021-05-15 \\ + options & 23.255 & 16.160 & 20.400 & \yes & \yes & 2015-03-01 \\ + kvsetkeys & {\fnsym} & {\fnsym} & 40.360 & \no & \no & 2022-10-05 \\ + l3keys & 70.643 & 35.602 & 32.230 & \no & \no & 2022-12-17 \\ + xkeyval & 255.906 & 221.276 & 173.400 & \yes & \yes & 2022-06-16 \\ + \yax & 438.242 & 131.846 & 114.800 & \yes & \yes & 2010-01-22 \\ + ltxkeys & 3400.142 & 4737.958 & 5368.000 & \no & \no & 2012-11-17 \\ + \bottomrule + \end{tabular} + \par + \endgroup + \medskip + \fnsym For \pkg{kvsetkeys} the linear model used for the other + packages is a poor fit, \pkg{kvsetkeys} seems to have approximately + quadratic run-time, the coefficients of the second degree polynomial fit are + $p_2=\num{7.617}$, $p_1=\num{47.727}$, and $p_0=\num{57.988}$. Of course the + other packages might not really have linear run-time, but at least from 1~to + 20~keys the fits don't seem too bad. If one extrapolates the fits for 100 + \kv\ pairs one finds that most of them match pretty well, the exception + being \pkg{ltxkeys}, which behaves quadratic as well with + $p_2=\num{23.500}$, $p_1=\num{2906.634}$, and $p_0=\num{6547.489}$. +\end{table} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/expkv-bundle.pdf b/Master/texmf-dist/doc/latex/expkv-bundle/expkv-bundle.pdf Binary files differnew file mode 100644 index 00000000000..1d7023208a8 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/expkv-bundle.pdf diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/expkv-bundle.tex b/Master/texmf-dist/doc/latex/expkv-bundle/expkv-bundle.tex new file mode 100644 index 00000000000..f8110ea36a1 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/expkv-bundle.tex @@ -0,0 +1,353 @@ +\PassOptionsToPackage{full}{textcomp} +\documentclass[exfoo=value, exbar, exfoo=\empty]{l3doc} + +% preamble >>= +\makeatletter +\let\save@onlypreamble\@onlypreamble +\let\@onlypreamble\@gobble +\usepackage[all]{expkv} +\let\@onlypreamble\save@onlypreamble +\makeatother +\usepackage[oldstylenums,nott]{kpfonts} +\input{glyphtounicode} +\pdfgentounicode=1 +\usepackage{xfp} % required for an example +\usepackage{booktabs} +\usepackage{array} +\usepackage{collcell} +\usepackage{siunitx} +\DeclareSIUnit\ops{ops} +\usepackage{xcolor} +\usepackage{caption} +\usepackage{microtype} +\usepackage{accsupp} +\usepackage{enumitem} +\usepackage{randtext} + +\usepackage{tcolorbox}%>>= +\newtcolorbox{exresult}[2][] + {% + colback=ekvgrey!10!white% + ,colframe=ekvgrey% + ,fontupper=\small + ,width={\dimexpr#2\relax}% + ,#1% + } +\newtcbox\exres[1][] + { + colback=ekvgrey!10!white + ,colframe=ekvgrey + ,size=small + ,nobeforeafter + ,tcbox raise base + ,fontupper=\small + ,#1 + } +%=<< + +\usepackage{listings}%>>= +\input{preamble-lst.tex} +%=<< + +\let\metaORIG\meta +\protected\def\meta #1{\texttt{\metaORIG{#1}}} + +\input{preamble-examples.tex} +\input{preamble-logos.tex} + +\makeatletter +% shortcuts >>= +\newcommand\Vkey{\texttt{Val}-\key} +\newcommand*\Nkey{\texttt{NoVal}-\key} +\newcommand\kv{\meta{key}=\penalty2000\meta{value}} +\newcommand\kvarg{\{\kv, \ldots\}} +\newcommand\key{\meta{key}} +\newcommand\val{\meta{value}} +\newcommand\set{\meta{set}} +\newcommand\prefix{\texorpdfstring{\textit{prefix}}{prefix}} +\newcommand\prefixes{\textit{prefixes}} +\newcommand\type{\texorpdfstring{\textit{type}}{type}} +\newcommand\types{\textit{types}} +\newcommand\tkn[2]{\texttt{\char`#1}\textsubscript{#2}} +\newcommand\expansion{\meta{expansion}} +%\newcommand\expnotation{} +%\edef\expnotation + %{\noexpand\texttt{exp\string|}\penalty\@M-\hskip\z@skip notation} +\newcommand\singlecs[1] + {% + The \meta{cs} should be a single control sequence, such as + \cs[no-index]{#1}. \ignorespaces + } +\newcommand\ekvdocsection[8] + {% + \clearpage + \chardef\ekvdoc@insection1 + \section[{#7}]% + {% + #7% + \hfill + \begingroup\scriptsize\ttfamily + \begin{tabular}{@{}r@{}l@{}}% + #1{\string\input\{expkv#8\}} & \rlap{#2{\ \% plain}}\\ + #3{\string\usepackage\{expkv#8\}} & \rlap{#4{\ \% LaTeX}}\\ + #5{\string\usemodule[expkv#8]} & \rlap{#6{\ \% ConTeXt}}\\ + \end{tabular}% + \endgroup + \label{sec:expkv#8}% + }% + \chardef\ekvdoc@insection0 + } +\chardef\ekvdoc@insection0 +\newcommand*\genericekv + {\ekvdocsection{}{\textcolor{gray}}{}{\textcolor{gray}}{}{\textcolor{gray}}} +\newcommand*\latexekv + {\ekvdocsection\phantom\phantom{}{\textcolor{gray}}\phantom\phantom} +%=<< + +\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} + +\input{preamble-prefixes.tex} +\input{preamble-noidx.tex} +\input{preamble-enverb.tex} + +% vissp >>= +\ExplSyntaxOn +\cs_new_protected:Npn \vissp #1 + { + \group_begin: + \tl_set:Nn \l_tmpa_tl {#1} + \tl_replace_all:Nnn \l_tmpa_tl { ~ } { \asciispace } + \l_tmpa_tl + \group_end: + } +\ExplSyntaxOff +% =<< + +\ekvcSplit\expkvorules% >>= + { + cd = \emph{nothing} + ,cu = \emph{nothing} + ,pd = \emph{nothing} + ,pu = \emph{nothing} + } + {% + \begin{description} + \item[Class:] + \begin{description} + \item[defined] #1 + \item[undefined] #2 + \end{description} + \item[Package:] + \begin{description} + \item[defined] #3 + \item[undefined] #4 + \end{description} + \end{description}% + } +\ekvcSecondaryKeys\expkvorules + { + meta d = {cd={#1},pd={#1}} + ,meta u = {cu={#1},pu={#1}} + }% =<< + +\newcommand*\pmso[1] % poor man's strike out%>>= + {% + \leavevmode + \begingroup + \sbox0{#1}% + \rlap{\vrule height .6ex depth -.5ex width \wd0\relax}% + \usebox0\relax + \endgroup + }%=<< + +\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} + +\renewcommand*\partname{Part} + +% \addsec and friends >>= +\newcommand*\addsec@[2] + {% + \c@secnumdepth=% + \expanded + {% + \m@ne + \unexpanded{#1{#2}}% + \c@secnumdepth=\the\c@secnumdepth\relax + }% + } +\newcommand*\addsec {\addsec@\section} +\newcommand*\addssec {\addsec@\subsection} +\newcommand*\addsssec{\addsec@\subsubsection} +% =<< + +\newenvironment{syntaxexample}% >>= + {% + \quote + \ttfamily\small\frenchspacing + \parskip=\z@ + \def\indent{\leavevmode\phantom{mm}}% + } + {\endquote} +\newenvironment{syntaxexample*} + {\syntaxexample\obeylines} + {\endsyntaxexample}% =<< + +\newcommand\expkvdocPrintErrors[1][] + {% + \protected\long\def\expkvdoc@errfont##1% + {\texttt{\frenchspacing\textcolor{red!80!black}{##1}}}% + \protected\long\def\ekv@err@collect##1\par##2% + {\expkvdoc@errfont{! \detokenize{##2} Error: ##1}#1}% + \protected\long\def\expkvdoc@errm##1##2% + {\expkvdoc@errfont{! expkv##1 Error: ##2}#1}% + \def\ekv@errm{\expkvdoc@errm{}}% + \def\ekvc@errm{\expkvdoc@errm{-cs}}% + \def\ekvd@errm{\expkvdoc@errm{-def}}% + \def\ekvp@errm{\expkvdoc@errm{-pop}}% + } +\makeatother + +\ExplSyntaxOn +\str_new:N \g__expkvdoc_module_str +\cs_new_protected:Npn \expkvdocfile #1% >>= + { + \expkvdoc_for_module:nn {#1} + { + \str_if_eq:nnTF {#1} {main} + { \lstset{style=expkv} } + { \lstset{style=expkv-#1} } + \addtocontents{exs} + { + \medskip + \noindent + \use:c { expkv \str_if_eq:nnF {#1} {main} { \str_head:n {#1} } } + \smallskip + } + \input{pkg-#1.tex} + } + }% =<< +\cs_new_protected:Npn \expkvdocdtx #1% >>= + { + \expkvdoc_for_module:nn {#1} + { + \str_if_eq:nnTF {#1} {main} + { \DocInput{expkv.dtx} } + { \DocInput{expkv-#1.dtx} } + } + }% =<< +\cs_new_protected:Npn \expkvdoc_for_module:nn #1#2% >>= + { + \use:e + { + \exp_not:n + { + \str_gset:Nn \g__expkvdoc_module_str {#1} + #2 + \str_gset:Nn \g__expkvdoc_module_str + } + { \g__expkvdoc_module_str } + } + }% =<< +\ExplSyntaxOff + +\input{preamble-l3doctweaks.tex} + +%=<< + +% for thanking Niranjan +% code from https://tex.stackexchange.com/a/635125/117050 +\def\DevnagVersion{2.17} +\usepackage{devanagari} + +\newif\ifexpkvDocImplementation%\expkvDocImplementationtrue + +\begin{document} +\title{\expkvbundle} +%\title% >>= + %{% + %\texorpdfstring + %{% + %\huge\expkvbundle + %\\[\medskipamount] + %\Large an {\expFormat}andable + %\meta{{\kvstyle k}\kern-.05em ey}=% + %\meta{{\kvstyle v}\kern-.05em alue} + %implementation and more% + %} + %{expkv-bundle - an expandable <key>=<value> implementation and more}% + %}% =<< +\author + {% + Jonathan P. Spratte% + \thanks + {% + \protect\randomize{jspratte@yahoo.de}; + Special thanks to {\protect\dn Enr\2jn} (Niranjan) for valuable + suggestions and additions to this documentation.% + }% + } +\date{2023-01-23} +\begingroup +\renewcommand*\thefootnote{\fnsymbol{footnote}} +\maketitle +\endgroup + +\begin{abstract}%>>= + \noindent\parfillskip=0pt + The \expkvbundle\ provides at its core a \emph{fully expandable} \kv\ parser, + that is \emph{safe} for active commas and equals signs, \emph{reliable} to + only strip one set of braces after spaces are stripped, and blazingly + \emph{fast}, as of writing this only \pkg{keyval} is faster. + \par + \bigskip + This parser gets augmented by a family of packages. \expkvc\ allows to easily + define expandable macros using a \kv\ interface, making the expandable parser + available to the masses. \expkvd\ provides a \kv\ interface to + define common \key-types. With \expkvo\ you can parse package + and class options. \expkvp\ enables you to design your own prefix + oriented parsers for interface definitions. +\end{abstract}%=<< + +\tableofcontents + +\clearpage + +\begin{documentation}% >>= +\ifexpkvDocImplementation\part{Documentation}\fi + +\input{introduction.tex} +\clearpage +\input{impatient.tex} + +\expkvdocfile{main} +\expkvdocfile{cs} +\expkvdocfile{def} +\expkvdocfile{opt} +\expkvdocfile{pop} +\clearpage +\lstset{style=expkv-all} +\input{comparison.tex} +\end{documentation}% =<< + +\clearpage +\listofexamples + +\ifexpkvDocImplementation + \clearpage + \begin{implementation}% >>= + \part{Implementation} + \expkvdocdtx{main} + \clearpage + \expkvdocdtx{cs} + \clearpage + \expkvdocdtx{def} + \clearpage + \expkvdocdtx{opt} + \clearpage + \expkvdocdtx{pop} + \end{implementation}% =<< +\fi + +\clearpage +\PrintIndex +\end{document} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/impatient.tex b/Master/texmf-dist/doc/latex/expkv-bundle/impatient.tex new file mode 100644 index 00000000000..b3d62b07faf --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/impatient.tex @@ -0,0 +1,56 @@ +\addsec{\expkvbundle\ for the Impatient} + +This section gives a very brief and non-exhaustive overview over the contents of +the \expkvbundle. For more information (and more functionality) you'll have to +read the sections of the packages you're interested in. + +\expkvbundle\ supports expansion control in \kv\ lists. The corresponding +syntax and features are documented in \autoref{sec:expkv:expansion}. + +The following user interface macros (and more) are available in the different +packages of the bundle: +\paragraph{Defining keys} +\begin{itemize} + \item \cs[no-index]{ekvdefinekeys}\marg{set}\texttt{\kvarg} + defines the keys in the \kv\ list, many common key types are available + (\autoref{sec:d:macros} and for the types \autoref{sec:d:types}). + \item \cs[no-index]{ekvdef}\marg{set}\marg{key}\marg{code} + defines the behaviour of a \Vkey\ (\autoref{sec:expkv:setup}). + \item \cs[no-index]{ekvdefNoVal}\marg{set}\marg{key}\marg{code} + defines the behaviour of a \Nkey\ (\autoref{sec:expkv:setup}). +\end{itemize} +\paragraph{Parsing \kv\ lists} +\begin{itemize} + \item \cs[no-index]{ekvset}\marg{set}\texttt{\kvarg} + sets defined keys (\autoref{sec:expkv:set}). + \item \cs[no-index]{ekvparse}\marg{k-code}\marg{kv-code}\texttt{\kvarg} + parses the \kv\ list and runs \meta{k-code} or \meta{kv-code} on the + elements (\autoref{sec:expkv:parse}). +\end{itemize} +\paragraph{Defining expandable \kv\ macros} +\begin{itemize} + \item \cs[no-index]{ekvcSplit}\meta{cs}\texttt{\kvarg}\marg{code} + defines a fully expandable macro with the keys in the \kv\ list, + values are accessed by |#1| to |#9| (\autoref{sec:c:split}). + \item \cs[no-index]{ekvcHash}\meta{cs}\texttt{\kvarg}\marg{code} + defines a fully expandable macro with the keys in the \kv\ list, + values are accessed using \cs[no-index]{ekvcValue}\hskip0pt\marg{key}|{#1}| + (\autoref{sec:c:hash}). + \item \cs[no-index]{ekvcSecondaryKeys}\meta{cs}\texttt{\kvarg} + defines additional keys of predefined types for a \meta{cs} defined with + \cs[no-index]{ekvcSplit} or \cs[no-index]{ekvcHash} + (\autoref{sec:c:secondary} and for the types + \autoref{sec:c:secondarytypes}). +\end{itemize} +\paragraph{Parsing options} (\autoref{sec:o:macros}) +\begin{itemize} + \item \cs[no-index]{ekvoProcessOptions}\marg{set} + processes the global options, and the options given to the current and + all future calls of the package. + \item \cs[no-index]{ekvoProcessGlobalOptions}\marg{set} + processes the global options. + \item \cs[no-index]{ekvoProcessLocalOptions}\marg{set} + processes the local options of a package or class. + \item \cs[no-index]{ekvoProcessFutureOptions}\marg{set} + processes the options of future calls of the package. +\end{itemize} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/introduction.tex b/Master/texmf-dist/doc/latex/expkv-bundle/introduction.tex new file mode 100644 index 00000000000..de52d9e5843 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/introduction.tex @@ -0,0 +1,115 @@ +\addsec{Introduction} +%The name \expkv\ stands for \emph{exp}andable \emph{k}ey-\emph{v}alue and shall +%be pronounced \enquote{axe(b) kay vee}. + +This bundle consists of different packages the base being \expkv. Most of these +packages are available for \hologo{plainTeX}, \LaTeXe, and \hologo{ConTeXt}. For +stylistic reasons the package names are printed as +\expkvLogoFormat{\textit{$\langle$pkg$\rangle$}}, but the files are named +\texttt{expkv-\meta{pkg}} (CTAN-rules don't allow \string| in names), so in +order to load \expkvc\ in \LaTeXe\ you should use +\begin{enverb}[no-tcb] + \usepackage{expkv-cs} +\end{enverb} +Each section describing a package of this bundle has next to its heading the +formats in which they work printed flush right. If more than a single format is +supported by a package the functionality is defined by the \hologo{plainTeX} +variant and the other variants only load the generic code in a way suitable for +the format. + +\addssec{Terminology} +This documentation uses a few terms which always mean specific things: +\begin{description} + \item[\kv\ pair] + is one element in a comma separated list which contains at least one equals + sign \emph{not} contained in any braces, and the first such equals sign is + the separator between the \key\ (with an optional \expansion\ prefix) and + the \val. + \item[\key] + means the entire left-hand side of a \kv\ pair with an optional \expansion\ + prefix stripped, or if =\val\ is omitted the complete list element, again + with an \expansion\ prefix stripped. + \item[\key-name] + synonymous to \key. + \item[\Vkey] + describes a \key\ in a \kv\ pair. + \item[\Nkey] + describes a \key\ for which =\val\ was or should be omitted. + \item[\val] + is the right-hand side of a \kv\ pair. + \item[\kv\ list] + is a comma separated list containing \kv\ pairs and \Nkey{}s (each possibly + with an \expansion\ prefix). + \item[\kvarg] + an argument that should get a \kv\ list. + \item[\expansion\ prefix] + an optional prefix in front of \key\ to specify \expansion-rules (see + \autoref{sec:expkv:expansion}), that prefix consists of the \expansion-rules + followed by a colon immediately followed by a space. + \item[\expansion] + a list of tokens specifying expansion steps for \key\ and \val. + \item[\expansion-rule] + a single expansion step in the \expansion-rules. + \item[\expansion-rules] + synonymous to \expansion. + \item[\expnotation] + the notation of \expansion-rules in form of the \expansion\ prefix. + \item[key-code] + the code that is executed for a given \key. + \item[key-macro] + the internal macro that stores the key-code. +\end{description} + +Though not really terminology but more typographic representation I want to +highlight a distinction between two different types of code listings in this +documentation. I use the following looks to show a code example and its results: +\begin{enverb}[same-line=0.6] + \newcommand*\foo{This is an example.} + \foo +\end{enverb} +And this is how a syntax summary or a syntax example looks like (this is more +abstract than an example and contains short meta-descriptions of inputs): +\begin{syntaxexample} + \cs[no-index]{function}\marg{syntax} +\end{syntaxexample} +Inside such syntax summaries the following rules usually apply (and \meta{arg} +would be the meta description here): +\begin{description}[font=\small\ttfamily] + \item[\marg{arg}] + a mandatory argument is shown in braces + \item[\meta{arg}] + a mandatory argument that should be a single token is shown without + additional parentheses/braces/brackets + \item[\oarg{arg}] + an optional argument is shown in brackets (and should be input with + brackets) + \item[\meta{*}] + an optional star is shown like this +\end{description} +If other types of arguments are displayed the documentation will explain what +they mean in this particular place. + +\addssec{Category Codes} +Supporting different category codes of a single character code makes the +programmer's life harder in \TeX, but there are valid reasons to make some +active, or letter. Because of this the packages in this bundle support different +category codes for specific syntax relevant characters (unless otherwise +documented). This doesn't mean that \expkv\ changes any category codes, only +that parsing is correct \emph{if} they are changed later (the codes listed +assume standard category codes of \hologo{plainTeX} and \LaTeXe\ apply while +\expkv\ is loaded). The supported tokens are: +\begin{description} + \item[=] \tkn{=}{12} and \tkn{=}{13} + \item[,] \tkn{,}{12} and \tkn{,}{13} + \item[:] (for the \expnotation) \tkn{:}{11}, \tkn{:}{12}, and \tkn{:}{13} + \item[*] (for starred macros) \tkn{*}{3}, \tkn{*}{4}, \tkn{*}{6}, \tkn{*}{7}, + \tkn{*}{8}, \tkn{*}{11}, \tkn{*}{12}, and \tkn{*}{13} + \item[{[}] (for |\ekvoptarg|) only \tkn{[}{12} + \item[{]}] (for |\ekvoptarg|) only \tkn{]}{12} +\end{description} + +\addssec{Bugs} +Just like \pkg{keyval}, \expkv\ is bug free. But if you find \pmso{bugs}hidden +features\footnote{Thanks, David!} you can tell me about them either via mail +(see the first page) or directly on GitLab if you have an account there: +\url{https://gitlab.com/islandoftex/texmf/expkv-bundle} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/pkg-cs.tex b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-cs.tex new file mode 100644 index 00000000000..850b9af6205 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-cs.tex @@ -0,0 +1,937 @@ +\genericekv\expkvc{-cs} + +\expkvc\ aids in creating fully expandable macros that take a \kv\ argument. It +implements somewhat efficient solutions to expandable \kv\ parsing without the +user having to worry too much about the details. + +The package supports two main approaches for this. The first is splitting the +keys up into individual arguments, the second preparses the \kv\ list into a +single argument in which accessing the value of individual keys is fast. The +behaviour of the second type is similar to a hash table, so we call that variant +|Hash|, the first type is called |Split|. Both these variants support a number +of so called \emph{primary keys} (a primary key matches an argument, roughly +speaking). + +In addition to these methods there is a structured way to define additional keys +which might build upon the primary keys but not directly relate to an argument. +These keys are called \emph{secondary keys}. Primary and secondary keys belong +to a specific macro (the macro name serves as the \emph{set}). + +A word of advice you should consider: Macros defined with \expkvc\ are simple to +create, and there might be good use cases for them (for instance since they +don't work by assignments but only by argument forwarding logic they have no +issues with implicit or explicit groups whatsoever). But they don't scale as +well as established \kv\ interfaces (think of the idiomatic key definitions with +\pkg{keyval}, or \pkg{l3keys}, or \expkv\ with or without \expkvd), and they are +slower than idiomatic key definitions in packages with fast \kv\ parsing. + + +\subsection{Defining Macros and Primary Keys} + +All macros defined with \expkvc\ have to be previously undefined (or have the +|\meaning| of |\relax|). There is no way to automatically undefine keys once +they are set up -- so to make sure there are no conflicts only new definitions +are allowed. The \emph{set} name (as used by \cs[no-index]{ekvset}) will be +\cs[no-index]{string}\cs[no-index]{\meta{macro}}. + + +\subsubsection{Primary Keys\label{sec:c:primary}} + +The notion of primary keys needs a bit of explanation, or better, the input +syntax for the argument \meta{primary keys} in the following explanations. The +\meta{primary keys} argument should be a \kv\ list in which each \key\ will be +one primary key and \val\ the initial value of said \key\ (and that value is +mandatory, even if you leave it blank that's fine, but you have to explicitly +state it). By default all keys are defined short, but you can define |\long| +keys by prefixing \key\ with |long| (\emph{e.g.}, +|long name=Jonathan P. Spratte| to define a |\long| key called |name|). Due to +some internal implementations it's worth noting that |\long| keys are a +microscopic grain faster. The \meta{cs} will only be defined |\long| if at least +one of the keys was |\long|. For obvious reasons there is no interface in place +to define something as |\protected|. + +To allow keys to start with the word |long| even if you don't want them to be +|\long| you can also prefix them with |short|. The first found prefix of the two +will stop the parsing for prefixes and what remains becomes the \key. + +These rules culminate in the following: + +\begin{enverb}[no-tcb] + \ekvcSplit\foo + { + long short = abc\par + ,short long = def + } + {#1#2} +\end{enverb} +will define a macro |\foo| that knows two primary keys, |short| which is defined +|\long| (so will accept explicit |\par| tokens inside its value at use time), +and |long| which doesn't accept explicit |\par| tokens (leading to a low level +\TeX\ error). The description of |\ekvcSplit| follows shortly. + +There is one exception to the rule that each \key\ in \meta{primary keys} needs +to get a value: If you include a key named |...| without a value this will be a +primary key in which every unknown key will be collected -- and \meta{cs} will +be defined |\long|. The unknown keys will be stored in a way that \emph{most} +\kv\ parsers will parse them correctly (but this is no general guarantee, for +instance \pkg{pgfkeys} can accidentally strip multiple sets of braces at the +wrong moment). See some examples in \autoref{sec:c:unknown}. + +At the moment \expkvc\ doesn't require any internal keys, but I can't foresee +whether this will be the case in the future as well, as it might turn out that +some features I deem useful can't be implemented without such internal keys. +Because of this, please don't use key names starting with "EKVC|" as that +should be the private name space. + + +\subsubsection{Split\label{sec:c:split}} + +The split variants will provide the key values as separate arguments. This +limits the number of keys for which this is truly useful. + +\begin{function}{\ekvcSplit} + \begin{syntax} + \cs{ekvcSplit}\meta{cs}\marg{primary keys}\marg{definition} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. The \meta{primary keys} will be defined for this + macro (see \autoref{sec:c:primary}). The \meta{definition} is the code that + will be executed. You can access the \val\ of a \key\ by using a macro + parameter from |#1| to |#9|. The order of the macro parameters will be the + order provided in the \meta{primary keys} list (so |#1| is the \val\ of the + \key\ defined first). With \cs{ekvcSplit} you can define macros using at most + nine primary keys. +\end{function} +\begin{example}{Simple macro with \cs[no-index]{ekvcSplit}} + The following defines a macro |\foo| that takes the keys |a| and |b| + and outputs their values in a textual form: + \begin{enverb} + \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} + \foo{} + \foo{b=e} + \end{enverb} +\end{example} + +\begin{function}{\ekvcSplitAndForward} + \begin{syntax} + \cs{ekvcSplitAndForward}\meta{cs}\marg{after}\marg{primary keys} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. You can use as many primary keys as you want with + this. The primary keys will be forwarded to \meta{after} as braced arguments + (as many as necessary for your primary keys). The order of the braced + arguments will be the order of your primary key definitions. In \meta{after} + you can use just a single control sequence, or some arbitrary stuff which will + be left in the input stream before your braced values (with one set of braces + stripped from \meta{after}), so both of the following would be fine: + \begin{enverb}[no-tcb] + \ekvcSplitAndForward\foo\foo@aux{keyA = A, keyB = B} + \ekvcSplitAndForward\foo{\foo@aux{more args}}{keyA = A, keyB = B} + \end{enverb} +\end{function} +\noindent +In the first case |\foo@aux| should take at least two arguments (|keyA| and +|keyB|), in the second case at least three (|more args|, |keyA|, and |keyB|). + +\begin{function}{\ekvcSplitAndUse} + \begin{syntax} + \cs{ekvcSplitAndUse}\meta{cs}\marg{primary keys} + \end{syntax} + This will roughly do the same as \cs{ekvcSplitAndForward}, but instead of + specifying what will be used after splitting the keys, \meta{cs} will use what + follows the \kv\ list. So its syntax will be +\end{function} +\begin{quote} + \ttfamily + \meta{cs}\kvarg\marg{after} +\end{quote} +and the code in |after| should expect at least as many arguments as the number +of keys defined for \meta{cs}. + + +\subsubsection{Hash\label{sec:c:hash}} + +The hash variants will provide the key values as a single argument in which you +can access specific values using a special macro. The implementation might be +more convenient and scale better, \emph{but} it is slower (for a rudimentary +macro with a single key benchmarking was almost 1.7~times slower, the root of +which being the key access with \cs{ekvcValue}, not the parsing, and for a key +access using \cs{ekvcValueFast} it was still about 1.2~times slower). Still to +be future proof, considering the hash variants is a good idea, and to get best +performance but less maintainable code you can resort to the split approach. + +\begin{function}{\ekvcHash} + \begin{syntax} + \cs{ekvcHash}\meta{cs}\marg{primary keys}\marg{definition} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. You can use as many primary keys as you want. The + primary keys will be forwarded as a single argument containing every key to + the underlying macro. The underlying macro is defined as \meta{definition}, in + which you can access the \val\ of a \key\ by using + \cs{ekvcValue}\marg{key}|{#1}| (or similar). +\end{function} +\begin{example}{Simple macro with \cs[no-index]{ekvcHash}} + This defines an equivalent macro to the |\foo| defined with \cs{ekvcSplit} + earlier: + \begin{enverb} + \ekvcHash\foo{a=a,b=b}{a is \ekvcValue{a}{#1}.\par + b is \ekvcValue{b}{#1}.\par} + \foo{} + \foo{b=e} + \end{enverb} +\end{example} + +\begin{function}{\ekvcHashAndForward} + \begin{syntax} + \cs{ekvcHashAndForward}\meta{cs}\marg{after}\marg{primary keys} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. You can use as many primary keys as you want. The + primary keys will be forwarded as a single argument containing every key to + \meta{after}. You can use a single macro for \meta{after} or use some + arbitrary stuff, which will be left in the input stream before the hashed \kv\ + list with one set of braces stripped. In the macro called in \meta{after} you + can access the \val\ of a \key\ by using \cs{ekvcValue}\marg{key}|{#1}| (or + whichever argument the hashed \kv\ list will be in). +\end{function} +\begin{example}{Using \cs[no-index]{ekvcHashAndForward}} + This defines a macro |\foo| processing two keys, and passing the result to + |\foobar|: + \begin{enverb} + \ekvcHashAndForward\foo\foobar{a=a,b=b} + \newcommand*\foobar[1]{a is \ekvcValue{a}{#1}.\par + b is \ekvcValue{b}{#1}.\par} + \foo{} + \foo{b=e} + \end{enverb} +\end{example} + +\begin{function}{\ekvcHashAndUse} + \begin{syntax} + \cs{ekvcHashAndUse}\meta{cs}\marg{primary keys} + \end{syntax} + This will roughly do the same as \cs{ekvcHashAndForward}, but instead of + specifying what will be used after hashing the keys during the definition, + \meta{cs} will use what follows the \kv\ list. So its syntax will be + \begin{quote} + \ttfamily + \meta{cs}\kvarg\marg{after} + \end{quote} +\end{function} + +\begin{function}{\ekvcValue} + \begin{syntax} + \cs{ekvcValue}\marg{key}\marg{key list} + \end{syntax} + This is a safe way to access your keys in a hash variant. \key\ is the key + which's \val\ you want to use out of the \meta{key list}. \meta{key list} + should be the key list argument forwarded to your underlying macro by + \cs{ekvcHash}, \cs{ekvcHashAndForward}, or \cs{ekvcHashAndUse}. It will be + tested whether the hash function to access that \key\ exists, the \key\ + argument is not empty, and that the \meta{key list} really contains a \val\ of + that \key. This macro needs exactly two steps of expansion and if used inside + of an |\edef| or |\expanded| context will protect the \val\ from further + expanding. +\end{function} + +\begin{function}{\ekvcValueFast} + \begin{syntax} + \cs{ekvcValueFast}\marg{key}\marg{key list} + \end{syntax} + This behaves similar to \cs{ekvcValue}, but \emph{without any} safefy tests. + As a result this is about 1.4~times faster \emph{but} will throw low level + \TeX\ errors eventually if the hash function isn't defined or the \key\ isn't + part of the \meta{key list} (\emph{e.g.}, because it was defined as a key for + another macro -- all macros share the same hash function per \meta{key} name). + Note that this will not only throw low level errors but result in undefined + behaviour as well! This macro needs exactly three steps of expansion in the + no-error case. +\end{function} + +\begin{function}{\ekvcValueSplit} + \begin{syntax} + \cs{ekvcValueSplit}\marg{key}\marg{key list}\marg{next} + \end{syntax} + If you need a specific \key\ from a \meta{key list} more than once, it'll be a + good idea to only extract it once and from then on keep it as a separate + argument (or if you want to forward this value to another macro). Hence the + macro \cs{ekvcValueSplit} will extract one specific \key's \val\ from the list + and forward it as an argument to \meta{next}, so the result of this will be + \meta{next}\marg{value}. This is roughly as fast as \cs{ekvcValue} and runs + the same tests. +\end{function} +\begin{example} + {Splitting of a key from a hash list using \cs[no-index]{ekvcValueSplit}} + The following defines a macro |\foo| which will take three keys. Since the + next parsing step will need the value of one of the keys multiple times we + split that key off the list (in this example the next step doesn't use the key + multiple times for simplicity though), and the entire list is forwarded as the + second argument: + \begin{enverb} + \ekvcHash\foo{a=a,b=b,c=c} + {\ekvcValueSplit{a}{#1}\foobar{#1}} + \newcommand*\foobar[2]{a is #1.\par + b is \ekvcValue{b}{#2}.\par + c is \ekvcValue{c}{#2}.\par} + \foo{} + \end{enverb} +\end{example} + +\begin{function}{\ekvcValueSplitFast} + \begin{syntax} + \cs{ekvcValueSplitFast}\marg{key}\marg{key list}\marg{next} + \end{syntax} + This behaves just like \cs{ekvcValueSplit}, but it won't run the safety tests, + hence it is faster but more error prone, just like the relation between + \cs{ekvcValue} and \cs{ekvcValueFast}. +\end{function} + + +\subsection{Secondary Keys\label{sec:c:secondary}} + +To lift some limitations of each primary key matching one argument or hash +entry, you can define secondary keys. Those have to be defined for each macro +individually but it doesn't matter whether that macro was set up as a split or +hash variant. + +Secondary keys can have a \prefix\ (|long|), and must have a \type\ +(like |meta|). Some \types\ might require some \prefix\ while other +\types\ might forbid the usage of a specific \prefix. + +Please keep in mind that key names shouldn't start with "EKVC|". + +\begin{function}{\ekvcSecondaryKeys} + \begin{syntax} + \cs{ekvcSecondaryKeys}\meta{cs}\kvarg + \end{syntax} + This is the front facing macro to define secondary keys. For the macro + \meta{cs} define \key\ to have definition \val. The general syntax for \key\ + should be + \begin{quote}\ttfamily + \meta{prefix} \meta{name} + \end{quote} + Where \meta{prefix} is a space separated list of optional \prefixes\ + followed by one \type. The syntax of \meta{value} is dependent on the + used \type. +\end{function} + + +\subsubsection{\textit{Prefixes}} + +Currently there is only one \prefix\ available, which is +\begin{function}[module=expkv-cs prefix]{long} + The following key will be defined |\long|. +\end{function} + + +\subsubsection{\textit{Types}\label{sec:c:secondarytypes}} + +Compared to \expkvd\ you might notice that the \types\ here are much fewer. +Unfortunately the expansion only concept doesn't allow for great variety in the +common key \types. + +The syntax examples of the \types\ will show which \prefix\ will be +automatically used by printing those black (\texttt{\enfprefix{long}}), which +will be available in grey (\texttt{\allprefix{long}}), and which will be +disallowed in red (\texttt{\notprefix{long}}). This will be put flush right next +to the syntax line. + +If a secondary key references another key it doesn't matter whether that other +key is a primary or secondary key (unless explicitly stated otherwise). + +\begin{function}[module=expkv-cs type]{meta} + \begin{syntax} + meta \key\ = \kvarg \prefixes2 + \end{syntax} + With a |meta| key you can set other keys. Whenever \meta{key} is used the keys + in the \kv\ list will be set to the values given there. You can use the \val\ + given to \key\ by using |#1| in the \kv\ list. +\end{function} + +\begin{function}[module=expkv-cs type]{nmeta} + \begin{syntax} + nmeta \key\ = \kvarg \prefixes3 + \end{syntax} + An |nmeta| key is like a |meta| key, but it doesn't take a value at use time, + so the \kv\ list is static. +\end{function} + +\begin{function}[module=expkv-cs type]{alias} + \begin{syntax} + alias \meta{key} = \marg{key_2} \prefixes3 + \end{syntax} + This assigns the definition of \meta{key_2} to \key. As a result \key\ is an + alias for \meta{key_2} behaving just the same. Both the \Vkey\ and the \Nkey\ + will be copied if they are defined when |alias| is used. Of course, + \meta{key_2} has to be defined as at least one of \Nkey\ or \Vkey. +\end{function} + +\begin{function}[module=expkv-cs type]{default} + \begin{syntax} + default \key\ = \marg{default} \prefixes3 + \end{syntax} + If \key\ is defined as a \Vkey\ you can define a \Nkey\ version with this. The + \Nkey\ will behave as if \key\ was given \meta{default} as its \val. Note that + this doesn't change the initial values of primary keys set at definition time + (see \cs{ekvcChange} in \autoref{sec:c:change} for this). If \key\ isn't yet + defined this results in an error. +\end{function} + +\begin{function}[module=expkv-cs type]{enum} + \begin{syntax} + enum \key\ = \marg{key_2}\kvarg \prefixes2 + \end{syntax} + This defines \key\ to only accept the values given in the list of the second + argument of its definition. It forwards the position of \val\ in that list to + \meta{key_2} (zero-based). The \meta{key_2} has to already be defined by the + time an |enum| key is set up. Each \val\ in the list (and at use time) is + |\detokenize|d, so no expansion takes place here. + + If you use |enum| twice on the same \key\ the new values will again start at + zero (so it is possible to define multiple values with the same outcome), + however since you can't skip values you'll have to use the same as in the + first call for values with just a single variant. There is no interface to + delete existing values. +\end{function} +\begin{example}{Enumerating choices with the \texttt{enum} \type} + First a small example that might give you an idea of what the description + above could mean: + \begin{enverb} + \ekvcSplit\foo{k-internal=-1}{#1} + \ekvcSecondaryKeys\foo + {enum k = {k-internal}{a,b,c}} + \foo{}\foo{k=a}\foo{k=b}\foo{k=c} + \end{enverb} +\end{example} +\begin{example}{A slightly more complicated usage of the \texttt{enum} \type} + We can define a choice setup that might do different things based on the + choice encountered, and the numeric value is easy to parse using + \cs[no-index]{ifcase}: + \begin{enverb} + \ekvcSplit\foo{k-internal=-1} + {% + \ifcase#1 + is\or + This\or + easy% + \else + .% + \fi + } + \ekvcSecondaryKeys\foo + {enum k = {k-internal}{a,b,c}} + \foo{k=b} \foo{k=a} \foo{k=c}\foo{} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{choice} + \begin{syntax} + choice \key\ = \marg{key_2}\kvarg \prefixes2 + \end{syntax} + This is pretty similar to an |enum|, but unlike with |enum| the forwarded + \val\ will not be numeric, instead the \val\ as given during the definition + time will be forwarded. This means that while the user input has to match in a + |\detokenize|d form, the \val\ might still expand further during your macro's + expansion (if what you provided as a choice is expandable). +\end{function} +\begin{example}{Filtering possible values with the \texttt{choice} \type} + We could use this to filter out the possible vertical placements of a \LaTeXe\ + |tabular|: + \begin{enverb} + \ekvcSplit\foo{v-internal=c,a=t,b=c,c=b} + {% + \begin{tabular}[#1]{@{} c @{:} c @{}} + a & #2\\ + b & #3\\ + c & #4\\ + \end{tabular}% + } + \ekvcSecondaryKeys\foo + {choice v = {v-internal}{t,c,b}} + \foo{} \foo{v=t} \foo{v=c} \foo{v=b} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{aggregate,e-aggregate} + \begin{syntax} + aggregate \key\ = \marg{primary}\marg{definition} \prefixes3 + \end{syntax} + While other key \types\ replace the current value of the associated + primary key, with |aggregate| you can create keys that append or prepend (or + whatever you like) the new value to the current one. Your definition of an + |aggregate| key must be exactly two \TeX\ arguments, where \meta{primary} + should be the name of a primary key, and \meta{definition} the way you want to + store the current and the new value. Inside \meta{definition} you can use |#1| + for the current, and |#2| for the new value. The \meta{definition} will not + expand any further during the entire parsing for |aggregate|, whereas in + |e-aggregate| everything that ends up in \meta{definition} (so whatever you + provide including the values in |#1| and |#2|) will be fully expanded (using + the |\expanded| primitive), so use |\noexpand| and |\unexpanded| to protect + what shouldn't be expanded. + The resulting \meta{key} will inherit being either |short| or |long| from the + \meta{primary} key. +\end{function} +\begin{example}{Building a list with the \texttt{aggregate} \type} + The following defines an internal key (|k-internal|), which is used to build a + comma separated list from each call of the user facing key (|k|): + \begin{enverb} + \ekvcSplit\foo + {k-internal=0,color=red} + {\textcolor{#2}{#1}} + \ekvcSecondaryKeys\foo + {aggregate k = {k-internal}{#1,#2}} + \foo{}\par + \foo{k=1,k=2,k=3,k=4} + \end{enverb} +\end{example} +\begin{example}{Building a convoluted list with the \texttt{aggregate} \type} + But also more strange stuff could end there, like macros or using the same + value multiple times: + \begin{enverb}[no-tcb] + \ekvcSecondaryKeys\foo + {aggregate k = {k-internal}{\old{#1}\new{#2\old{#1}}}} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{flag-bool} + \begin{syntax} + flag-bool \key\ = \meta{cs} \prefixes3 + \end{syntax} + This is a secondary \type\ that doesn't involve any of the primary or other + secondary keys. This defines \key\ to take a value, which should be either + |true| or |false|, and set the flag called \meta{cs} accordingly as a boolean. + If \meta{cs} isn't defined yet it will be initialised as a flag. Note that the + flag will not be set to a specific state automatically so a flag set in one + macro might affect every other macro in the current scope. Please also read + \autoref{sec:c:flags}. +\end{function} +\begin{example}{Using a Boolean flag with the \texttt{flag-bool} \type} + Provide a key |bold| to turn the output of our macro bold if the associated + flag is |true|. + \begin{enverb} + \ekvcSplit\foo{a=a,b=b} + {% + \ekvcFlagIf\fooFlag + {\textbf{a is #1 and b is #2}\par} + {a is #1 and b is #2\par}% + } + \ekvcSecondaryKeys\foo{flag-bool bold = \fooFlag} + \foo{}\foo{bold=true}\foo{}\foo{bold=false}\foo{} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{flag-true,flag-false} + \begin{syntax} + flag-true \key\ = \meta{cs} \prefixes3 + \end{syntax} + This is similar to |flag-bool|, but the \meta{key} will be a \Nkey\ and if + used will set the flag to either |true| or |false|. If \meta{cs} isn't defined + yet it will be initialised as a flag. Note that the flag will not be set to a + specific state automatically. Please also read \autoref{sec:c:flags}. +\end{function} + +\begin{function}[module=expkv-cs type]{flag-raise} + \begin{syntax} + flag-raise \key\ = \meta{cs} \prefixes3 + \end{syntax} + This defines \key\ to be a \Nkey\ that will raise the flag called \meta{cs} on + usage. If \meta{cs} isn't defined yet it will be initialised as a flag. Note + that the flag will not be set to a specific state automatically. Please also + read \autoref{sec:c:flags}. +\end{function} + + +\subsection{Changing the Initial Values\label{sec:c:change}} + +\begin{function}{\ekvcChange} + \begin{syntax} + \cs{ekvcChange}\meta{cs}\kvarg + \end{syntax} + This processes the \kv\ list for the macro \meta{cs} to set new defaults for + it (meaning the initial values used if you don't provide anything at use time, + not those specified with the |default| \type). \meta{cs} should be defined + with \expkvc\ (but it doesn't matter if it's a split or hash variant). Inside + the \kv\ list both primary and secondary keys can be used. If \meta{cs} was + defined |\long| earlier it will still be |\long|, every other \TeX\ prefix + will be stripped (but \expkvc\ doesn't support them anywhere else so that + should be fine). The resulting new defaults will be stored inside the + \meta{cs} locally (just as the original initial values were). If there was an + unknown key forwarding added to \meta{cs} (see \autoref{sec:c:unknown}) any + unknown key will be stored inside the list of unknown keys as well. + \cs{ekvcChange} is not expandable! +\end{function} +\begin{example} + {Changing the values for future calls using \cs[no-index]{ekvcChange}} + With \cs{ekvcChange} we can now do the following: + \begin{enverb} + \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} + \begingroup + \ekvcChange\foo{b=B} + \foo{} + \ekvcSecondaryKeys\foo{meta c={a={#1},b={#1}}} + \ekvcChange\foo{c=c} + \foo{} + \endgroup + \foo{} + \end{enverb} +\end{example} +\begin{example}{A typical setup macro for \expkvc\ macros} + As a result with this the typical setup macro could be implemented: + \begin{enverb}[no-tcb] + \ekvcHashAndUse\fooKV{keyA=a,keyB=b} + \def\fooA#1{\fooKV{#1}\fooAinternal} + \def\fooB#1{\fooKV{#1}\fooBinternal} + \protected\def\foosetup{\ekvcChange\fooKV} + \end{enverb} + Of course the usage is limited to a single macro |\fooKV|, hence this might + not be as powerful as similar macros used with other \kv\ interfaces. But at + least a few similar macros could be grouped using the same key parsing macro + internally like |\fooA| and |\fooB| do in this example. +\end{example} + + +\subsection{Handling Unknown Keys\label{sec:c:unknown}} + +If your macro should handle unknown keys without directly throwing an error you +can use the special |...| marker in the \meta{primary keys} list. Since those +keys will be processed once by \expkv\ they will be forwarded normalised: The +\key\ and the \val\ will be forwarded with one set of surrounding spaces and +braces, so a \kv\ pair will result in \texttt{\vissp{ \marg{key} = \marg{val} }} +and a \Nkey\ is forwarded as \texttt{\vissp{ \marg{key} }} (this way most other +\kv\ implementations should parse the correct input). + +The exact behaviour differs slightly between the two variants (as all primary +keys do). The behaviour inside the split variants will be similar to normal +primary keys, the $n$-th argument (corresponding to the position of |...| inside +the primary keys list) will contain any unknown key encountered while parsing +the argument. And inside the split variant you can use a primary key named |...| +at the same time (since only the position in the list determines the argument, +not the name). +\begin{example}{Using unknown key handlers to wrap another \kv\ enabled macro} + The following will forward any unknown key to |\includegraphics| to control + the appearance while processing its own keys: + \begin{enverb}[same-line=0.6] + \newcommand*\foo{\ekvoptarg\fooKV{}} + \ekvcSplitAndForward\fooKV\fooOUT + { + a=a + ,... + ,b=b + ,...={} + } + \newcommand\fooOUT[5] + {% + a is #1 and b is #3.\par + \includegraphics[{#2}]{#5}\par + \texttt{...} is #4.\par + } + \foo[width=.5\linewidth, b=c, + ...={a stupid key name, but works}] + {example-image-duck} + \end{enverb} +\end{example} + +Inside the hash variants the unknown keys list will be put inside the hash named +|...| (we have to use some name, and this one seems reasonable). As a +consequence a primary key named |...| would clash with the unknown key handler. +If you still used such a key it would remove any unknown key stored there until +that point and replace the list with its value. +\begin{example}{Wrapping an existing macro, but with a hash variant} + The following is more or less equivalent to the above example, but with the + hash variant, and it will not contain the primary |...| key. We have to make + sure that |\includegraphics| sees the \kv\ list, so need to expand + |\ekvcValue{...}{#1}| before |\includegraphics| parses it. + \begin{enverb}[same-line=0.76] + \newcommand*\foo{\ekvoptarg\fooKV{}} + \ekvcHashAndForward\fooKV\fooOUT + {a=a, b=b, ...} + \newcommand\fooOUT[2] + {% + a is \ekvcValue{a}{#1} and + b is \ekvcValue{b}{#1}.\par + \ekvcValueSplit{...}{#1}{\includegraphics[}]% + {#2}\par + } + \foo[width=\linewidth, b=c] + {example-image-duck-portrait} + \end{enverb} +\end{example} + + +\subsection{Flags\label{sec:c:flags}} + +The idea of flags is taken from \pkg{expl3}. They provide a way to store +numerical information expandably, however only incrementing and accessing works +expandably, decrementing is unexpandable. A flag has a height, which is a +numerical value, and which can be raised by 1. Flags come at a high +computational cost (accessing them is slow and they require more memory than +normal \TeX\ data types like registers, both issues getting linearly worse with +the height), so don't use them if not necessary. + +The state of flags is always changed locally to the current group, but not to +the current macro, so if you're using one of the \types\ involving flags +bear in mind that they can affect other macros using the same flags at the +current scope! + +\expkvc\ provides some macros to access, alter, and use flags. Flags of \expkvc\ +don't share a name space with the flags of \pkg{expl3}. + +\begin{function}{\ekvcFlagNew} + \begin{syntax} + \cs{ekvcFlagNew}\meta{flag} + \end{syntax} + This initialises the macro \meta{flag} as a new flag. It isn't checked whether + the macro \meta{flag} is currently undefined. A \meta{flag} will expand to the + flag's current height with a trailing space (so you can use it directly with + \cs[no-index]{ifnum} for example and it will terminate the number scanning on + its own). + + All other macros dealing with flags take as a parameter a macro defined as a + \meta{flag} with \cs{ekvcFlagNew}. +\end{function} + +\begin{function}{\ekvcFlagHeight} + \begin{syntax} + \cs{ekvcFlagHeight}\meta{flag} + \end{syntax} + This expands to the current height of \meta{flag} in a single step of + expansion (without a trailing space). +\end{function} + +\begin{function}{\ekvcFlagRaise} + \begin{syntax} + \cs{ekvcFlagRaise}\meta{flag} + \end{syntax} + This expandably raises the height of \meta{flag} by 1. +\end{function} + +\begin{function}{\ekvcFlagSetTrue,\ekvcFlagSetFalse} + \begin{syntax} + \cs{ekvcFlagSetTrue}\meta{flag} + \end{syntax} + By interpreting an even value as false and an odd value as true we can use a + flag as a boolean. This expandably sets \meta{flag} to true or false, + respectively, by raising it if necessary. +\end{function} + +\begin{function}{\ekvcFlagIf} + \begin{syntax} + \cs{ekvcFlagIf}\meta{flag}\marg{true}\marg{false} + \end{syntax} + This interprets a \meta{flag} as a boolean and expands to either \meta{true} + or \meta{false}. +\end{function} + +\begin{function}{\ekvcFlagIfRaised} + \begin{syntax} + \cs{ekvcFlagIfRaised}\meta{flag}\marg{true}\marg{false} + \end{syntax} + This tests whether the \meta{flag} is raised, meaning it has a height greater + than zero, and if so expands to \meta{true} else to \meta{false}. +\end{function} + +\begin{function}{\ekvcFlagReset,\ekvcFlagResetGlobal} + \begin{syntax} + \cs{ekvcFlagReset}\meta{flag} + \end{syntax} + This resets a flag (so restores its height to 0). This operation is \emph{not} + expandable and done locally for \cs{ekvcFlagReset} and globally for + \cs{ekvcFlagResetGlobal}. If you really intend to use flags you can reset them + every now and then to keep the performance hit low. +\end{function} + +\begin{function}{\ekvcFlagGetHeight} + \begin{syntax} + \cs{ekvcFlagGetHeight}\meta{flag}\marg{next} + \end{syntax} + This retrieves the current height of the \meta{flag} and provides it as a + braced argument to \meta{next}, leaving \meta{next}\marg{height} in the input + stream. +\end{function} + +\begin{function}{\ekvcFlagGetHeights} + \begin{syntax} + \cs{ekvcFlagGetHeights}\marg{flag-list}\marg{next} + \end{syntax} + This retrieves the current height of each \meta{flag} in the \meta{flag-list} + and provides them as a single argument to \meta{next}. Inside that argument + each height is enclosed in a set of braces individually. The \meta{flag-list} + is just a single argument containing the \meta{flag}s. So a usage like + |\ekvcFlagGetHeights{\myflagA\myflagB}{\stuff}| will expand to + |\stuff{|\marg{height-A}\marg{height-B}|}|. +\end{function} + + +\subsection{Further Examples} + +\begin{example}{Defining an expandable \kv\ macro with an optional argument} + Using \cs[no-index]{NewExpandableDocumentCommand} or \expkv's \cs{ekvoptarg} + or \cs{ekvoptargTF} and forwarding arguments one can easily define \kv\ macros + with actual optional and mandatory arguments as well. A small nonsense + example: + \begin{enverb}[below,inter=resulting in] + \makeatletter + \newcommand*\nonsense{\ekvoptarg\nonsense@a{}} + \ekvcHashAndForward\nonsense@a\nonsense@b + { + keyA = A, + keyB = B, + keyC = c, + keyD = d, + } + \newcommand*\nonsense@b[2] + {% + \begin{tabular}{lll|} + key & A & \ekvcValue{keyA}{#1} \\ + & B & \ekvcValue{keyB}{#1} \\ + & C & \ekvcValue{keyC}{#1} \\ + & D & \ekvcValue{keyD}{#1} \\ + \multicolumn{2}{l}{mandatory} & #2 \\ + \end{tabular}% + } + \makeatother + \nonsense{} % do nonsense + \nonsense[keyA=hihi]{haha} + \nonsense[keyA=hihi, keyB=A]{hehe} + \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} + \end{enverb} +\end{example} + +\begin{example}{The \cs[no-index]{sine} example revisited} + In \autoref{sec:expkv:sneak} I presented an expandable macro to calculate the + sine of some user input with a few keys, and there I hinted to \expkvc, so + here's the same function implemented with |\ekvcSplitAndForward|. There is a + small difference here, we need to use an internal key to store whether degrees + or radians will be used, but we don't need to use an internal key to collect + the values of our individual keys in the correct order. + \begin{enverb} + \makeatletter + \newcommand\sine{\ekvoptarg\sine@kv{}} + \ekvcSplitAndForward\sine@kv\sine@do + { + f = sin + ,internal = d + ,round = 3 + } + \ekvcSecondaryKeys\sine@kv + { + nmeta degree = internal=d + ,nmeta radian = internal={} + } + \newcommand*\sine@do[4]{\fpeval{round(#1#2(#4),#3)}} + \makeatother + \sine{60}\par + \sine[round=10]{60}\par + \sine[f=cos,radian]{pi}\par + \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval} + \end{enverb} +\end{example} + + +\subsection{Freedom for Keys!} + +If this had been the \TeX{}book this subsection would have had a double bend +sign. Not because it is overly complicated, but because it shows things which +could break \expkvc's expandability and its alignment safety. This is for +experienced users wanting to get the most flexibility and knowing what they are +doing. + +In case you're wondering, it is possible to define other keys than the primaries +and the secondary key \types\ listed in \autoref{sec:c:secondary} for a +macro defined with \expkvc\ by using the low-level interface of \expkv\ or even +the interface provided by \expkvd. The set name used for \expkvc's keys is the +macro name, including the leading backslash, or more precisely the result of +|\string|\meta{cs} is used. This can be exploited to define additional keys with +arbitrary code. Consider the following \emph{bad} example: +\begin{enverb}[no-tcb] + \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2.\par} + \protected\ekvdef{\string\foo}{c}{\def\fooC{#1}} +\end{enverb} +This would define a key named |c| that will store its \val\ inside a macro. The +issue with this is that this can't be done expandably. As a result, the macro +|\foo| isn't always expandable any more (not that bad if this was never +required; killjoy if it was) and as soon as the key |c| is used it is also no +longer alignment safe\footnote{This means that the \kv\ list can't contain + alignment markers that are not inside an additional set of braces if used + inside a \TeX\ alignment} +(might be bad depending on the usage). + +So why do I show you this? Because we could as well do something useful like +creating a key that pre-parses the input and after that passes the parsed value +on. This parsing would have to be completely expandable though (and we could +perhaps also implement this using the |e-aggregate| \type). For the +pass-on part we can use the following function: + +\begin{function}{\ekvcPass} + \begin{syntax} + \cs{ekvcPass}\meta{cs}\marg{key}\marg{value} + \end{syntax} + This passes \val\ on to \key\ for the \expkvc-macro \meta{cs}. It should be + used inside the key parsing of a macro defined with \expkvc, else this most + likely results in a low level \TeX\ error. You can't forward anything to the + special unknown key handler |...| as that is no defined key. +\end{function} +\begin{example} + {Forwarding pre-parsed keys to an \expkvc\ key with \cs[no-index]{ekvcPass}} + With this we could for example split the value of a key at a hyphen and pass + the parts to different keys: + \begin{enverb} + \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2.\par} + \ekvdef{\string\foo}{c}{\fooSplit#1\par} + \def\fooSplit#1-#2\par + {\ekvcPass\foo{a}{#1}\ekvcPass\foo{b}{#2}} + \foo{} + \foo{c=1-2} + \end{enverb} +\end{example} + +Additionally, there is a more general version of the |aggregate| secondary key +type, namely the |process| key type: + +\begin{function}[module=expkv-cs type]{process} + \begin{syntax} + process \key\ = \marg{primary}\marg{definition} \prefixes2 + \end{syntax} + This will grab the current value of a \meta{primary} key as |#1| (without + changing the current value) and the new value as |#2| and leave all the + processing to \meta{definition}. You should use \cs{ekvcPass} to forward the + values afterwards. Unlike |aggregate| you can specify whether the \meta{key} + should be long or not, this isn't inherited from the \meta{primary} key. Keep + in mind that you could easily break things here if your code does not work by + expansion. +\end{function} +\begin{example}{Filtering out values with the \texttt{process} \type} + We could define a key that only accepts values greater than the current value + with this: + \begin{enverb} + \ekvcSplit\foo{internal=5}{a is #1.\par} + \ekvcSecondaryKeys\foo + { + process a={internal} + {\ifnum#1<#2 \ekvcPass\foo{internal}{#2}\fi} + } + \foo{a=1} + \foo{a=5} + \foo{a=9} + \end{enverb} +\end{example} +\begin{example}{Filtering out values with the \texttt{e-aggregate} \type} + The same is possible with an |e-aggregate| key as well though: + \begin{enverb}[no-tcb] + \ekvcSplit\foo{internal=5}{a is #1.\par} + \ekvcSecondaryKeys\foo + { + e-aggregate a={internal} + {\ifnum#1<#2 \unexpanded{#2}\else\unexpanded{#1}\fi} + } + \end{enverb} +\end{example} + +\subsection{Useless Macros} + +These macros are most likely of little to no interest to users. + +\begin{function}{\ekvcDate,\ekvcVersion} + These two macros store the version and date of the package/generic code. +\end{function} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/pkg-def.tex b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-def.tex new file mode 100644 index 00000000000..fade98e8b8f --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-def.tex @@ -0,0 +1,727 @@ +\genericekv\expkvd{-def} + +Since the trend for the last couple of years goes to defining keys for a \kv\ +interface using a \kv\ interface, I thought that maybe providing such an +interface for \expkv\ will make it more attractive for actual use. But at the +same time I didn't want to broaden \expkv's initial scope. So here is \expkvd, +go define \kv\ interfaces with \kv\ interfaces. + +Unlike many of the other established \kv\ interfaces to define keys, \expkvd\ +works using prefixes instead of suffixes (\emph{e.g.}, |.tl_set:N| of +\pkg{l3keys}) or directory like handlers (\emph{e.g.}, |/.store in| of +\pkg{pgfkeys}). This was decided as a personal preference, more over in \TeX\ +parsing for the first spaces is way easier than parsing for the last one, so +this should also turn out to be faster. \expkvd's prefixes are sorted into two +categories: \prefixes, which are equivalent to \TeX's prefixes like |\long| and +of which a \key\ can have multiple, and \types\ defining the basic behaviour of +the \key\ and of which a \key\ must have one. For a description of the available +\prefixes\ take a look at \autoref{sec:d:prefixes}, the \types\ are described in +\autoref{sec:d:types}. + + +\subsection{Macros\label{sec:d:macros}} + +The number of user-facing macros is quite manageable: + +\begin{function}{\ekvdefinekeys} + \begin{syntax} + \cs{ekvdefinekeys}\marg{set}\kvarg + \end{syntax} + In \meta{set}, define \key\ to have definition \val. The general syntax for + \key\ should be + \begin{quote} + \ttfamily + \meta{prefix} \meta{name} + \end{quote} + where \meta{prefix} is a space separated list of optional \prefixes\ followed + by one \type. The syntax of \val\ is dependent on the used \type. +\end{function} + +\begin{function}{\ekvdDate,\ekvdVersion} + These two macros store the version and date of the package. +\end{function} + + +\subsection{Prefixes} + +As already said, prefixes are separated into two groups, \prefixes\ and \types. +Not every \prefix\ is allowed for all \types. + + +\subsubsection{\textit{Prefixes}\label{sec:d:prefixes}} + +\begin{function}[module=expkv-def prefix]{new} + The following \key\ must be new (so previously undefined). An error is thrown + if it is already defined and the new definition is ignored. |new| only asserts + that there are no conflicts between \Nkey{}s and other \Nkey{}s or \Vkey{}s + and other \Vkey{}s. +\end{function} +\begin{example}{The effects of the \texttt{new} \prefix} + You can test the following (lines throwing an error are marked by a comment, + error messages are printed in red for this example): + \expkvdocPrintErrors[\par]% + \begin{enverb}[below] + \ekvdefinekeys{new-example} + { + new code key = \domystuffwitharg{#1} + ,new noval KEY = \domystuffwithoutarg + ,new bool key = \mybool % Error! + ,new bool KEY = \mybool % Error! + ,new meta key = {KEY} % Error! + ,new nmeta KEY = {key} % Error! + } + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def prefix]{also} + The following key \type\ will be \emph{added} to an existing \key's + definition. You can't add a \type\ taking an argument at use time to an + existing \key\ which doesn't take an argument and vice versa. Also you'll get + an error if you try to add an action which isn't allowed to be either |\long| + or |\protected| to a \key\ which already is |\long| or |\protected| (the + opposite order would be suboptimal as well, but can't be really captured with + the current code). + + A \key\ already defined as |\long| or |\protected| will stay that way, but you + can add |\long| or |\protected| to a \key\ which isn't by using |also|. +\end{function} +\begin{example} + {Overload a key \type\ with another with the \texttt{also} \prefix} + Suppose you want to create a boolean \key, but additionally to setting a + boolean value you want to execute some more code as well. For this you can use + the following: + \begin{enverb}[no-tcb] + \ekvdefinekeys{also-example} + { + bool key = \ifmybool + ,also code key = \domystuff{#1} + } + \end{enverb} +\end{example} + +If you use |also| on a |choice|, |bool|, |invbool|, or |boolpair| \key\ it is +tried to determine if the key already is of one of those types. If this test is +true the declared choices will be added to the possible choices but the key's +definition will not be changed other than that. If that wouldn't have been done, +the callbacks of the different choices could get called multiple times. + +\begin{function}[module=expkv-def prefix]{protected,protect} + The following \key\ will be defined |\protected|. Note that \types\ which + can't be defined expandable will always use |\protected|. This only affects + the key at use time not the \key\ definition. +\end{function} + +\begin{function}[module=expkv-def prefix]{long} + The following \key\ will be defined |\long| (so can take an explicit |\par| + token in its \val). Please note that this only changes the \key\ at use time. + |long| being present or not doesn't limit you to use |\par| inside of the + \key's definition (if the \type\ allows this). +\end{function} + + +\subsubsection{\textit{Types}\label{sec:d:types}} + +Since the \prefixes\ apply to some of the \types\ automatically but sometimes +one might be disallowed we need some way to highlight this behaviour. In the +following an enforced \prefix\ will be printed black +(\texttt{\enfprefix{protected}}), allowed \prefixes\ will be grey +(\texttt{\allprefix{protected}}), and disallowed \prefixes\ will be red +(\texttt{\notprefix{protected}}). This will be put flush-right in the syntax +showing line. + +\begin{function}[module=expkv-def type]{code,ecode} + \begin{syntax} + code \key\ = \marg{definition} \prefixes2222 + \end{syntax} + Define \key\ to be a \Vkey\ expanding to \meta{definition}. You can use |#1| + inside \meta{definition} to access the \key's \val. The |ecode| variant will + fully expand \meta{definition} inside an |\edef|. +\end{function} +\ekvset{enverb}{no-tcb,store}% affect all the next examples. +\begin{example} + {Defining a \Vkey\ with arbitrary effect with the \texttt{code} \type} + The following defines the key |foo|, that'll count the number of tokens passed + to it (we'll borrow a function from \pkg{expl3} for this). It'll accept + explicit |\par| tokens. Also it'll flip the \TeX-if \cs[no-index]{iffoo} to + true. The result of the counting will be stored in a count register. (Don't + get confused, all the next examples are part of this |\ekvdefinekeys| call, so + there is no closing brace here.) + \begin{enverb} + \ExplSyntaxOn + \cs_new_eq:NN \exampleCount \tl_count_tokens:n + \ExplSyntaxOff + \newcount\examplefoocount + \newif\iffoo + \ekvdefinekeys{example} + { + protected long code foo = + \footrue + \examplefoocount=\exampleCount{#1}\relax + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{noval,enoval} + \begin{syntax} + noval \key\ = \marg{definition} \prefixes2223 + \end{syntax} + The |noval| \type\ defines \key\ as a \Nkey\ expanding to \meta{definition}. + |enoval| fully expands \meta{definition} inside an |\edef|. +\end{function} +\begin{example}{An arbitrary \Nkey\ action with the \texttt{noval} \type} + The following defines the \Nkey\ |foo| to toggle the \TeX-if + \cs[no-index]{iffoo} to false and set |\examplecount| to |0|. It'll be + |\protected| and mustn't override any existing key. + \begin{enverb} + ,new protected noval foo = \foofalse\examplefoocount=0\relax + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{default,odefault,fdefault,edefault} + \begin{syntax} + default \key\ = \marg{definition} \prefixes2223 + \end{syntax} + This serves to place a default \val\ for a \Vkey. Afterwards if you use \key\ + as a \Nkey\ it will be the same as if \key\ got passed \meta{definition} as + its \val. The |odefault| variant will expand the key-macro once, so will be + slightly quicker, but not change if you redefine the \Vkey\ afterwards. The + |fdefault| version will expand the key-code until a non-expandable token or a + space is found, a space would be gobbled.\footnotemark{} + The |edefault| on the other hand fully expands the key-code with + \meta{definition} as its argument in |\expanded|. The \prefix\ |new| + means that there should be no \Nkey\ of that name yet. +\end{function}% +\footnotetext{For those familiar with \TeX-coding: This uses a +\cs[no-index]{romannumeral}-expansion} +\begin{example} + {Setting a default value for a \Vkey\ with the \texttt{default} \type} + We later decide that the above behaviour isn't what we need any more and + instead redefine the \Nkey\ |foo| to pass some default value to the \Vkey\ + |foo|. + \begin{enverb} + ,default foo = {Some creative default text} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{initial,oinitial,finitial,einitial} + \begin{syntax} + initial \key\ = \marg{value} \prefixes3333 + initial \key + \end{syntax} + With |initial| you can set an initial \val\ for an already defined \key. It'll + just call the \key\ and pass it \val. The |einitial| variant will expand \val\ + using |\expanded| prior to passing it to the \key\ and the |oinitial| + variant will expand the first token in \val\ once. |finitial| will expand + \val\ until a non-expandable token or a space is found, a space would be + gobbled.\footnotemark + + If you don't provide a \val\ (and no equals sign) the \Nkey\ of the same name + is called once (or, if you specified a |default| for a \Vkey\ that would be + used). +\end{function}% +\footnotetext{Again using \cs[no-index]{romannumeral}} +\begin{example}{Specifying initial values with the \texttt{initial} \type} + We want to get a defined initial behaviour for our |foo|. So we count + 0~tokens. + \begin{enverb} + ,initial foo = {} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{bool,gbool,boolTF,gboolTF} + \begin{syntax} + bool \key\ = \meta{cs} \prefixes2223 + \end{syntax} + \singlecs{iffoo} + This will define \key\ to be a boolean key, which only takes the values |true| + or |false| and will throw an error for other values. If the \key\ is used as + a \Nkey\ it'll have the same effect as if you use |true|. |bool| and |gbool| + will behave like \TeX-ifs, so either be \cs[no-index]{iftrue} or + \cs[no-index]{iffalse}. The \meta{cs} in the |boolTF| and |gboolTF| variants + will take two arguments and if true the first will be used else the second, so + they are always either |\@firstoftwo| or |\@secondoftwo|. The variants with a + leading |g| will set the \meta{cs} globally, the other locally. If \meta{cs} + is not yet defined it'll be initialised as the |false| version. Note that the + initialisation is \emph{not} done with |\newif|, so you will not be able to do + |\footrue| outside of the \kv\ interface, but you could use |\newif| yourself. + Even if the \key\ will not be |\protected| the commands which execute the + |true| or |false| choice will be, so the usage should be safe in an expansion + context (\emph{e.g.}, you can use \texttt{edefault \key\ = false} without an + issue to change the default behaviour to execute the |false| choice). + Internally a |bool| is the same as a |choice| \type\ which is set up to handle + |true| and |false| as choices. |new| will assert that neither the \Vkey\ nor + the \Nkey\ are already defined. +\end{function} +\begin{example}{Defining Boolean keys with the \texttt{bool} \type} + Also we want to have a direct way to set our \cs[no-index]{iffoo}, now that + the \Nkey\ doesn't toggle it any longer. + \begin{enverb} + ,bool dofoo = \iffoo + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{invbool,ginvbool,invboolTF,ginvboolTF} + \begin{syntax} + invbool \key\ = \meta{cs} \prefixes2223 + \end{syntax} + These are inverse boolean keys, they behave like |bool| and friends but set + the opposite meaning to the macro \meta{cs} in each case. So if |key=true| is + used |invbool| will set \meta{cs} to \cs[no-index]{iffalse} and vice versa. +\end{function} +\begin{example} + {Inversing the logic of a Boolean with the \texttt{invbool} \type} + And since traditional interfaces lacked \kv\ support for packages, often a + negated boolean key was used as well. + \begin{enverb} + ,invbool nofoo = \iffoo + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{boolpair,gboolpair,boolpairTF,gboolpairTF} + \begin{syntax} + boolpair \key\ = \meta{cs_1}\meta{cs_2} \prefixes2223 + \end{syntax} + The |boolpair| \type\ behaves like both |bool| and |invbool|, the \meta{cs_1} + will be set to the meaning according to the rules of |bool|, and \meta{cs_2} + will be set to the opposite. +\end{function} + +\begin{function}[module=expkv-def type]{store,estore,gstore,xstore} + \begin{syntax} + store \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + This will define a \Vkey\ to store \val\ inside of the control sequence. If + \meta{cs} isn't yet defined it will be initialised as empty. The variants + behave similarly to their |\def|, |\edef|, |\gdef|, and |\xdef| counterparts, + but will allow you to store macro parameters inside them without needing to + double them. So |estore foo = \foo, initial foo = #1| will not result in a low + level \TeX\ error. +\end{function} +\begin{example} + {Also store the \val\ of an existing \key\ in a macro using the + \texttt{also} \prefix\ and the \texttt{store} \type} + Not only do we want to count the tokens handed to |foo|, but we want to also + store them inside of a macro (and we don't need to specify |long| here, since + |foo| is already |\long| from our |code| definition above). + \begin{enverb} + ,also store foo = \examplefoostore + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{data,edata,gdata,xdata} + \begin{syntax} + data \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + This will define a \Vkey\ to store \val\ inside of the control sequence. But + unlike the |store| \type\ the macro \meta{cs} will be a switch at the same + time, it'll take two arguments and if \meta{key} was used expands to the first + argument followed by \val\ in braces, if \key\ was not used \meta{cs} will + expand to the second argument (so behave like |\@secondoftwo|). The idea is + that with this type you can define a key which should be typeset formatted. + The |edata| and |xdata| variants will fully expand \val, the |gdata| and + |xdata| variants will store \val\ inside \meta{cs} globally. Juts like with + |store| you can use macro parameters without having to double them. The + \prefixes\ only affect the key-macro, \meta{cs} will always be expandable and + |\long|. +\end{function} +\begin{example}{Define a key using the \texttt{data} \type} + Next we start to define other keys, now that our |foo| is pretty much + exhausted. The following defines a key |bar| to be a |data| key. + \begin{enverb} + ,data bar = \examplebar + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{dataT,edataT,gdataT,xdataT} + \begin{syntax} + dataT \key\ = \meta{cs} \prefixes2212 + \end{syntax} + Just like |data|, but instead of \meta{cs} grabbing two arguments it'll only + grab one, so by default it'll behave like |\@gobble|, and if \val\ was given + to \key\ the \meta{cs} will behave like |\@firstofone| appended by + \marg{value}. +\end{function} +\begin{example}{Define a key using the \texttt{dataT} \type} + Another key we want to use is |baz|. + \begin{enverb} + ,dataT baz = \examplebaz + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{int,eint,gint,xint} + \begin{syntax} + int \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + An |int| key will be a \Vkey\ setting a \TeX\ count register. If \meta{cs} + isn't defined yet, |\newcount| will be used to initialise it. The |eint| and + |xint| variants will use |\numexpr| to allow basic computations in their \val. + The |gint| and |xint| variants set the register globally. +\end{function} + +\begin{function}[module=expkv-def type]{dimen,edimen,gdimen,xdimen} + \begin{syntax} + dimen \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + This is just like |int| but uses a dimen register, |\newdimen|, and |\dimexpr| + instead. +\end{function} + +\begin{function}[module=expkv-def type]{skip,eskip,gskip,xskip} + \begin{syntax} + skip \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + This is just like |int| but uses a skip register, |\newskip|, and |\glueexpr| + instead. +\end{function} +\begin{example} + {Define keys that use \TeX\ registers, here a skip with the \texttt{eskip} + \type} + Exemplary for the different register keys, the following defines |distance| so + that we can store some distance. + \begin{enverb} + ,eskip distance = \exampledistance + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{toks,gtoks,apptoks,gapptoks,pretoks,gpretoks} + \begin{syntax} + toks \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + Store \val\ inside of a toks-register. The |g| variants use |\global|, the + |app| variants append \val\ to the contents of that register, the |pre| + variants will prepend \val. If \meta{cs} is not yet defined it will be + initialised with |\newtoks|. +\end{function} + +\begin{function}[module=expkv-def type]{box,gbox} + \begin{syntax} + box \key\ = \meta{cs} \prefixes2212 + \end{syntax} + \singlecs{foo} + Typesets \val\ into a |\hbox| and stores the result in a box register. The + boxes are colour safe. \expkvd\ currently doesn't provide a |vbox| type. +\end{function} + +\begin{function}[module=expkv-def type]{meta} + \begin{syntax} + meta \key\ = \kvarg \prefixes2222 + \end{syntax} + This key \type\ can set other keys, you can access the \val\ given to the + created \Vkey\ inside the \kv\ list using |#1|. This works by injecting the + \kv\ list into the currently parsed list, so behaves just as if the \kv\ list + was directly used instead of \key. +\end{function} +\begin{example} + {Define a \Vkey\ as a shortcut to set multiple other keys with the + \texttt{meta} \type} + And we want to set a full set of keys with just this single one called |all|. + \begin{enverb} + ,meta all = + {distance=5pt,baz=cheese cake,bar=cocktail bar,foo={#1}} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{nmeta} + \begin{syntax} + nmeta \key\ = \kvarg \prefixes2223 + \end{syntax} + This \type\ sets other keys, but unlike |meta| this defines a \Nkey, so the + \kv\ list is static. +\end{function} +\begin{example} + {Set multiple other keys from a \Nkey\ with the \texttt{nmeta} \type} + and if |all| is set without a value we want to do something about it as well. + \begin{enverb} + ,nmeta all = + {distance=10pt,baz=nothing,bar=Waikiki bar,foo} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{smeta} + \begin{syntax} + smeta \key\ = \marg{set}\kvarg \prefixes2222 + \end{syntax} + Yet another |meta| variant. |smeta| will define a \Vkey, you can access the + given \val\ in the provided \kv\ list using |#1|. Unlike |meta| this will + process that \kv\ list inside of \meta{set} using a nested |\ekvset| call, so + this is equal to \texttt{\cs[no-index]{ekvset}\marg{set}\kvarg}. As a + result you can't use |\ekvsneak| using keys or similar macros in the way you + normally could. +\end{function} + +\begin{function}[module=expkv-def type]{snmeta} + \begin{syntax} + snmeta \key\ = \marg{set}\kvarg \prefixes2223 + \end{syntax} + And the last |meta| variant. |snmeta| combines |smeta| and |nmeta|, so parses + the \kv\ list inside of \meta{set} and defines a \Nkey\ with a static list. +\end{function} + +\begin{function}[module=expkv-def type]{set} + \begin{syntax} + set \key\ = \marg{set} \prefixes2233 + set \key + \end{syntax} + This will define a \Nkey\ that will change the current set to \meta{set}. If + you give no value to this definition (omit |= |\marg{set}) the set name will + be the same as \key\ so + |set |\key\ + is equivalent to + |set |\key| = |\marg{key}. + Note that just like in \expkv\ it'll not be checked whether \meta{set} is + defined and you'll get a low-level \TeX\ error if you use an undefined + \meta{set}. +\end{function} + +\begin{function}[module=expkv-def type]{choice} + \begin{syntax} + choice \key\ = \{\val=\meta{definition}, \ldots\} \prefixes2223 + \end{syntax} + |choice| defines a \Vkey\ that will only accept a limited set of values. You + should define each possible \meta{value} inside of the \val=\meta{definition} + list. If a defined \meta{value} is passed to \meta{key} the \meta{definition} + will be left in the input stream. You can make individual values |protected| + inside the \val=\meta{definition} list by using that \prefix. To also allow + choices that shouldn't be |\protected| but which start with the word + |protected| you can also use |unprotected| as a special \prefix. By default a + |choice| key and all its choices are expandable, an undefined \meta{value} + will throw an error in an expandable way. You can add additional choices after + the \meta{key} was created by using |choice| again for the same \key, + redefining choices is possible the same way, but there is no interface to + remove certain choices. To change the behaviour of unknown choices see also + the |unknown-choice| \type. +\end{function} +\begin{example} + {Define a choice with arbitrary code using the \texttt{choice} \type} + We give the users a few choices. + \begin{enverb} + ,choice choose = + { + protected lemonade = \def\exampledrink{something sour} + ,protected water = \def\exampledrink{something boring} + } + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{choice-store} + \begin{syntax} + choice-store \key\ = \meta{cs}\{\val=\meta{definition}, \ldots\} \prefixes2223 + \end{syntax} + \singlecs{foo} + This is a special \type\ of the |choice| \type\ that'll store the given choice + inside the macro \meta{cs}. Since storing inside a macro can't be done + expandably every choice-code is |\protected|, and you might define the + |choice-store| key itself as |\protected| as well if you want. Inside the + \val|=|\meta{definition} list the |=|\meta{definition} part is optional, if + you omit it the \val\ will be stored as given during define-time inside of + \meta{cs} (during use-time the \val\ needs to be matched |\detokenize|d), and + if you specify |=|\meta{definition} that \meta{definition} will be stored + inside of \meta{cs} instead. If \meta{cs} doesn't yet exist it's initialised + as empty. +\end{function} +\begin{example} + {Show the equivalent setup for a \texttt{choice} \type\ to mimic a + \texttt{choice-store} \type} + The following keys |key1| and |key2| are equivalent at use time (this doesn't + continue the |\ekvdefinekeys|-call for the set |example| above): + \begin{enverb}[no-store,no-tcb] + \newcommand*\mya{}% initialise \mya + \ekvdefinekeys{choice-store-example} + { + choice key1 = + { + protected a = \def\mya{a} + ,protected b = \def\mya{b} + ,protected c = \def\mya{c} + ,protected d = \def\mya{FOO} + } + ,choice-store key2 = \myb{a,b,c,d=FOO} + } + \end{enverb} +\end{example} +\begin{example} + {Store the user's choices in a macro with the \texttt{choice-store} \type} + (this continues the |\ekvdefinekeys|-call for the set |example| from above) + After the above drinks we define a few more choices which are directly stored. + \begin{enverb} + ,choice-store choose = \exampledrink{beer,wine} + \end{enverb} + One might notice that the entire setup of the |choose| key could've been done + using only |choice-store|. +\end{example} + +\begin{function}[module=expkv-def type]{choice-enum} + \begin{syntax} + choice-enum \key\ = \meta{cs}\{\val, \ldots\} \prefixes2223 + \end{syntax} + \singlecs{foo} + This is similar to |choice-store|, the differences are: \meta{cs} should be a + count register or is initialised as such using |\newcount|; instead of the + \val\ itself being stored its position in the list of choices is stored + (zero-based). It is not possible to specify a \meta{definition} to store + something else than the numerical position inside the list. +\end{function} +\begin{example} + {Show the equivalent setup for a \texttt{choice} \type\ to mimic a + \texttt{choice-enum} \type} + The following keys |key1| and |key2| are equivalent at use time (another + example not using the |example| set of above's |\ekvdefinekeys|): + \begin{enverb}[no-store,no-tcb] + \newcount\myc + \ekvdefinekeys{choice-enum-example} + { + choice key1 = + { + protected a={\myc=0 } + ,protected b={\myc=1 } + ,protected c={\myc=2 } + } + ,choice-enum key2 = \myd{a,b,c} + } + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{unknown-choice} + \begin{syntax} + unknown-choice \key\ = \marg{definition} \prefixes2323 + \end{syntax} + By default an unknown \val\ passed to a |choice| or |bool| \type\ (and all + their variants) will throw an error. However, with this prefix you can define + an alternative action which should be executed if \key\ received an unknown + choice. In \meta{definition} you can refer to the given invalid choice with + |#1|. +\end{function} +\begin{example} + {Handle unknown choices without throwing an error with the + \texttt{unknown-choice} \type} + If a drink was chosen with |choose| that's not defined we don't want to throw + an error, but store something else instead. + \begin{enverb} + ,protected unknown-choice choose = + \def\exampledrink{something unavailable} + }% closing brace for \ekvdefinekeys + \end{enverb} +\end{example} + +\begin{function}[module=expkv-def type]{unknown code} + \begin{syntax} + unknown code = \marg{definition} \prefixes2322 + \end{syntax} + By default \expkv\ throws errors when it encounters unknown keys in a set. + With the |unknown| \type\ you can define handlers that deal with undefined + keys, instead of a \key\ name you have to specify a subtype for this, here the + subtype is |code|. + + With |unknown code| the \meta{definition} is used for unknown keys which were + provided a value (so corresponds to |\ekvdefunknown|), you can access the + unknown \key\ name with |#1| (|\detokenize|d), the given \val\ with |#2|, and + the unprocessed \key\ name with |#3| (in case you want to further expand + it).\footnotemark +\end{function}% +\footnotetext{There is some trickery involved to get this more intuitive +argument order without any performance hit if you compare this to +\cs[no-index]{ekvdefunknown} directly} + +\begin{function}[module=expkv-def type]{unknown noval} + \begin{syntax} + unknown noval = \marg{definition} \prefixes2323 + \end{syntax} + This is like |unknown code| but uses \meta{definition} for unknown keys to + which no value was passed (so corresponds to |\ekvdefunknownNoVal|). You can + access the |\detokenize|d \key\ name with |#1| and the unprocessed one with + |#2|. +\end{function} + +\begin{function}[module=expkv-def type]{unknown redirect-code} + \begin{syntax} + unknown redirect-code = \marg{set-list} \prefixes2331 + \end{syntax} + This uses a predefined action for |unknown code|. Instead of throwing an + error, it is tried to find the \meta{key} in each \meta{set} in the comma + separated \meta{set-list}. The first found match will be used and the + remaining options from the list discarded. If the \meta{key} isn't found in + any \meta{set} an expandable error will be thrown eventually. Internally + \expkv's |\ekvredirectunknown| will be used. +\end{function} + +\begin{function}[module=expkv-def type]{unknown redirect-noval} + \begin{syntax} + unknown redirect-noval = \marg{set-list} \prefixes2333 + \end{syntax} + This behaves just like |unknown redirect-code| but will set up means to + forward keys for |unknown noval|. Internally \expkv's + |\ekvredirectunknownNoVal| will be used. +\end{function} + +\begin{function}[module=expkv-def type]{unknown redirect} + \begin{syntax} + unknown redirect = \marg{set-list} \prefixes2333 + \end{syntax} + This is a short cut to apply both, |unknown redirect-code| and + |unknown redirect-noval|, as a result you might get doubled error messages, + one from each. +\end{function} + +\bigskip +Time to use all those keys defined in the different examples! +% undo the global no-tcb setting +\ekvset{enverb}{no-store,undo-no-tcb}% +\begin{enverb}[restore,below,inter=Which results in three paragraphs of text:] + \newcommand\defexample[1][] + {% + \ekvset{example}{#1}% + After walking \the\exampledistance\space we finally reached + \examplebar{\emph}{no particular place}. + There I ordered + \iffoo + a drink called \examplefoostore\space (that has + \the\examplefoocount\space tokens in it)% + \else + nothing of particular interest% + \fi + \examplebaz{ and ate \emph}. + Then a friend of mine also chose \exampledrink. + \par + } + \defexample[nofoo] + \defexample[all,choose=lemonade] + \defexample + [all=wheat beer,bar=Biergarten,baz=pretzel,choose=champagne] +\end{enverb} + +\subsection{Another Example} + +This picks up the standard use case from \autoref{sec:expkv:standard}, but +defines the keys using |\ekvdefinekeys|. + +\begin{enverb} + \makeatletter + \ekvdefinekeys{myrule} + { + store ht = \myrule@ht + ,initial ht = 1ex + ,store wd = \myrule@wd + ,initial wd = 0.1em + ,store raise = \myrule@raise + ,initial raise = \z@ + ,meta lower = {raise={-#1}} + } + \ekvsetdef\myruleset{myrule} + \newcommand*\myrule[1][] + {% + \begingroup + \myruleset{#1}% + \rule[\myrule@raise]{\myrule@wd}{\myrule@ht}% + \endgroup + } + \makeatother + a\myrule\par + a\myrule[ht=2ex,lower=.5ex]\par + \myruleset{wd=5pt} + a\myrule +\end{enverb} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/pkg-main.tex b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-main.tex new file mode 100644 index 00000000000..0109eb70dae --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-main.tex @@ -0,0 +1,1097 @@ +\genericekv\expkv{} + +This package supports two different front ends to parse a \kv\ list. The first +(|\ekvset|) is similar to \pkg{keyval}'s |\setkeys|, it parses the list and +executes defined actions based on the encountered \key{}s. The second +(|\ekvparse|) is more versatile, it only splits the list into \key{}s and +\val{}s and then runs user-provided code on the result. + +The first is described in subsections~\ref{sec:expkv:setup} +to~\ref{sec:expkv:set}, the latter is described in \autoref{sec:expkv:parse}. + +Unlike the other packages in the bundle, if you load \expkv\ as a \LaTeXe\ +package there is a single option available: +\begin{function}{all} + \begin{syntax} + \cs[no-index]{usepackage}[all]\{expkv\} + \end{syntax} + Loads all the packages of \expkvbundle. +\end{function} + +\subsection{General Parsing Rules} + +\expkv\ parses a \kv\ list by first splitting the elements on commas (active or +other), then looking for an equals sign (active or other). If there is one the +\kv\ pair will be split at the first. From both \key\ and \val\ (if there was a +\val) one set of outer spaces is stripped, and afterwards one set of outer +braces (meaning braces which are around the complete remainder after space +stripping if there are any). + +So the syntax looks something like the following pseudo-input: +\begin{syntaxexample} + \vissp{ \marg{key} = \marg{value} } +\end{syntaxexample} +with the displayed spaces and braces being optional and removed if found. Note +that if you want either \key\ or \val\ to include a comma the braces become +mandatory, the same is true if \key\ should contain an equals sign. + +\subsubsection{Expansion Control\label{sec:expkv:expansion}} + +\expkv\ provides a mechanism to specify expansions of a \key\ and/or \val. For +those familiar with \pkg{pgfkeys} this is similar to its |.expand once| or +|.expanded| handlers. This concept will be called \expnotation\ or \expansion\ +throughout this documentation. + +The syntax for this notation is a leading list of \expansion-rules followed by a +colon that is immediately followed by a space. Also the \expansion-rules must +not contain any spaces outside of braces, and the remainder on the right hand +side of the colon must not be blank, else it is not considered an \expnotation\ +but just a weirdly formed \key-name. + +The entire syntax of a \kv\ pair is +\begin{syntaxexample} + \vissp{ \{\expansion: \asciispace\marg{key} \} = \marg{value} } +\end{syntaxexample} +Note that the \expansion\ prefix is right delimited by \verb*|: | so the space +after the colon is only optional in the sense that the entire \expansion\ prefix +is optional. Else all displayed spaces and braces are optional, the inner set of +spaces and braces around \key\ only being optional if the optional \expansion\ +prefix (\expansion\verb*|: |) was present. If that part was present the list of +\expansion-rules will be executed, which might change the contents of both \key\ +and \val. For |\ekvparse| this is always true, however in |\ekvset| it is only +parsed for the \expnotation\ if there is no \key\ matching the given input (so +this notation doesn't impose a restriction on key names, though \key-names +actually containing what would otherwise be an \expansion\ prefix should be +pretty rare in practice). + +All packages in \expkvbundle\ support this notation (most of them internally use +|\ekvset| or |\ekvparse|). Please note however that while \expkvo\ fully +supports them, reinsertion via the |\r| \expansion-rule might affect the unused +global options list if used in the class options. + +An \expansion-rule consists of a single token. In a \Vkey\ they work on the +\val\ (but you can use the |\key| rule to also affect the \key\ there) while in +a \Nkey\ they work on the \key. The following rules are available (those +familiar with \pkg{expl3} will notice that the first six are identical to its +argument types): + +\begingroup +\let\bigskip\medskip +\begin{function}[module=Expansion]{o} + Expands the first token once. +\end{function} + +\begin{function}[module=Expansion]{e} + Expands the entire \val\ inside of |\expanded|. +\end{function} + +\begin{function}[module=Expansion]{c} + Builds a |\csname| from the contents. +\end{function} + +\begin{function}[module=Expansion]{f} + Expands the contents until a space or an unexpandable token is found (the + space would be removed). +\end{function} + +\begin{function}[module=Expansion]{V} + The \val\ should be a single token, either defined as a parameterless macro or + as a register (via |\newcount| etc.). This expands to the value of the + register or the macro's replacement text. If the token in \val\ has the + |\meaning| of |\relax| an error is thrown and the result is empty. +\end{function} + +\begin{function}[module=Expansion]{v} + This is a combination of |c| and |V|, meaning the \val\ is turned into a + single control sequence via |\csname|, and then expanded to its value. The + control sequence will only be built if it's defined. +\end{function} +\begin{example}{The difference between \texttt{co} and \texttt{v} expansion} + Say we want to hand the contents of a macro as the value to our key, but the + actual macro name depends on user input. For this we have two options which + behave slightly different. One is to use |v| the other is to combine the |co| + \expansion-rules. The following demonstrates both (I modified the way errors + are thrown to instead output them in red for this; you'll learn about + |\ekvparse| in a few pages, for now just stick with me): + \expkvdocPrintErrors + \makeatletter + \begin{enverb}[below] + \newcommand\mypair[2]{Arg: `\detokenize{#2}'. }% + \newcommand\myvalue{Value}% + \ekvparse\@firstofone\mypair + { + co: key = myvalue, v: key = myvalue, \par + ,co: key = myValue, v: key = myValue, \par + } + \end{enverb} + The difference is that in |co| the variable is implicitly initialised as + |\relax| by |c| if it doesn't exist and then doesn't expand in |o|. On the + other hand |v| will check whether the variable would exist and throw an error + if it doesn't (and will not set it to |\relax| by blindly using |\csname|). +\end{example} + +\begin{function}[module=Expansion]{s} + Strips one set of outer spaces and outer braces. +\end{function} + +\begin{function}[module=Expansion]{b} + Adds one set of outer braces. +\end{function} + +\begin{function}[module=Expansion]{p} + \begin{syntax} + p\marg{contents} + \end{syntax} + Places \meta{contents} before the \val. +\end{function} + +\begin{function}[module=Expansion]{P} + \begin{syntax} + P\marg{contents} + \end{syntax} + Places \meta{contents} after the \val. +\end{function} + +\begin{function}[module=Expansion]{g} + Gobbles the first token or balanced group on the left (leads to a low-level + \TeX-error if the \val\ is empty). +\end{function} + +\begin{function}[module=Expansion]{\r} + In a \Vkey\ reinserts the contents of \val\ after all the \expansion-rules + were executed (the \key-name needs to be empty). In a \Nkey\ the contents of + \key\ are reinserted after all the \expansion-rules were executed (the \val\ + needs to be empty, which is an easy to fulfil rule as there was no \val). + Normal \kv\ parsing is aborted afterwards for the current \kv\ list element. +\end{function} +\begin{example}{Parse the contents of a macro as additional \kv\ input} + Say we want to store a list of common settings in a macro, then we want to + parse a few keys, insert the contents of the macro, and parse a few more keys. + The following does exactly that (|\ekvset| is analogue to |\setkeys| of the + \pkg{keyval} package if you're familiar with it, else you'll learn about + |\ekvset| a few pages down the road so be patient): + \begin{enverb}[no-tcb] + \newcommand*\mykeylist{color=red,height=5cm} + \ekvset{mypkg}{key=value, o\r: \mykeylist, other key=other value} + \end{enverb} + You could also use the following with the same outcome, but this looks more + complicated so the other form should be preferred: + \begin{enverb}[no-tcb] + \ekvset{mypkg}{key=value, o\r: {}=\mykeylist, other key=other value} + \end{enverb} +\end{example} + +\begin{function}[module=Expansion]{\key} + \begin{syntax} + \cs[module=Expansion]{key}\marg{expansion} + \end{syntax} + This is the only supported way to change the contents of \key\ for a \Vkey\ in + the \expnotation. All the rules in \expansion\ are applied to \key\ instead of + \val. +\end{function} + +\begin{function}[module=Expansion]{R} + This is the same as if you used |V\r|. So it expects a single token, retrieves + its value, and reinserts this as additional \kv\ input. +\end{function} + +\begin{function}[module=Expansion]{r} + This is the same as if you used |v\r|. So it builds a |\csname| if that is + defined, retrieves its value, and reinserts this as additional \kv\ input. +\end{function} +\begin{example} + {Parse the contents of a macro as additional \kv\ input (revisited)} + Now that we also know the |R| and |r| rule, the example above can be input + even simpler: + \begin{enverb}[no-tcb] + \ekvset{mypkg}{key=value, R: \mykeylist, other key=other value} + \end{enverb} + or + \begin{enverb}[no-tcb] + \ekvset{mypkg}{key=value, r: mykeylist, other key=other value} + \end{enverb} +\end{example} +\endgroup + +\subsection{Setting up Keys\label{sec:expkv:setup}} + +\expkv\ provides a rather simple approach to setting up keys, similar to +\pkg{keyval}. If you're looking for a more sophisticated interface similar to +those of \pkg{l3keys} or \pkg{pgfkeys} take a look at \expkvd\ described in +\autoref{sec:expkv-def} or for a simple interface that defines expandable macros +at \expkvc\ described in \autoref{sec:expkv-cs}. + +Keys in \expkv\ (as in many other \kv\ implementations) belong to a \emph{set}, +so that different sets can contain keys of the same name. Unlike many other +implementations \expkv\ doesn't provide means to set a default value, instead we +have keys that take a value (we call those \Vkey) and keys that don't (which are +called \Nkey\ by \expkv), but both can share the same name on the user level, +the only difference for the user is whether =\val\ was used or not. + +The following macros are available to define new keys. Those macros containing +\enquote{\texttt{def}} in their name can be prefixed by anything allowed to +prefix |\def| (but \emph{don't} use |\outer|, keys defined with it won't ever be +usable). And prefixes allowed for |\let| can prefix those macros with +\enquote{\texttt{let}} in their name, accordingly. Neither \set\ nor \key\ are +allowed to be empty for new keys. \set\ will be used as is inside of +|\csname ...\endcsname| and \key\ will get |\detokenize|d. Also \set\ should not +contain an explicit |\par| token. + +\begin{function}{\ekvdef} + \begin{syntax} + \cs{ekvdef}\marg{set}\marg{key}\marg{code} + \end{syntax} + Defines a \Vkey\ in a \set\ to expand to \meta{code}. In \meta{code} you can + use |#1| to refer to the given \val. +\end{function} +\begin{example}{Define a single \Vkey} + Define |text| in |foo| to store the \val\ inside |\foo@text|: + \begin{enverb}[no-tcb] + \protected\long\ekvdef{foo}{text}{\def\foo@text{#1}} + \end{enverb} +\end{example} + +\begin{function}{\ekvdefNoVal} + \begin{syntax} + \cs{ekvdefNoVal}\marg{set}\marg{key}\marg{code} + \end{syntax} + Defines a \Nkey\ in \set\ to expand to \meta{code}. +\end{function} +\begin{example}{Define a single \Nkey} + Define |bool| in |foo| to set |\iffoo@bool| to |true|: + \begin{enverb}[no-tcb] + \protected\ekvdefNoVal{foo}{bool}{\foo@booltrue} + \end{enverb} +\end{example} + +\begin{function}{\ekvlet} + \begin{syntax} + \cs{ekvlet}\marg{set}\marg{key}\meta{cs} + \end{syntax} + Let the \Vkey\ in \set\ to \meta{cs}. There are no checks on \meta{cs} + enforced, but the code should expect the \val\ as a single braced argument + directly following it. +\end{function} +\begin{example}{Copy a macro to define a \Vkey} + Let |cmd| in |foo| do the same as |\foo@cmd|: + \begin{enverb}[no-tcb] + \ekvlet{foo}{cmd}\foo@cmd + \end{enverb} +\end{example} + +\begin{function}{\ekvletNoVal} + \begin{syntax} + \cs{ekvletNoVal} \marg{set} \marg{key} \meta{cs} + \end{syntax} + Let the \Nkey\ in \set\ to \meta{cs}. Again no checks on \meta{cs} are done. + It shouldn't expect any provided argument. +\end{function} +\begin{example}{Copy a macro to define a \Nkey} + See above. +\end{example} + +\begin{function}{\ekvletkv} + \begin{syntax} + \cs{ekvletkv}\marg{set}\marg{key}\marg{set2}\marg{key2} + \end{syntax} + Copies the definition such that \Vkey\ in \set\ behaves like \meta{key2} of + \meta{set2}. It is not checked whether that second key exists! +\end{function} +\begin{example}{Copy an existing \Vkey} + Let |B| in |bar| do the same as |A| in |foo|: + \begin{enverb}[no-tcb] + \ekvletkv{bar}{B}{foo}{A} + \end{enverb} +\end{example} + +\begin{function}{\ekvletkvNoVal} + \begin{syntax} + \cs{ekvletkvNoVal}\marg{set}\marg{key}\marg{set2}\marg{key2} + \end{syntax} + And this lets the \Nkey\ in \set\ to the definition of the |NoVal|-\meta{key2} + in \meta{set2}. Again, it is not checked whether the second key exists. +\end{function} +\begin{example}{Copy an existing \Nkey} + See above. +\end{example} + +\subsection{Handle Unknown Keys} + +By default \expkv\ throws an error message if it encounters an undefined \key. +You can change this behaviour with the macros listed here. Just like in the +section above, prefixes for |\def| are allowed if the macro has |def| in its +name, and |\let| prefixes are allowed if the macro is named something with +|let|. + +\begin{function}{\ekvdefunknown} + \begin{syntax} + \cs{ekvdefunknown}\marg{set}\marg{code} + \end{syntax} + Execute \meta{code} if an undefined \Vkey\ is encountered while parsing in + \set. You can refer to the given \val\ with |#1|, the unknown \key's name + with |#2| (will be |\detokenize|d), and to the \key's name without + |\detokenize| applied with |#3| in \meta{code} (this order is chosen for + performance reasons). + + \cs{ekvdefunknown} and \cs{ekvredirectunknown} are mutually exclusive, you + can't use both. +\end{function} +\begin{example}{Search undefined \Vkey{}s in another \set} + Also search |bar| for undefined keys of set |foo| (and use the not yet + |\detokenize|d \key's name in case the undefined key handler of |bar| needs + that): + \begin{enverb}[no-tcb] + \long\ekvdefunknown{foo}{\ekvset{bar}{{#3}={#1}}} + \end{enverb} + This example differs from using |\ekvredirectunknown{foo}{bar}| (see below) in + that also the unknown-key handler of the |bar| set will be triggered, error + messages for undefined keys will look different, and this is slower than using + \cs{ekvredirectunknown}. +\end{example} + +\begin{function}{\ekvdefunknownNoVal} + \begin{syntax} + \cs{ekvdefunknownNoVal}\marg{set}\marg{code} + \end{syntax} + With this you can let \expkv\ execute \meta{code} if an unknown \Nkey\ was + encountered. You can refer to the given \key\ with |#1| (will be + |\detokenize|d), and to the not |\detokenize|d \key's name with |#2|. + + \cs{ekvdefunknownNoVal} and \cs{ekvredirectunknownNoVal} + are mutually exclusive, you can't use both. +\end{function} +\begin{example}{Search undefined \Nkey{}s in another \set} + Adding to the above also handling of \Nkey{}s in |foo|: + \begin{enverb}[no-tcb] + \ekvdefunknownNoVal{foo}{\ekvset{bar}{{#2}}} + \end{enverb} +\end{example} + +\begin{function}{\ekvredirectunknown} + \begin{syntax} + \cs{ekvredirectunknown}\marg{set}\marg{set-list} + \end{syntax} + This is a short cut to set up a special \cs{ekvdefunknown}-rule for \set\ that + will check each set in the comma separated \meta{set-list} for an unknown + \Vkey. The resulting unknown-key handler will always be |\long| and \emph{not} + |\protected|. The first set in \meta{set-list} has highest priority, once the + \Vkey\ is found in one of the sets the remainder of the list is discarded. If + \meta{key} isn't found in any of the sets an error will be thrown eventually. + Note that the error message looks different than a normal key-not-found error, + in particular no unwanted-value message can be thrown (it will not be checked + if a \Nkey\ of the same name does exist), and the error message will contain + all sets. + + \cs{ekvdefunknown} and \cs{ekvredirectunknown} + are mutually exclusive, you can't use both. +\end{function} +\begin{example}{Search an undefined \Vkey\ in a list of other \set{}s} + For every undefined \Vkey\ in |foo| also search the sets |bar| and |baz|: + \begin{enverb}[no-tcb] + \ekvredirectunknown{foo}{bar, baz} + \end{enverb} +\end{example} + +\begin{function}{\ekvredirectunknownNoVal} + \begin{syntax} + \cs{ekvredirectunknownNoVal}\marg{set}\marg{set-list} + \end{syntax} + This behaves just like \cs{ekvredirectunknown}, it does the same but for + \Nkey{}s. Again no prefixes are supported (the result will neither be |\long| + nor |\protected|). Note that the error messages will not check whether a + missing-value error should be thrown. + + \cs{ekvdefunknownNoVal} and \cs{ekvredirectunknownNoVal} + are mutually exclusive, you can't use both. +\end{function} +\begin{example}{Search an undefined \Nkey\ in a list of other \set{}s} + See above. +\end{example} + +\begin{function}{\ekvletunknown} + \begin{syntax} + \cs{ekvletunknown}\marg{set}\meta{cs} + \end{syntax} + This lets the handler for unknown \Vkey{}s to \meta{cs}. \meta{cs} should + expect three arguments, the first will be the \val\ the second the + |\detokenize|d \key-name, the third the unprocessed \key-name. No conditions + on \meta{cs} are enforced. +\end{function} +\begin{example} + {Do the same as an already defined macro if an unknown \Vkey\ is found} + Let the set |foo| do the same as the macro |\foo@unknown| whenever an unknown + \Vkey\ is encountered: + \begin{enverb}[no-tcb] + \ekvletunknown{foo}\foo@unknown + \end{enverb} +\end{example} + +\begin{function}{\ekvletunknownNoVal} + \begin{syntax} + \cs{ekvletunknownNoVal}\marg{set}\meta{cs} + \end{syntax} + This does the same as \cs{ekvletunknown} but for \Nkey{}s. The \meta{cs} + should expect two arguments, namely the |\detokenize|d \key\ and the + unprocessed \key. +\end{function} +\begin{example}{Silently ignore unknown \Nkey{}s} + Let the set |foo| ignore unknown \Nkey{}s by gobbling the \key-name: + \begin{enverb}[no-tcb] + \ekvletunknownNoVal{foo}\@gobbletwo + \end{enverb} +\end{example} + +\subsection{Helpers in Actions} + +\begin{function}{\ekvifdefined,\ekvifdefinedNoVal} + \begin{syntax} + \cs{ekvifdefined}\marg{set}\marg{key}\marg{true}\marg{false} + \cs{ekvifdefinedNoVal}\marg{set}\marg{key}\marg{true}\marg{false} + \end{syntax} + These two macros test whether there is a \key\ in \set. It is false if either + a hash table entry doesn't exist for that key or its meaning is |\relax|. +\end{function} +\begin{example}{Check if a \Vkey\ is already defined} + Check whether the key |special| is already defined in set |foo|, if it isn't + input a file that contains more key definitions: + \begin{enverb}[no-tcb] + \ekvifdefined{foo}{special}{}{\input{foo.morekeys.tex}} + \end{enverb} +\end{example} + +\begin{function}{\ekvifdefinedset} + \begin{syntax} + \cs{ekvifdefinedset}\marg{set}\marg{true}\marg{false} + \end{syntax} + This macro tests whether \set\ is defined (which it is if at least one key was + defined for it). If it is \meta{true} will be run, else \meta{false}. +\end{function} +\begin{example}{Check if a \set\ is already defined} + Check whether the set |VeRyUnLiKeLy| is already defined, if so throw an error, + else do nothing: + \begin{enverb}[no-tcb] + \ekvifdefinedset{VeRyUnLiKeLy} + {\errmessage{VeRyUnLiKeLy already defined}}{} + \end{enverb} +\end{example} + +\begin{function}{\ekvsneak,\ekvsneakPre} + \begin{syntax} + \cs{ekvsneak}\marg{after} + \end{syntax} + Puts \meta{after} after the effects of \cs{ekvset} (without cancelling the + current \cs{ekvset} call). The first variant will put \meta{after} after any + other tokens which might have been sneaked before, while \cs{ekvsneakPre} will + put \meta{after} before other smuggled stuff. After \cs{ekvset} has parsed the + entire \kv\ list everything that has been \cs{ekvsneak}ed will be left in the + input stream. +\end{function} +\begin{example}{Execute code after \cs[no-index]{ekvset} if a \Nkey\ was used} + Define a key |secret| in the set |foo| that will sneak out + |\foo@secretly@sneaked|: + \begin{enverb}[no-tcb] + \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo@secretly@sneaked}} + \end{enverb} +\end{example} +A more elaborate usage example is shown in \autoref{sec:expkv:sneak}. + +\begin{function}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak} + \begin{syntax} + \cs{ekvbreak}\marg{after} + \end{syntax} + Gobbles the remainder of the current \cs{ekvset} call and its argument list + and inserts \meta{after}. So this can be used to break out of \cs{ekvset}. The + first variant will also gobble anything that has been sneaked out using + \cs{ekvsneak} or \cs{ekvsneakPre}, while \cs{ekvbreakPreSneak} will put + \meta{after} before anything that has been smuggled and \cs{ekvbreakPostSneak} + will put \meta{after} after the stuff that has been sneaked out. +\end{function} +\begin{example}{Stop parsing a \kv\ list if a specific \Nkey\ was used} + Define a key |abort| that will stop key parsing inside the set |foo| and + execute |\foo@aborted|, or if it got a value |\foo@aborted@with|: + \begin{enverb}[no-tcb] + \ekvdefNoVal{foo}{abort}{\ekvbreak{\foo@aborted}} + \ekvdef{foo}{abort}{\ekvbreak{\foo@aborted@with{#1}}} + \end{enverb} +\end{example} + +\begin{function}{\ekvmorekv} + \begin{syntax} + \cs{ekvmorekv}\kvarg + \end{syntax} + Adds the contents of the \kv\ list to the list processed by the current call + of |\ekvset|. +\end{function} +\begin{example}{Use one \key\ to set multiple other keys} + Define a \Nkey\ |style| that sets the keys |border|, |width|, and |height| as + a shortcut: + \begin{enverb}[no-tcb] + \ekvdefNoVal{foo}{style}{\ekvmorekv{border, width=2cm, height=1.5ex}} + \end{enverb} +\end{example} + +\begin{function}{\ekvchangeset} + \begin{syntax} + \cs{ekvchangeset}\marg{new-set} + \end{syntax} + Replaces the current \set\ with \meta{new-set}, so for the rest of the current + \cs{ekvset} call that call behaves as if it was called with + \cs{ekvset}\marg{new-set}. It is comparable to using \key|/.cd| in + \pkg{pgfkeys}. +\end{function} +\begin{example}{Change the current \set} + Define a key |cd| in set |foo| that will change to another set as specified in + the \val. If the set is undefined it'll stop the parsing and throw an error as + defined in the macro |\foo@cd@error|: + \begin{enverb}[no-tcb] + \ekvdef{foo}{cd} + {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo@cd@error}}} + \end{enverb} +\end{example} + +\subsection{Parsing Keys in Sets}\label{sec:expkv:set} + +\begin{function}{\ekvset} + \begin{syntax} + \cs{ekvset}\marg{set}\kvarg + \end{syntax} + This macro parses the \kv\ list and checks for defined \key{}s that are in + \set. Unlike the generic \cs{ekvparse} this macro uses |\detokenize| on the + \key\ before checking whether it is a defined key. + + \cs{ekvset} is nestable, and fully expandable. But it is \emph{not} alignment + safe. As a result \key\ names and \val{}s that contain an |&| must be wrapped + in braces if \cs{ekvset} is used inside an alignment (like \LaTeXe's |tabular| + environment) or alternatively you have to create a wrapper that ensurs an + alignment safe context. +\end{function} +\begin{example}{Set defined keys using \cs[no-index]{ekvset}} + Parse |key=arg, key| in set |foo|: + \begin{enverb}[no-tcb] + \ekvset{foo}{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetSneaked} + \begin{syntax} + \cs{ekvsetSneaked}\marg{set}\marg{sneak}\kvarg + \end{syntax} + This behaves like \cs{ekvset} in which \cs{ekvsneak} was immediately called. +\end{function} +\begin{example} + {Set defined keys and execute code afterwards using + \cs[no-index]{ekvsetSneaked}} + Parse |key=arg, key| in the set |foo| with |\afterwards| sneaked out: + \begin{enverb}[no-tcb] + \ekvsetSneaked{foo}{\afterwards}{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetdef} + \begin{syntax} + \cs{ekvsetdef}\meta{cs}\marg{set} + \end{syntax} + Defines the macro \meta{cs} to be a shortcut for \cs{ekvset}\marg{set}. You + can use any \TeX-prefix allowed to prefix |\def| for \cs{ekvsetdef} (so + |\long|, |\protected|, or |\global| -- don't use |\outer|). + The resulting macro is faster than but else equivalent to the idiomatic + definition:\par + |\def|\meta{cs}|#1{|\cs{ekvset}\marg{set}|{#1}}| +\end{function} +\begin{example} + {Define a setup command for a defined \set\ using \cs[no-index]{ekvsetdef}} + Define the macro |\foosetup| to parse keys in the set |foo| and use it to + parse |key=arg, key|: + \begin{enverb}[no-tcb] + \ekvsetdef\foosetup{foo} + \foosetup{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetSneakeddef} + \begin{syntax} + \cs{ekvsetSneakeddef}\meta{cs}\marg{set} + \end{syntax} + Just like \cs{ekvsetdef} this defines a shorthand macro \meta{cs}, but this + will make it a shorthand for \cs{ekvsetSneaked}, meaning \meta{cs} will take + two arguments (first the \cs{ekvsneak} argument, then the \kv\ list). Hence + the result is a faster version of:\par + |\long\def|\meta{cs}|#1#2{|\cs{ekvsetSneaked}\marg{set}|{#1}{#2}}| +\end{function} +\begin{example} + {Define a setup command that will also require code to execute after all + keys were processed using \cs[no-index]{ekvsetSneakeddef}} + Define the macro |\foothings| to parse keys in the set |foo| and accept a + sneaked argument, then use it to parse |key=arg, key| and sneak |\afterwards|: + \begin{enverb}[no-tcb] + \ekvsetSneakeddef\foothings{foo} + \foothings{\afterwards}{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetdefSneaked} + \begin{syntax} + \cs{ekvsetdefSneaked}\meta{cs}\marg{set}\marg{sneaked} + \end{syntax} + This macro behaves like \cs{ekvsetSneakeddef}, but with a fixed \meta{sneaked} + argument. So the resulting macro is faster than but else equivalent to\par + |\long\def|\meta{cs}|#1{|\cs{ekvsetSneaked}\marg{set}\marg{sneaked}|{#1}}| +\end{function} +\begin{example} + {Define a setup command that will execute codes after all keys were + processed using \cs[no-index]{ekvsetdefSneaked}} + Define the macro |\barthing| to parse keys in the set |bar| and always execute + |\afterwards| afterwards, then use it to parse |key=arg, key|: + \begin{enverb}[no-tcb] + \ekvsetdefSneaked\barthing{bar}{\afterwards} + \barthing{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvcompile} + \begin{syntax} + \cs{ekvcompile}\meta{*}\meta{cs}\meta{parameters}\marg{set}\kvarg + \end{syntax} + This macro defines \meta{cs} to be a \emph{fast} way to set the given \kv\ + list in \meta{set}. The meaning of the keys is frozen if you don't give the + optional |*| (if the star is present the stored content will be the key-macros + and later redefinitions of keys will affect them, otherwise the key-macros are + expanded once, hence the key-code is stored). This does support the unknown + key handlers set up with |\ekvdefunknown| and |\ekvdefunknownNoVal| and also + the redirection of unknown keys (the latter will not be expanded exhaustively + though, so the key-search is done on every later call of \meta{cs}). Any + prefix allowed for |\def| might prefix \cs{ekvcompile}. The list is not + entirely fixed, as you might use \meta{parameters} in a \val\ (this is not a + single token but a parameter text as you'd use it with |\def|). They can not + be part of a \key-name (the names are indeed fixed). If you need a |#| in a + \val\ you'll need to double it just as you'd do in |\def|. Internally + \cs{ekvcompile} uses |\ekvparse| and no |\ekvset| variant, because of this the + \expnotation\ is handled slightly differently; in case you're using a + \key-name that starts with something that looks like \expnotation\ you'll have + to explicitly add an empty \expansion\ prefix. +\end{function} +\begin{example} + {Compile a \kv\ list into a macro that will quickly set that list} + Define the macro |\foo| to set some keys in the set |foo|. Since one key has a + strange name we need to add an empty \expansion\ prefix. Also we'd like |\foo| + to take one parameter which is part of the \val\ of |bar| (since the list is + parsed now and not when |\foo| is used we don't need to put braces around that + value, even if at use time |#1| contains commas): + \begin{enverb}[no-tcb] + \ekvcompile\foo#1{foo} + { + bar = #1baz + ,: part-of-key: name = strange + ,NoVal + } + \end{enverb} + After this using |\foo{VAL}| will be the same as but faster than + \begin{enverb}[no-tcb] + \ekvset{foo}{bar={VALbaz},part-of-key: name=strange,NoVal} + \end{enverb} +\end{example} + +\subsection{Generic Key Parsing}\label{sec:expkv:parse} + +\begin{function}{\ekvparse} + \begin{syntax} + \cs{ekvparse}\marg{code1}\marg{code2}\kvarg + \end{syntax} + This macro parses the \kv\ list and provides \Nkey{}s to \meta{code1} as a + single argument and \Vkey{}s with their corresponding \val\ as two arguments + to \meta{code2}. + + \cs{ekvparse} is fully expandable and alignment safe, meaning that you don't + have to take any extra precautions if it is used inside an alignment context + (like \LaTeXe's |tabular| environment) and any \key\ or \val\ can contain an + |&|. \cs{ekvparse} expands in exactly two steps, the result is provided inside + |\unexpanded| (so doesn't expand further in an |\edef| or |\expanded| + context). + + \cs{ekvbreak}, \cs{ekvsneak}, \cs{ekvmorekv}, \emph{etc.}\ don't work + in \cs{ekvparse}. \cs{ekvparse} does not throw an error if multiple + unprotected equals signs are found (it just splits at the first), and doesn't + throw an error if a \key\ is empty. If something looks like \expnotation\ (has + a colon followed but not preceded by a space and with non-blank material + following it) it'll be parsed as such (which might throw errors due to + undefined \expansion-rules if that wasn't the intended input). If you for some + reason need to input a \key-name that would match that pattern you'll need to + precede it by \verb*|: | (an empty \expansion\ prefix). +\end{function} +\begin{example} + {Parse a \kv\ list and execute arbitrary code for each element using + \cs[no-index]{ekvparse}} + \begin{enverb}[no-tcb] + \ekvparse{\handlekey{S}}{\handlekeyval{S}}{foo = bar, key, baz={zzz}} + \end{enverb} + would be equivalent to + \begin{enverb}[no-tcb] + \handlekeyval{S}{foo}{bar}\handlekey{S}{key}\handlekeyval{S}{baz}{zzz} + \end{enverb} + and afterwards |\handlekey| and |\handlekeyval| would have to further handle + the keys. No such macros are contained in \expkv, but I hope you get the idea. + Because it expands in two steps and doesn't expand any further both + \begin{enverb}[no-tcb] + \expandafter\parse\expanded{\ekvparse\k\kv{foo = bar, key, baz={zzz}}} + \end{enverb} + and + \begin{enverb}[no-tcb] + \expandafter\expandafter\expandafter + \parse\ekvparse\k\kv{foo={bar}, key, baz = zzz} + \end{enverb} + expand to + \begin{enverb}[no-tcb] + \parse\kv{foo}{bar}\k{key}\kv{baz}{zzz} + \end{enverb} +\end{example} + +\subsection{Other Useful Macros} + +\begin{function}{\ekvoptarg} + \begin{syntax} + \cs{ekvoptarg}\marg{next}\marg{default} + \end{syntax} + This macro will expandably check for a following optional argument in brackets + (|[]|). After the optional argument there has to be a mandatory one (or else + this might have unwanted side effects). The code in \meta{next} should expect + two arguments (or more), namely the processed optional argument and the + mandatory one that followed it. If there was an optional argument the result + will be \meta{next}\marg{optional}\meta{mandatory} (so the optional argument + will be wrapped in braces, the mandatory argument will be untouched). If there + was no optional argument the result will be + \meta{next}\marg{default}\marg{mandatory} (so the default will be used and the + mandatory argument will be wrapped in braces after it was read once -- if it + was already wrapped it is effectively unchanged). + + \cs{ekvoptarg} expands in exactly two steps, grabs all the arguments only at + the second expansion step, and is alignment safe. It has its limitations + however. It can't tell the difference between |[| and |{[}|, so it doesn't + work if the mandatory argument is a single bracket. Also if the optional + argument should contain a nested closing bracket it has to be nested in braces + like so: |[{arg[u]ment}]| (or else the result would be |arg[u| with a trailing + |ment]|). +\end{function} +\begin{example}{Expandably search for an optional argument with a default value} + Say we have a macro that should take an optional argument defaulting to |1|, + we could program it like this: + \makeatletter + \begin{enverb} + \newcommand\foo{\ekvoptarg\@foo{1}} + \newcommand\@foo[2]{Mandatory: #2\par Optional: #1} + \foo{5}\par + \foo[4]{5}\par + \end{enverb} +\end{example} + +\begin{function}{\ekvoptargTF} + \begin{syntax} + \cs{ekvoptargTF}\marg{true}\marg{false} + \end{syntax} + This macro is similar to \cs{ekvoptarg} but will result in + \meta{true}\marg{optional}\meta{mandatory} or \meta{false}\marg{mandatory} + instead of placing a default value. + + \cs{ekvoptargTF} expands in exactly two steps, grabs all the arguments only at + the second expansion step, and is alignment safe. It has the same limitations + as \cs{ekvoptarg}. +\end{function} +\begin{example} + {Expandably search for an optional argument and behave differently if it's + found or not} + Say we have a macro that should behave differently depending on whether there + was an optional argument or not. This could be done with: + \makeatletter + \begin{enverb} + \newcommand\foo{\ekvoptargTF\foo@a\foo@b} + \newcommand\foo@a[2]{Mandatory: #2\par Optional: #1} + \newcommand\foo@b[1]{Mandatory: #1\par No optional.} + \foo{5}\par + \foo[4]{5}\par + \end{enverb} +\end{example} + +\begin{function}{\ekvcsvloop} + \begin{syntax} + \cs{ekvcsvloop}\marg{code}\marg{csv-list} + \end{syntax} + This loops over the comma separated items in \meta{csv-list} and, after + stripping spaces from either end of \meta{item} and removing at most one set + of outer braces, leaves |\unexpanded{|\meta{code}\marg{item}|}| for each list + item in the input stream. Blank elements are ignored (if you need a blank + element it should be given as \verb*|{ }|). It supports both active commas and + commas of category other. \cs{ekvcsvloop} is not alignment safe, but you could + make it so by nesting it in |\expanded| (since the braces around the argument + of |\expanded| will hide alignment characters from \TeX's parsing). +\end{function} +\begin{example} + {Loop over a comma separated list and execute arbitrary code for each + element} + The following splits a comma separated list and prints it in a typewriter font + with parentheses around each element: + \begin{enverb} + \newcommand*\myprocessor[1]{\texttt{(#1)}} + \ekvcsvloop\myprocessor{abc,def,ghi}\par + \ekvcsvloop\myprocessor{1,,2,,3,,4}\par + \end{enverb} +\end{example} + +\begin{function}{\ekverr} + \begin{syntax} + \cs{ekverr}\marg{package}\marg{message} + \end{syntax} + This macro will throw an error fully expandably.\footnotemark\ The error + length is limited to a total length of 69~characters, and since ten characters + will be added for the formatting (\verb*|! | and \verb*| Error: |) that leaves + us with a total length of \meta{package} plus \meta{message} of 59~characters. + If the message gets longer \TeX\ will only display the first 69~characters and + append |\ETC.| to the end. + + Neither \meta{package} nor \meta{message} expand any further. Also + \meta{package} must not contain an explicit |\par| token or the token + |\thanks@jfbu|. No such restriction applies to \meta{message}. + + If |^^J| is set up as the |\newlinechar| (which is the case in \LaTeXe\ but + not in \hologo{plainTeX} by default) you can use that to introduce line breaks + in your error message. However that doesn't change the message length limit. +\end{function} +\footnotetext{The used mechanism was to the best of my knowledge first +implemented by Jean-François Burnol.} +After your own error message some further text will be placed. The formatting +of that text will look good if |^^J| is the |\newlinechar|, else not so much. +That text will read: +\begin{verbatim} + ! Paragraph ended before \<an-expandable-macro> + completed due to above exception. If the error + summary is not comprehensible see the package + documentation. + I will try to recover now. If you're in inter- + active mode hit <return> at the ? prompt and I + continue hoping recovery was complete. +\end{verbatim} +Any clean up has to be done by you, |\ekverr| will expand to nothing after +throwing the error message. + +In \ConTeXt\ this macro works differently. While still being fully expandable, +it doesn't have the character count limitation and doesn't impose restrictions +on \meta{package}. It will not display the additional text and adding line +breaks is not possible. +\begin{example}{Expandably throw error messages using \cs[no-index]{ekverr}} + Say we set up a macro that takes as mandatory argument a simple equation which + must not be empty and if it's not empty it displays it and calculates the + result: + \begin{enverb}[no-tcb] + \newcommand*\mycalc[1] + {% + \the\numexpr + \if\relax\detokenize{#1}\relax + \ekverr{my}{Empty equation not allowed, leaving -2147483647}% + -2147483647% + \else + #1% + \fi + \relax + } + Using \mycalc{} wrong. + \end{enverb} + If that code gets executed the following will be the terminal output + \begin{verbatim} + Runaway argument? + ! my Error: Empty equation not allowed, leaving -2147483647 + ! Paragraph ended before \<an-expandable-macro> + completed due to above exception. If the error + summary is not comprehensible see the package + documentation. + I will try to recover now. If you're in inter- + active mode hit <return> at the ? prompt and I + continue hoping recovery was complete. + <to be read again> + \par + l.17 Using \mycalc{} + wrong. + ? + \end{verbatim} + and the output would contain + \exres{Using -2147483647 wrong} + if we continued the \TeX\ run at the prompt. +\end{example} + + +\subsection{Other Macros} + +\begin{variable}{\ekvDate,\ekvVersion} + These two macros store \expkv's date and version. +\end{variable} + +\begin{function}{\ekv@name,\ekv@name@set,\ekv@name@key} + \begin{syntax} + \cs{ekv@name}\marg{set}\marg{key} + \cs{ekv@name@set}\marg{set} + \cs{ekv@name@key}\marg{key} + \end{syntax} + The names of the macros storing the code of \Vkey{}s are stored in are built + with these macros. The name is built from two blocks, one that is formatting + the \set\ name, and on for formatting the \key\ name. To get the actual name + the argument to \cs{ekv@name@key} must be |\detokenize|d. Both blocks are put + together (with the necessary |\detokenize|) by \cs{ekv@name}. For \Nkey{}s an + additional |N| gets appended, so their name is + \cs{ekv@name}\marg{set}\marg{key}|N|. + + You can use these macros to implement additional functionality or access + key-macros outside of \expkv, but \emph{don't} change them! \expkv\ relies on + their exact definitions internally. +\end{function} +\begin{example}{Directly call key code without parsing a \kv\ list} + Execute the key-macro of the \Nkey\ named |bar| in set |foo|: + \begin{enverb}[no-tcb] + \csname\ekv@name{foo}{bar}N\endcsname + \end{enverb} +\end{example} + + +\subsection{Examples} +\subsubsection{Standard Use-Case\label{sec:expkv:standard}} + +\begin{example}{A \kv\ based replacement for \LaTeXe's \cs[no-index]{rule}} + Because I keep forgetting the correct order of \LaTeXe's |\rule| command I + want to create a \kv\ interface to it. For this I define the keys |ht| to + specify the rule's height, |wd| to specify its width, and to give a + displacement I use two keys (because who can remember whether the rule is + moved upwards or downwards?). + + First the internals storing the values are initialised + \begin{enverb}[no-tcb,store] + \makeatletter + \newcommand*\myrule@ht{1ex} + \newcommand*\myrule@wd{0.1em} + \newcommand*\myrule@raise{\z@} + \end{enverb} + then the keys are defined. We could use |\dimen| registers instead of defining + macros, but macros have the advantage that the font dependent dimensions are + evaluated at use time. + \begin{enverb}[no-tcb,store] + \protected\ekvdef{myrule}{ht}{\def\myrule@ht{#1}} + \protected\ekvdef{myrule}{wd}{\def\myrule@wd{#1}} + \protected\ekvdef{myrule}{raise}{\def\myrule@raise{#1}} + \protected\ekvdef{myrule}{lower}{\def\myrule@raise{-#1}} + \end{enverb} + We also want a way to change the initial values without outputting a rule + (since there are unexpandable keys involved it's a good idea to define this + |\protected|) + \begin{enverb}[no-tcb,store] + \protected\ekvsetdef\myruleset{myrule} + \end{enverb} + and we need an actual frontend that does the job: + \begin{enverb}[no-tcb,store] + \newcommand*\myrule[1][] + {% + \begingroup + \myruleset{#1}% + \rule[\myrule@raise]{\myrule@wd}{\myrule@ht}% + \endgroup + } + \makeatother + \end{enverb} + Now we can use it: + \begin{enverb}[same-line=0.5,restore] + a\myrule\par + a\myrule[ht=2ex,lower=.5ex]\par + \myruleset{wd=5cm} + a\myrule + \end{enverb} +\end{example} + + +\subsubsection{An Expandable \kv\ Macro Using + \cs[no-index]{ekvsneak}\label{sec:expkv:sneak}} + +\begin{example}{An expandable \kv\ macro using \cs[no-index]{ekvsneak}} + Let's set up an expandable macro that uses a \kv\ interface. The problems + we'll face for this are: + \begin{enumerate} + \item ignoring duplicate keys + \item default values for keys which weren't used + \item providing the values as the correct argument to a macro (ordered) + \end{enumerate} + + First we need to decide which \kv\ parsing macro we want to do this with, + \cs{ekvset} or \cs{ekvparse}. For this example we also want to show the usage + of \cs{ekvsneak}, hence we'll choose \cs{ekvset}. And we'll have to use + \cs{ekvset} such that it builds a parsable list for our macro internals. To + gain back control after \cs{ekvset} is done we have to put an internal of our + macro at the start of that list, so we use an internal key that uses + \cs{ekvsneakPre} after any user input. + + To ignore duplicates will be easy if the value of the key used last will be + put first in the list, so we'll use \cs{ekvsneakPre} for the real values as + well. If for some reason we wanted a key for which the first usage was the + binding one we'd use \cs{ekvsneak} for that one. + + Providing default values can be done in different ways. We'll use a simple + approach in which we'll just put the outcome of our keys if they were used + with default values before our end marker. + + Ordering the keys can be done simply by searching for a specific token for + each argument (that token acts as a flag), so our sneaked out values will + include these specific tokens acting as markers. + + Now we got an answer to each of our initial problems. Everything that's left + is deciding what our macro should actually do. For this example we'll define a + macro that calculates the sine of a number rounded to a specified precision. + The macro should also understand input in radian and degree, and we could also + decide to evaluate a different function. For the real hard part of this + (expandably calculating trigonometric functions) we'll use \pkg{xfp}. + + First we set up our keys according to our earlier considerations and set up + the user facing macro |\sine|. The end marker of the parsing list will be a + |\sine@stop| token (which we don't need to define) and we put our default + values right before it. The user macro |\sine| uses |\ekvoptargTF| to check + for the optional argument short cutting a bit if no optional argument was + found. If you'd so prefer you could use \pkg{ltcmd}'s + |\NewExpandableDocumentCommand| to expandably get an optional argument as + well. + \begin{enverb}[no-tcb,store] + \RequirePackage{xfp} + \makeatletter + \ekvdef{sine}{f}{\ekvsneakPre{\f{#1}}} + \ekvdef{sine}{round}{\ekvsneakPre{\rnd{#1}}} + \ekvdefNoVal{sine}{degree}{\ekvsneakPre{\deg{d}}} + \ekvdefNoVal{sine}{radian}{\ekvsneakPre{\deg{}}} + \ekvdefNoVal{sine}{internal}{\ekvsneakPre{\sine@rnd}} + \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}} + \newcommand*\sine@args[2] + {\ekvset{sine}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}} + \end{enverb} + + Now we need to define some internal macros to extract the value of each key's + last usage (remember that this will be the argument after the first matching + flag). For that we use one delimited macro per key. + \begin{enverb}[no-tcb,store] + \def\sine@rnd#1\rnd#2#3\sine@stop{\sine@deg#1#3\sine@stop{#2}} + \def\sine@deg#1\deg#2#3\sine@stop{\sine@f#1#3\sine@stop{#2}} + \def\sine@f#1\f#2#3\sine@stop{\sine@final{#2}} + \end{enverb} + After the macros |\sine@rnd|, |\sine@deg|, and |\sine@f| the macro + |\sine@final| will see + |\sine@final|\marg{f}\marg{degree/radian}\marg{round}\marg{num}. Now + |\sine@final| has to expandably deal with those arguments such that the + |\fpeval| macro of \pkg{xfp} gets the correct input. Luckily this part is + pretty easy after the build up we've done until now. In |\fpeval| the + trigonometric functions have names such as |sin| or |cos|, and the degree + taking alternatives just have an appended |d| (so |sind| or |cosd|). So + putting \meta{f} and \meta{degree/radian} together will form the correct + names. + \begin{enverb}[no-tcb,store] + \newcommand*\sine@final[4]{\fpeval{round(#1#2(#4),#3)}} + \makeatother + \end{enverb} + Let's give our macro a test: + \begin{enverb}[restore,inter=\def\RequirePackage#1{}] + \sine{60}\par + \sine[round=10]{60}\par + \sine[f=cos,radian]{pi}\par + \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval} + \end{enverb} +\end{example} + +Please note that setting this up a lot more user friendly is easily possible by +utilizing \expkvc\ (see \autoref{sec:expkv-cs}). diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/pkg-opt.tex b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-opt.tex new file mode 100644 index 00000000000..452fbd1c281 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-opt.tex @@ -0,0 +1,229 @@ +\latexekv\expkvo{-opt} + +\expkvo\ allows to parse \LaTeXe\ class and package options as \kv\ lists using +sets of \expkv. + +With the 2021-05-01 release of \LaTeXe\ there were some very interesting changes +to the package and class options code. It is now possible to use braces inside +the options, and we can access options without them being preprocessed. As a +result, some but not all restrictions were lifted from the possible option +usage. What will still fail is things that aren't save from an |\edef| expansion +(luckily, the \expnotation\ can be used to get around that as well). One feature +of \expkvo\ that doesn't work any more is the possibility to parse the unused +option list, because that one doesn't contain the full information any more. +\expkvo\ will fall back to v0.1 if the kernel is older than 2021-05-01. + +Another very interesting change in \LaTeXe\ was the addition of \pkg{ltkeys} and +its |\ProcessKeyOptions| with the possibility to parse future options with it +instead of getting the dreaded |Option clash| error. The idea is brilliant and +changes made in the 2022-10-22 version allow us to provide the same feature +without having to hack any kernel internals, so starting with kernel version +2022-11-01 \expkvo\ supports this as well. + +\expkvo\ shouldn't place any restrictions on the keys, historic shortcomings of +the kernel cannot be helped though, so the supported things vary with the kernel +version (see above). The one thing that \expkvo\ doesn't support, which \expkv\ +alone would, is active commas or equals signs. But there is no good reason why +any of the two should be active in the preamble. + +You can use \LaTeXe's rollback support, so to load v0.1 explicitly use: +\begin{enverb}[no-tcb] + \usepackage{expkv-opt}[=v0.1] +\end{enverb} +which will load the last version of \expkvo\ that doesn't use the raw option +lists (this shouldn't be done by a package author, but only by a user on a +single-document basis if there are some incompatibilities, which is unlikely). + + +\subsection{Macros\label{sec:o:macros}} + +\subsubsection{Option Processors\label{sec:o:processors}} + +\expkvo's behaviour if it encounters a defined or an undefined \key\ depends on +which list is being parsed and whether the current file is a class or not. Of +course in every case a defined \key's callback will be invoked but an +additional action might be executed. For this reason the rule set of every macro +will be given below the short description which list it will parse. + +During each of the processing macros the current list element (not processed in +any way) is stored within the macro |\CurrentOption|. + +\begin{function}{\ekvoProcessOptions} + \begin{syntax} + \cs{ekvoProcessOptions}\marg{set} + \end{syntax} + This runs |\ekvoProcessGlobalOptions|, then |\ekvoProcessLocalOptions|, and + finally |\ekvoProcessFutureOptions|. If you're using |\ekvoUseUnknownHandlers| + it'll affect all three option processors. Else the respective default + unknown-rules are used. +\end{function} + +\begin{function}{\ekvoProcessLocalOptions} + \begin{syntax} + \cs{ekvoProcessLocalOptions}\marg{set} + \end{syntax} + This parses the options which are directly passed to the current class or + package for an \expkv\ \set. +\end{function} +\expkvorules + {% + cd = {remove the option from the list of unused global options if the local + option list matches the option list of the main class and the unused + global options list is not empty; else \emph{nothing}} + ,cu = add the key to the list of unused global options (if the local option + list matches the option list of the main class) + ,pu = throw an error + } + +\begin{function}{\ekvoProcessGlobalOptions} + \begin{syntax} + \cs{ekvoProcessGlobalOptions}\marg{set} + \end{syntax} + In \LaTeXe\ the options given to |\documentclass| are global options. This + macro processes the global options for an \expkv\ \set. +\end{function} +\expkvorules{d = remove the option from the list of unused global options} + +\begin{function}{\ekvoProcessFutureOptions} + \begin{syntax} + \cs{ekvoProcessFutureOptions}\marg{set} + \end{syntax} + This parses the option list of every future call of the package with + |\usepackage| or similar with an \expkv\ \set, circumventing the + |Option clash| error that'd be thrown by \LaTeXe. It is only available for + kernel versions starting with 2022-11-01. It is mutually exclusive with + \LaTeXe's |\ProcessKeyOptions| (which ever comes last defines how future + options are parsed). +\end{function} +\expkvorules{u = throw an error} + +\begin{function}{\ekvoProcessOptionsList} + \begin{syntax} + \cs{ekvoProcessOptionsList}\meta{list}\marg{set} + \end{syntax} + Process the \kv\ list stored in the macro \meta{list}. +\end{function} +\expkvorules{} + + +\subsubsection{Other Macros\label{sec:o:others}} + +\begin{function}{\ekvoUseUnknownHandlers} + \begin{syntax} + \cs{ekvoUseUnknownHandlers}\meta{cs_1}\meta{cs_2}\quad{\normalfont\itshape or} + \cs{ekvoUseUnknownHandlers}* + \end{syntax} + With this macro you can change the action \expkvo\ executes if it encounters + an undefined \key\ for the next (and only the next) list processing macro. The + macro \meta{cs_1} will be called if an undefined \Nkey\ is encountered and get + one argument being the \key\ (without being |\detokenize|d). Analogous the + macro \meta{cs_2} will be called if an undefined \Vkey\ was parsed and get two + arguments, the first being the \key\ (without being |\detokenize|d) and the + second the \val. + \par + If you use the starred variant, it'll not take further arguments. In this case + the undefined handlers defined via |\ekvdefunknown| and |\ekvdefunknownNoVal| + in the parsing set get used, and if those aren't available they'll simply do + nothing. +\end{function} + +\begin{function}{\ekvoVersion,\ekvoDate} + These two macros store the version and date of the package. +\end{function} + + +\subsection{Examples} + +\begin{example}{A package using \expkvo} + Let's say we want to create a package that changes the way footnotes are + displayed in \LaTeX. For this it will essentially just redefine |\thefootnote| + and we'll call this package \pkg{ex-footnote}. First we report back which + package we are: + \begin{enverb}[no-tcb] + \ProvidesPackage{ex-footnote}[2020-02-02 v1 change footnotes] + \end{enverb} + Next we'll need to provide the options we want the package to have. + \begin{enverb}[no-tcb] + \RequirePackage{color} + \RequirePackage{expkv-opt}% also loads expkv + \ekvdef{ex-footnote}{color}{\def\exfn@color{#1}} + \ekvdef{ex-footnote}{format}{\def\exfn@format{#1}} + \end{enverb} + We can provide initial values just by defining the two macros storing the + value. + \begin{enverb}[no-tcb] + \newcommand*\exfn@color{} + \newcommand*\exfn@format{arabic} + \end{enverb} + Next we need to process the options given to the package. The package should + only obey options directly passed to it, so we're using + |\ekvoProcessLocalOptions| and |\ekvoProcessFutureOptions|: + \begin{enverb}[no-tcb] + \ekvoProcessLocalOptions {ex-footnote} + \ekvoProcessFutureOptions{ex-footnote} + \end{enverb} + Now everything that's still missing is actually changing the way footnotes + appear: + \begin{enverb}[no-tcb] + \renewcommand*\thefootnote + {% + \ifx\exfn@color\@empty + \csname\exfn@format\endcsname{footnote}% + \else + \textcolor{\exfn@color}{\csname\exfn@format\endcsname{footnote}}% + \fi + } + \end{enverb} + So the complete code of the package would look like this: + \begin{enverb}[no-tcb] + \ProvidesPackage{ex-footnote}[2020-02-02 v1 change footnotes] + + \RequirePackage{color} + \RequirePackage{expkv-opt}% also loads expkv + + \ekvdef{ex-footnote}{color}{\def\exfn@color{#1}} + \ekvdef{ex-footnote}{format}{\def\exfn@format{#1}} + \newcommand*\exfn@color{} + \newcommand*\exfn@format{arabic} + + \ekvoProcessLocalOptions {ex-footnote} + \ekvoProcessFutureOptions{ex-footnote} + + \renewcommand*\thefootnote + {% + \ifx\exfn@color\@empty + \csname\exfn@format\endcsname{footnote}% + \else + \textcolor{\exfn@color}{\csname\exfn@format\endcsname{footnote}}% + \fi + } + \end{enverb} + And it could be used with one (or thanks to |\ekvoProcessFutureOptions| all) + of the following lines: + \begin{enverb}[no-tcb] + \usepackage{ex-footnote} + \usepackage[format=fnsymbol]{ex-footnote} + \usepackage[color=green]{ex-footnote} + \usepackage[color=red,format=roman]{ex-footnote} + \end{enverb} +\end{example} + +\begin{example}{Parsing the global options} + This document was compiled with the global options + \texttt{[\detokenize\expandafter\expandafter\expandafter{\csname @raw@classoptionslist\endcsname]}} + in use. If we define the following keys + \begin{enverb}[store,no-tcb] + \ekvdef{optexample}{exfoo} + {Global option \texttt{exfoo} got \texttt{\detokenize{#1}}.\par} + \ekvdefNoVal{optexample}{exbar} + {Global option \texttt{exbar} set.\par} + \end{enverb} + we can use those options to control the result of the following: + \begin{enverb}[restore,same-line=.55] + \ekvoProcessGlobalOptions{optexample} + \end{enverb} + Please note that under normal conditions + \cs[no-index]{ekvoProcessGlobalOptions} is only useable in the preamble; this + example is only for academic purposes, you'll not be able to reproduce this + with the exact code shown above. +\end{example} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/pkg-pop.tex b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-pop.tex new file mode 100644 index 00000000000..e34d0c5d1f9 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/pkg-pop.tex @@ -0,0 +1,439 @@ +\genericekv\expkvp{-pop} + +The \expkvp\ is mainly written to lay the basis for \expkvc's and \expkvd's +key-defining front ends. Historically the two packages shared pretty similar +code. To unify this and reduce the overall code amount some auxiliary package +was originally planned, but then I realised that with little to no overhead +(apart from documentation) this can also be provided to users. Well, and then I +thought, why not make the whole thing expandable as well. And here we are. + +So what's the idea? This package provides a \textbf{p}refix \textbf{o}riented +\textbf{p}arser\footnote{Naming packages is hard, especially when the name +should fit a particular naming scheme. Big thanks to samcarter for helping +me: \url{https://topanswers.xyz/tex?q=1985}. The author apologises that there is +no \texttt{expkv-pnk}, \texttt{expkv-rok}, \texttt{expkv-jaz} or any other music +themed name in \expkvbundle.} with two kinds of prefixes. The first is called a +\prefix, of which an item can have arbitrary many, the second a \type, of which +every item has only one. To distinguish the concept of an optional \prefix\ from +the generic term \enquote{prefix} I'll use this formatting whenever the special +kind of prefix is meant. + +Another peculiarity of \expkvp\ compared to the other packages in +\expkvbundle\ is that it doesn't separate \Nkey{}s from \Vkey{}s as +strictly. Instead a |NoVal|-marker is used as the value. If this is not what you +want you can use \cs{ekvpValueAlwaysRequired} (see there). + + +\subsection{Parsing Rules} + +A parser is processing only the \key\ of a \kv\ pair. The \key\ is split at +spaces (braces might be lost by this process!). Each split off piece is checked +whether it's a defined prefix. If it's a \type\ parsing of the \key\ stops and +the remainder is considered a \meta{name}. If it's a \prefix\ it'll be recorded +and parsing goes on. If it's neither parsing is also stopped (and the last +parsed space delimited part is put back -- braces might've been lost at this +step). If a no-type rule has been defined (\cs{ekvpDefNoType}) that one is +executed else an error is thrown. + +The \prefix\ or \type\ has to match after being |\detokenize|d, whereas the +\meta{name} will be unchanged (except for stripping off the prefixes). If only a +\key\ is given (so no =\val\ used) the same is done, and instead of \val\ a +no-value marker is used (if that accidentally ends up in the input stream this +looks like this: \csname c\string_novalue\string_tl\endcsname; this is the same +as the marker used by \pkg{expl3} for those familiar with it). + +A \prefix\ has two parts to it, a \meta{pre} and a \meta{post} code, whereas a +\type\ only results in a \type-action (or the no-type action if that's defined +and no \type\ found). The parsing result can also be seen in +\autoref{fig:p:result}. + +\begin{figure}[b] + \centering + \def\plevel#1{\raisebox{1ex}{#1}}% + \def\ts#1{\textsubscript{$#1$}}% + \begingroup + \ttfamily + pre\ts1 + \plevel + {% + pre\ts2 + \plevel + {% + \ldots\space + \plevel + {% + pre\ts n + \plevel{\type-action} + post\ts n + }% + \ldots\space + }% + post\ts2 + }% + post\ts{1}% + \endgroup + \caption + {% + Structure of a single \kv\ pair's parsing result with $n$~\prefix{}es% + \label{fig:p:result}% + } +\end{figure} + +Please note that \expkvp's parsers are fully expandable as long as your +\prefixes\ and \types\ are. Additionally \expkvp\ doesn't provide means to +define parsers, \prefixes, or \types\ |\protected|. As a result, make sure +you'll always call |\ekvpParse| inside of a |\protected| macro if you need +anything that's unexpandable or else your code might not do what you intended +since some states may not be updated when expandable code tries to access them. +The macro |\ekvpProtect| can help to overcome this issue, but it's more of a +last resort than a clean solution. + +\subsection{Defining Parsers} + +\begin{function}{\ekvpNewParser} + \begin{syntax} + \cs{ekvpNewParser}\marg{parser} + \end{syntax} + Defines a new parser called \meta{parser}. Every parser has to be defined this + way. Throws an error if the parser is already defined. +\end{function} + +\begin{function}{\ekvpDefType} + \begin{syntax} + \cs{ekvpDefType}\marg{parser}\marg{type}\marg{code} + \end{syntax} + Defines a \type\ that is called \meta{type} for the parser \meta{parser}. If + the \type\ is parsed the \meta{code} will be used as a \type-action. Inside of + \meta{code} you can use |#1| to refer to the \meta{name} (the remainder of the + \key\ after stripping off all the prefixes), |#2| to use the unaltered \key, + and |#3| to access the \val\ which was given to your \key. +\end{function} + +\begin{function}{\ekvpDefPrefix} + \begin{syntax} + \cs{ekvpDefPrefix}\marg{parser}\marg{prefix}\marg{pre}\marg{post} + \end{syntax} + Defines a \prefix\ that is called \meta{prefix} for the parser \meta{parser}. + If the \prefix\ is encountered the code in \meta{pre} will be put before the + \type-action and the code in \meta{post} will be put behind it. + If multiple \prefix{}es are used the \meta{pre} of the first will be put first + and the \meta{post} of the first will be put last. Inside of \meta{pre} and + \meta{post} |#1| is replaced with the found \type, |#2| the \meta{name}, and + |#3| the unaltered \key. If no valid type was found and the no-type rule + defined with \cs{ekvpDefNoType} is executed the argument |#1| will be empty. +\end{function} + +\begin{function}{\ekvpDefAutoPrefix} + \begin{syntax} + \cs{ekvpDefAutoPrefix}\marg{parser}\marg{pre}\marg{post} + \end{syntax} + You can also define a \prefix-like rule that is executed on each element + automatically. So the \meta{pre} and \meta{post} code of this will be inserted + for every valid element of the \kv\ list. Just like for \cs{ekvpDefPrefix} you + can access the \type\ with |#1|, the \meta{name} with |#2|, and the unaltered + \key\ with |#3|. +\end{function} + +\begin{function}{\ekvpDefPrefixStore} + \begin{syntax} + \cs{ekvpDefPrefixStore}\marg{parser}\marg{prefix}\meta{cs}\marg{pre}\marg{post} + \end{syntax} + This is a shortcut to define a \prefix\ named \meta{prefix} for \meta{parser} + that'll store \meta{pre} inside of \meta{cs} (which should be a single control + sequence) before the \type-action and afterwards store \meta{post} in it. Both + definitions (in \meta{pre} and in \meta{post}) are put inside |\ekvpProtect|. +\end{function} + +\begin{function}{\ekvpDefPrefixLet} + \begin{syntax} + \cs{ekvpDefPrefixLet}\marg{parser}\marg{prefix}\meta{cs}\meta{pre}\meta{post} + \end{syntax} + This is similar to \cs{ekvpDefPrefixStore}, but instead of storing in the + \meta{cs} it'll be let to the single tokens specified by \meta{pre} and + \meta{post}. If either \meta{pre} or \meta{post} contains more than a single + token the remainder is put after the |\let| statement. Both assignments (in + \meta{pre} and in \meta{post}) are put inside |\ekvpProtect|. +\end{function} + +\begin{function}{\ekvpLet} + \begin{syntax} + \cs{ekvpLet}\marg{parser_1}\marg{type}\marg{name_1}\oarg{parser_2}\marg{name_2} + \end{syntax} + Copies the definition of a \prefix\ or \type. The \meta{type} should be one of + |prefix|, or |type|. The \prefix\ or \type\ \meta{name_1} for \meta{parser_1} + will be let equal to the \prefix\ or \type\ \meta{name_2} of \meta{parser_2}. + If you omit the optional \meta{parser_2} it will default to \meta{parser_1}. +\end{function} + + +\subsection{Changing Default Behaviours} + +\begin{function}{\ekvpValueAlwaysRequired} + \begin{syntax} + \cs{ekvpValueAlwaysRequired}\marg{parser} + \end{syntax} + By default a special no-value marker will be provided for \val\ if no value + was given to a key. If this is used instead an error will be thrown that a + value is required. +\end{function} + +\begin{function}{\ekvpDefNoValue} + \begin{syntax} + \cs{ekvpDefNoValue}\marg{parser}\marg{code} + \end{syntax} + This is a third alternative to the default behaviour and + \cs{ekvpValueAlwaysRequired}. With this macro you can stop normal parsing if + no value was specified and instead run \meta{code}. Inside of \meta{code} the + unprocessed \Nkey\ is available as |#1|. No further processing of this \kv\ + list element takes place. +\end{function} + +\begin{function}{\ekvpUseNoValueMarker} + \begin{syntax} + \cs{ekvpUseNoValueMarker}\marg{parser}\marg{marker} + \end{syntax} + This macro changes the no-value marker from the package default to + \meta{marker}. Note that macros like \cs{ekvpAssertValue} don't work with + markers different from the default one. +\end{function} + +\begin{function}{\ekvpDefNoValuePrefix} + \begin{syntax} + \cs{ekvpDefNoValuePrefix}\marg{parser}\marg{pre}\marg{post} + \end{syntax} + It is also possible to handle \Nkey{}s as if this was some special \prefix. + So if a \Nkey\ is encountered you'll have \meta{pre} and \meta{post} put + before and behind the \type-action (as the outermost \prefix). The no-value + marker will be forwarded as \val. If you want to change a parser's marker and + use this you have to use \cs{ekvpUseNoValueMarker} before calling + \cs{ekvpDefNoValuePrefix}, and you must not use \cs{ekvpDefNoValue} or + \cs{ekvpValueAlwaysRequired} before using \cs{ekvpDefNoValuePrefix} (both + result in undefined behaviour). +\end{function} + +\begin{function}{\ekvpDefNoType} + \begin{syntax} + \cs{ekvpDefNoType}\marg{parser}\marg{code} + \end{syntax} + This defines an action if no valid \type\ was found, otherwise this behaves + like \cs{ekvpDefType} with the same arguments |#1| (\meta{name}), |#2| + (unaltered \key), and |#3| (\val) in \meta{code}. If this isn't used for the + \meta{parser} instead an error will be thrown whenever no \type\ is found. +\end{function} + + +\subsection{Markers} + +\expkvp\ will place three markers for each list element that was parsed and +defines an auxiliary to gobble up to that marker. After each marker an +additional group is placed containing the current list element (excluding the +\val). The gobblers gobble that group as well. Those markers are: + +\begin{function}{\ekvpEOP,\ekvpGobbleP} + Is placed after all the \prefix{}es' \meta{pre} code, directly before the + \type-action. +\end{function} + +\begin{function}{\ekvpEOT,\ekvpGobbleT} + Is placed after the \type-action, directly before the last \prefix's + \meta{post} code. +\end{function} + +\begin{function}{\ekvpEOA,\ekvpGobbleA} + Is placed at the end of the complete result of the current element, so after + all the \prefix{}es' \meta{post} code. +\end{function} + + +\subsection{Helpers in Actions} + +\begin{function}{\ekvpIfNoVal} + \begin{syntax} + \cs{ekvpIfNoVal}\marg{arg}\marg{true}\marg{false} + \end{syntax} + This will expand to \meta{true} if the \meta{arg} is the special no-value + marker, otherwise \meta{false} is left in the input stream. +\end{function} + +\begin{function}{\ekvpAssertIf,\ekvpAssertIfNot} + \begin{syntax} + \cs{ekvpAssertIf}\oarg{marker}\marg{if}\marg{message} + \end{syntax} + This macro will run the \TeX-if test specified by \meta{if} (should expand to + any \TeX-style if, e.g., \cs[no-index]{iftrue} or + \cs[no-index]{ifx}\meta{A}\meta{B}). If the test is true everything's fine, + else an error message is thrown using \meta{message} followed by the current + element and everything up to \meta{marker} is gobbled (\meta{marker} can be + any of |EOT|, which is the default, |EOP|, or |EOA|). The |Not| variant will + invert the logic, so if the \TeX-style if is true this will throw the error. +\end{function} + +\begin{function}{\ekvpAssertTF,\ekvpAssertTFNot} + \begin{syntax} + \cs{ekvpAssertTF}\oarg{marker}\marg{if}\marg{message} + \end{syntax} + This is pretty similar to \cs{ekvpAssertIf}, but \meta{if} should be a test + that uses its first argument if it's true and its second otherwise (so an + error is thrown if the second argument is used, nothing happens otherwise). + The |Not| variant is again inversed. +\end{function} + +\begin{function}{\ekvpAssertValue,\ekvpAssertNoValue} + \begin{syntax} + \cs{ekvpAssertValue}\oarg{marker}\marg{arg} + \end{syntax} + Asserts that \meta{arg} is not the no-value marker (\cs{ekvpAssertValue}) or + is the no-value marker (\cs{ekvpAssertNoValue}), otherwise throws an error and + gobbles everything up to \meta{marker} (like \cs{ekvpAssertIf}). +\end{function} + +\begin{function}{\ekvpAssertOneValue,\ekvpAssertTwoValues} + \begin{syntax} + \cs{ekvpAssertOneValue}\oarg{marker}\marg{arg} + \end{syntax} + Asserts that \meta{arg} contains exactly one or two values (which could both + be either single tokens or braced groups -- spaces between the two values in + the \cs{ekvpAssertTwoValues} case are ignored), if the number of values + doesn't match an error is thrown and everything up to \meta{marker} gobbled. +\end{function} + +\begin{function}{\ekvpProtect} + \begin{syntax} + \cs{ekvpProtect}\marg{code} + \end{syntax} + This macro protects \meta{code} from further expanding in every context a + |\protected| macro wouldn't expand. It needs at least two steps of expansion. + When a |\protected| macro would expand this will remove the braces around + \meta{code} and \TeX\ will process \meta{code} the same way it normally would. + After the first step of expansion it'll leave two macros, and after each + further full expansion these two macros stay there. Since \expkvp\ offers no + method to define \prefixes\ or \types\ |\protected| you can instead use this + macro. But if your parser needs any assignments you should nest the + |\ekvpParse| call in a |\protected| macro anyway. +\end{function} + +\subsection{Using Parsers} + +\begin{function}{\ekvpParse} + \begin{syntax} + \cs{ekvpParse}\marg{parser}\kvarg + \end{syntax} + Parses the \kv\ list as defined for \meta{parser}. This expands in exactly two + steps, and returns inside of |\unexpanded|, so doesn't expand any further in + an |\edef| or |\expanded|. After the two steps it'll directly leave the code + as though every \prefix's \meta{pre} and \meta{post} code and the \type-action + were input directly along with the different markers. +\end{function} + +\subsection{The Boring Macros} +Just for the sake of completeness. +\begin{function}{\ekvpDate,\ekvpVersion} + Store the date and version, respectively. +\end{function} + +\subsection{Examples} +\begin{example}{Defining a parser similar to \expkvd} + \ekvset{enverb}{store,no-tcb}% + Let's define a parser that is similar to \expkvd's |\ekvdefinekeys|. First we + define a new parser named |exdef|: + \begin{enverb} + \ekvpNewParser{exdef} + \end{enverb} + We'll need to provide our prefixes, |long| and |protected|. The following + initialises them and defines their action. + \begin{enverb} + \newcommand*\exLong{} + \newcommand*\exProtected{} + \ekvpDefPrefixLet{exdef}{long}\exLong\long\empty + \ekvpDefPrefixLet{exdef}{protected}\exProtected\protected\empty + \end{enverb} + Now we define a few types, I'll go with |noval|, |store|, and |code| for + starters. We exploit the fact that |\ekvdef| and |\ekvdefNoVal| will expand + the first argument, so we can simply store the set name in a macro. + \begin{enverb} + \ekvpDefType{exdef}{code} + {% + \ekvpAssertValue{#3}% + \exProtected\exLong\ekvdef\exSetName{#1}{#3}% + } + \ekvpDefType{exdef}{noval} + {% + \ekvpAssertValue{#3}% + \ekvpAssertIfNot{\ifx\exLong\long}{`long' not accepted}% + \exProtected\ekvdefNoVal\exSetName{#1}{#3}% + } + \ekvpDefType{exdef}{store} + {% + \ekvpAssertOneValue{#3}% + \ifdefined#3\else + \let#3\empty + \fi + \protected\exLong\ekvdef\exSetName{#1}{\edef#3{\unexpanded{##1}}}% + } + \end{enverb} + Now we need a user facing macro that puts the pieces together (this uses + |\NewDocumentCommand| instead of |\newcommand| because the former defines + macros |\protected|). + \begin{enverb} + \NewDocumentCommand\exdefinekeys{m +m} + {\def\exSetName{#1}\ekvpParse{exdef}{#2}} + \end{enverb} + Now we got that sorted so we can use our little parser: + \ekvset{enverb}{no-store,undo-no-tcb}% + \begin{enverb}[restore] + \newif\ifbar + \exdefinekeys{exampleset} + { + long store foo = \myfoo + ,protected noval bar = \bartrue + ,code baz = baz was called with \detokenize{#1} + } + \ekvset{exampleset}{foo=Foobar,bar,baz=\empty} + \ifbar bar was true so: \myfoo\fi + \end{enverb} +\end{example} + +\begin{example}{Visualising the expandability of \cs[no-index]{ekvpParse}} + \ekvset{enverb}{store,no-tcb}% + With this example we want to take a closer look at the expansion of + |\ekvpParse|. So please bear with me if this doesn't make much sense + otherwise. One of the issues is that \prefixes\ are a somewhat unordered + concept, and only \types\ must come last. We could juggle with flags (an + expandable data-storage, see \autoref{sec:c:flags}) to overcome this somehow + just to define some pseudo-syntax here, but I guess that's not worth it. + Anyhow, here goes nothing.\par + First we need a parser + \begin{enverb} + \ekvpNewParser{exexp} + \end{enverb} + and a \prefix. We could define one that ensures the name starts of with + a letter. We also want each element to start a new line, which we do using an + auto prefix. + \begin{enverb} + \newcommand\ifletter{} + \long\def\ifletter#1#2\stop{\ifcat a\noexpand#1} + \ekvpDefPrefix{exexp}{letter} + {\ekvpAssertIf{\ifletter#2\stop}{not a letter}} + { (really a letter)} + \ekvpDefAutoPrefix{exexp}{\noindent}{\par} + \end{enverb} + Finally we define a \type\ and a |NoType| rule: + \begin{enverb} + \ekvpDefType{exexp}{*}{$#1\cdot#3 = \the\numexpr#1*#3\relax$} + \ekvpDefNoType{exexp}{the #3th letter is #1} + \end{enverb} + Now we want to see whether the thing is expandable: + \ekvset{enverb}{no-store,undo-no-tcb}% + \begin{enverb}[restore,below] + \raggedright + \edef\foo{\ekvpParse{exexp}{letter e = 5, * 4 = \empty3}} + 1st full expansion + \texttt{\meaning\foo}\par + \medskip + \edef\foo{\foo} + 2nd full expansion + \texttt{\meaning\foo}\par + \medskip + \foo + \end{enverb} +\end{example} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-enverb.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-enverb.tex new file mode 100644 index 00000000000..22f39cd31cc --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-enverb.tex @@ -0,0 +1,348 @@ +\RequirePackage{expkv-def} + +\ExplSyntaxOn +\cs_new_eq:NN \enverb@count \tl_count:n +\cs_set:Npx \enverb@othercr { \char_generate:nn {13} {12} } +\ExplSyntaxOff +%% key setup +\ekvdefinekeys{enverb} + { + boolTF auto-ignore = \enverb@ifautoignore + ,initial auto-ignore + ,protected code ignore = \let\enverb@ifautoignore\@secondoftwo + ,eint more-ignore = \enverb@moreignore + ,initial more-ignore = 2 + ,also eint ignore = \enverb@ignore + ,boolTF same-line = \enverb@ifsameline + ,unknown-choice same-line = + \let\enverb@ifsameline\@firstoftwo\edef\enverb@codeshare{\unexpanded{#1}} + ,initial same-line = 0.75 + ,invboolTF other-line = \enverb@ifsameline + ,nmeta below = {same-line=false, no-aboveskip, no-belowskip} + ,nmeta undo-below = {same-line=true, aboveskip, belowskip} + ,store same-line-lst = \enverb@options@lst@same + ,store same-line-tcb = \enverb@options@tcb@same + ,initial same-line-lst = {aboveskip=0pt,belowskip=0pt} + ,initial same-line-tcb = {nobeforeafter,box align=center} + ,store other-line-lst = \enverb@options@lst@other + ,store other-line-tcb = \enverb@options@tcb@other + ,meta slst = same-line-lst={#1} + ,meta stcb = same-line-tcb={#1} + ,meta olst = other-line-lst={#1} + ,meta otcb = other-line-tcb={#1} + ,store lst = \enverb@options@lst + ,store tcb = \enverb@options@tcb + ,long store bol = \enverb@bol@content + ,long store eol = \enverb@eol@content + ,einitial eol = \enverb@othercr + ,boolTF no-lst = \enverb@ifno@lst + ,also nmeta no-lst = below + ,boolTF no-tcb = \enverb@ifno@tcb + ,also nmeta no-tcb = below + ,nmeta undo-no-tcb = {no-tcb=false, undo-below} + ,store inter = \enverb@inter + ,data gappend = \enverb@append + ,dataT pre-tcb = \enverb@pretcb + ,store after = \enverb@after + ,nmeta store = gappend=\enverb@store + ,noval no-store = \let\enverb@append\@secondoftwo + ,nmeta restore = {pre-tcb=\enverb@store,after=\enverb@clear@store} + ,code aboveskip = \edef\enverb@aboveskip{\vskip\unexpanded{#1}\relax} + ,code belowskip = \edef\enverb@belowskip{\vskip\unexpanded{#1}\relax} + ,default aboveskip = \medskipamount + ,default belowskip = \medskipamount + ,initial aboveskip + ,initial belowskip + ,noval no-aboveskip = \let\enverb@aboveskip\@empty + ,noval no-belowskip = \let\enverb@belowskip\@empty + } +\protected\long\ekvsetdef\enverbsetup{enverb} +\protected\def\enverb@clear@store{\global\let\enverb@store\@empty} +\enverb@clear@store + +%% start of nested environments +\newcommand\enverb@tcb + {% + \expanded + {% + \noexpand\begin{exresult}% + [{% + \enverb@options@tcb,% + \enverb@ifsameline\enverb@options@tcb@same\enverb@options@tcb@other + }]% + }% + {\linewidth\enverb@ifsameline{-\enverb@codeshare\linewidth}{}}% + } +\newcommand\enverb@lst + {% + \expanded + {% + \noexpand\begin{lstlisting}% + [{% + \enverb@options@lst,% + \enverb@ifsameline\enverb@options@lst@same\enverb@options@lst@other + }]% + }% + } + +%% auxiliary error function +\newcommand\enverb@error[1] + {% + \GenericError + {(enverb)\@spaces\@spaces\@spaces\@spaces}% + {Environment enverb error: #1}% + {Just use it correctly!}% + {Read the sources.}% + } + +%% setup for weird category code regime +\begingroup +\lccode`\~=`\^^M +\catcode`\:=13 +\lccode`\:=`\ % <- space +\catcode`\;=13 +\lccode`\;=`\^^I % <- tab +\lowercase{\endgroup +%% code for spaces and CR +\def\enverb@body@space{}% +\def\enverb@body@tab{}% +\def\enverb@body@newline#1~% + {\enverb@ifnotend{#1}{\enverb@bol\unexpanded{#1}\enverb@eol~}}% +%% activate the category code regime of the body +\protected\def\enverb@body@setup + {% + \let\enverb@collected@body\@empty + \let\do\@makeother\dospecials + \catcode`\^^M=13 \let~\enverb@body@newline + \catcode`\ =13 \let:\enverb@body@space + \catcode`\^^I=13 \let;\enverb@body@tab + \let\enverb@bol\relax + \let\enverb@eol\relax + } +%% check for optional argument +\newcommand\enverb@search@oarg@a + {% + \ifx:\next + \ifenverb@firsteol + \else + \enverb@body@add{:}% + \fi + \let\next\enverb@search@oarg@b + \else + \ifx~\next + \ifenverb@firsteol + \enverb@firsteolfalse + \else + \enverb@body@add{~}% + \fi + \let\next\enverb@search@oarg@b + \else + \ifx[\next\@gobble]% + \let\next\enverb@oarg + \else + \ifenverb@firsteol + \let\next\enverb@body@after@begin + \else + \let\next\enverb@body + \fi + \fi + \fi + \fi + \next + } +%% start body collection +\newcommand\enverb@body + {\edef\enverb@collected@body{\iffalse}\fi\expandafter~\enverb@collected@body} +%% check the line after an oarg +\def\enverb@body@after@oarg#1~% + {\enverb@ensure@blank{#1}{closing bracket}\enverb@body} +%% check the line after the \begin statement +\def\enverb@body@after@begin#1~% + {\enverb@ensure@blank{#1}{\string\begin}\enverb@body} +} +%% quick check for empty line +\newcommand\enverb@ensure@blank[2] + {% + \expandafter\enverb@ifempty\expanded{{#1}}{}% + {% + \expanded{% + \noexpand\enverb@error + {% + Line after #2 not empty.\noexpand\MessageBreak + Contains: \detokenize\expandafter{\romannumeral`\^^@#1}% + }% + }% + }% + } +%% quick check for empty argument +\newcommand\enverb@ifempty[1] + {% + \enverb@ifempty@\enverb@ifempty@A#1\enverb@ifempty@B.\enverb@ifempty@true + \enverb@ifempty@A\enverb@ifempty@B + } +\def\enverb@ifempty@#1\enverb@ifempty@A\enverb@ifempty@B#2#3{#3} +\def\enverb@ifempty@true\enverb@ifempty@A\enverb@ifempty@B#1#2{#1} +\newcommand\enverb@gadd[2]{\xdef#1{\unexpanded\expandafter{#1#2}}} +\newcommand\enverb@body@add[1] + {% + \edef\enverb@collected@body + {\unexpanded\expandafter{\enverb@collected@body#1}}% + } +% start of environment `enverb' +\newcommand\enverb + {% + \begingroup + \def\tmp{enverb}% + \expandafter + \endgroup + \expandafter\enverb@ifnotend@setup@perhaps\expanded + {{\string{\@currenvir\string}}}% + \begingroup + \enverb@body@setup + \enverb@firsteoltrue + \let\enverb@collected@oarg\@empty + \enverb@search@oarg + } +\newif\ifenverb@firsteol +\newcommand\enverb@search@oarg{\futurelet\next\enverb@search@oarg@a} +\newcommand\enverb@search@oarg@b{\expandafter\enverb@search@oarg\@gobble} +\newcommand\enverb@oarg{\endgroup\enverb@oarg@} +\NewDocumentCommand\enverb@oarg@{O{}} + {% + \edef\enverb@collected@oarg{\unexpanded{#1}}% + \begingroup + \enverb@body@setup + \enverb@body@after@oarg + } +\def\enverb@ifnotend#1% + {% + \def\enverb@ifnotend##1% + {% + \enverb@ifnotend@ + ##1\enverb@mark\enverb@ifnotend@maybe + #1\enverb@mark\@thirdofthree + \enverb@stop + }% + \def\enverb@ifnotend@##1#1##2\enverb@mark##3##4\enverb@stop{##3{##1}{##2}}% + } +\expandafter\enverb@ifnotend\expanded{{\expandafter\@gobble\string\\end}} +\newcommand\enverb@ifnotend@maybe[2] + {\expandafter\enverb@ifnotend@perhaps\expandafter{\romannumeral`\^^@#2}{#1}} +\newcommand\enverb@ifnotend@setup@perhaps[1] + {% + \def\enverb@ifnotend@perhaps##1% + {% + \enverb@ifnotend@perhaps@\enverb@mark##1\enverb@mark\enverb@ifnotend@end + \enverb@mark#1\enverb@mark\@thirdofthree + \enverb@stop + }% + \def\enverb@ifnotend@perhaps@ + ##1\enverb@mark#1##2\enverb@mark##3##4\enverb@stop + {##3{##2}}% + } +\providecommand\@thirdofthree[3]{#3} +\outer\def\myabsolutestop{\stop} +\newcommand\enverb@ifnotend@end[3] + {% + \iffalse{\fi}% + \enverb@ensure@blank{#1}{\string\end}% + \expanded + {% + \endgroup + \enverbsetup + {\unexpanded\expandafter{\enverb@collected@oarg}}% + \noexpand\enverb@output + {\unexpanded\expandafter{\enverb@collected@body}}% + }% + {#2}% + \expandafter\end\expandafter{\@currenvir}% + } +\long\def\enverb@output#1#2% + {% + \enverb@ifautoignore + {\enverb@setup@ignore{\enverb@count{#2}+\enverb@moreignore}}% + {\enverb@setup@ignore\enverb@ignore}% + \edef\enverb@line##1\enverb@eol + {% + \noexpand\detokenize{##1}% + \noexpand\unexpanded{\unexpanded\expandafter{\enverb@eol@content}}% + }% + \edef\enverb@collected@body{#1}% + \expanded + {\unexpanded{\enverb@append\enverb@gadd\@gobble}\expandafter}% + \expandafter{\enverb@collected@body}% + \par + \enverb@aboveskip + \enverb@ifno@lst{}% + {% + \enverb@ifsameline + {\noindent\begin{minipage}[c]{\enverb@codeshare\linewidth}}% + {}% + \begingroup + \everyeof{\noexpand}% + \expandafter + \endgroup + \scantokens\expanded + {{% + \noexpand\csname enverb@lst\endcsname + \enverb@othercr + \enverb@collected@body + \string\end{lstlisting}\enverb@othercr + }}% + \enverb@ifsameline{\end{minipage}}{}% + }% + \enverb@inter + \enverb@ifno@tcb{}% + {% + \begingroup + \everyeof{\noexpand}% + \newlinechar=\endlinechar + \expandafter + \endgroup + \scantokens\expanded + {{% + \noexpand\csname enverb@tcb\endcsname + \enverb@pretcb{\unexpanded\expandafter}% + \enverb@collected@body + \noexpand\end{exresult}% + }}% + }% + \par + \enverb@belowskip + \enverb@after + \@endpetrue + } +\providecommand\@firstofnine[9]{#1} +\newcommand\enverb@setup@ignore[1] + {\expandafter\enverb@setup@ignore@\the\numexpr#1\relax;\enverb@line} +\def\enverb@setup@ignore@#1;#2% + {% + \ifnum#1>9 + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {\expandafter\enverb@setup@ignore@\the\numexpr#1-8;{\@firstofnine{#2}}}% + {% + \let\enverb@bol@gobble\@empty % just so that renewcommand doesn't go nuts + \expanded + {% + \unexpanded{\renewcommand\enverb@bol@gobble}\ifnum#1>\z@[#1]\fi + {% + \noexpand\unexpanded + {\unexpanded\expandafter{\enverb@bol@content}}% + \unexpanded{#2}% + }% + \unexpanded{\def\enverb@bol##1\enverb@eol}% + {% + \noexpand\unexpanded + {\unexpanded\expandafter{\enverb@bol@content}}% + \unexpanded{\expandafter\enverb@ifempty\expanded}{{##1}}% + {\noexpand\enverb@line}% + {\noexpand\enverb@bol@gobble}% + ##1\unexpanded{\enverb@eol}% + }% + }% + }% + } +\let\endenverb\@empty diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-examples.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-examples.tex new file mode 100644 index 00000000000..5a146d9eb6b --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-examples.tex @@ -0,0 +1,16 @@ +\makeatletter +\newcounter{example}[section] +\newcommand*\example[1] + {% + \par\smallskip + \refstepcounter{example}% + \addcontentsline{exs}{example}{#1}% + \noindent\textit{Example:} \ignorespaces + } +\newcommand*\l@example{\@dottedtocline{0}{1.5em}{2.3em}} +\newcommand\listofexamples + {% + \addsec{List of Examples}% + \@starttoc{exs}% + } +\makeatother diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-l3doctweaks.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-l3doctweaks.tex new file mode 100644 index 00000000000..47d2c744a52 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-l3doctweaks.tex @@ -0,0 +1,39 @@ +\ExplSyntaxOn +\cs_new_protected:Npn \__expkvdoc_patchfootnote:% >>= + { + \cs_set:Npx \footnote + { + \exp_not:n { \ClassError {expkvdoc} {Footnote~ in~ function} {} } + \exp_not:o { \footnote } + } + }% =<< +\AddToHook{env/function/begin}{\__expkvdoc_patchfootnote:} + +\group_begin: +\cs_set_protected:Npn \__expkvdoc_tmp:nN #1#2 + { + \__kernel_tl_set:Nx #2 { \tl_to_str:n {#1} } + \tl_replace_all:Nxn #2 { \c_underscore_str } { / } + \tl_remove_all:Nx #2 { \c_backslash_str } + \tl_put_left:Nn #2 { doc/function// } + } +\cs_if_eq:NNTF \__expkvdoc_tmp:nN \__codedoc_get_hyper_target:nN + { + \cs_gset:Npn \__codedoc_get_hyper_target:nN #1#2 + { + \__kernel_tl_set:Nx #2 { \tl_to_str:n {#1} } + \tl_replace_all:Nxn #2 { \c_underscore_str } { / } + \tl_remove_all:Nx #2 { \c_backslash_str } + \tl_put_left:Nx #2 { doc/function/ \g__expkvdoc_module_str / } + } + } + { + \ClassWarning{expkvdoc} + { + Didn't~ patch~ l3doc's~ hyper~ targets.~ You~ might~ get~ duplicate~ + labels. + } + } +\group_end: + +\ExplSyntaxOff diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-logos.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-logos.tex new file mode 100644 index 00000000000..f5c963134c8 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-logos.tex @@ -0,0 +1,107 @@ +\definecolor{ekvred}{HTML}{9F393D} +\colorlet{ekvgrey}{black!75} +\makeatletter +\newcommand*\kvstyle%>>= + {\normalfont\rmfamily\bfseries\color{ekvred}}%=<< +\newcommand*\expFormat % used for the title only >>= + {% + {% + \rmfamily + \bfseries + {\color{ekvgrey}e\kern-.05em x\kern-.05em}% + \lower.493ex\hbox{{\color{ekvgrey}P}}% + }% + }%=<< +\newcommand\expnotation% >>= + {% + \begingroup + \BeginAccSupp{ActualText=exp|notation,E=expansion-notation}% + \rmfamily + \bfseries + \color{ekvgrey}% + e\kern-.05em x\kern-.05em% + \lower.493ex\hbox{{\color{ekvgrey}P}}% + \kern-.05em:% + \kern.05em\textsc{notation}% + \EndAccSupp{}% + \endgroup + }% =<< +\newcommand\expkvLogoFormat[1]% >>= + {% + \begingroup + \rmfamily + \bfseries + {\color{ekvgrey}e\kern-.05em x\kern-.05em}% + \lower.493ex\hbox{{\color{ekvgrey}P}\kern-.1em{\color{ekvred}k}}% + \kern-.18em{\color{ekvred}v}% + \IfValueT{#1} + {% + \begingroup + \color{ekvgrey}% + \kern.05em\rule[-.1ex]{.08em}{1.2ex}% + \kern.05em\textsc{#1}% + \endgroup + }% + \endgroup + }% =<< +\NewDocumentCommand \expkvLogo { o }%>>= + {% + \mbox + {% + \BeginAccSupp{ActualText=expkv\IfValueT{#1}{-#1}}% + \ifodd\ekvdoc@insection + \href{https://gitlab.com/islandoftex/texmf/expkv-bundle}% + {\expkvLogoFormat{#1}}% + \else + \hyperref[sec:expkv\IfValueT{#1}{-#1}]{\expkvLogoFormat{#1}}% + \fi + \EndAccSupp{}% + }% + }%=<< +\newcommand*\expkv%>>= + {% + \texorpdfstring + {\expkvLogo} + {expkv}% + }%=<< +\newcommand*\expkvpkg[1]%>>= + {% + \texorpdfstring + {\expkvLogo[{#1}]}% + {expkv-#1}% + }%=<< +\newcommand*\expkvbundle% >>= + {% + \texorpdfstring + {\begingroup\chardef\ekvdoc@insection1 \expkvpkg{bundle}\endgroup}% + {\expkvpkg{bundle}}% + }% =<< +\newcommand*\expkvc{\expkvpkg{cs}} +\newcommand*\expkvd{\expkvpkg{def}} +\newcommand*\expkvo{\expkvpkg{opt}} +\newcommand*\expkvp{\expkvpkg{pop}} +\ExplSyntaxOn +\cs_new_protected:Npn \pkglogo #1%>>= + { + \texorpdfstring + { + \mbox + { + \BeginAccSupp{ActualText=#1} + \rmfamily + \str_case:nn {#1} + { + {yax} + { + Y\kern-.1em + \raise.15em\hbox{\scshape a} + \kern-.1em \lower.15em\hbox{X}% + } + } + \EndAccSupp{} + } + } + {#1} + }%=<< +\newcommand*\yax{\pkglogo{yax}} +\ExplSyntaxOff diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-lst.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-lst.tex new file mode 100644 index 00000000000..a94920bfffa --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-lst.tex @@ -0,0 +1,218 @@ +\def\mylstwd{.55em} +\lstdefinelanguage{expkv}[primitive]{TeX} + { + ,moretexcs=[2]% e-TeX + { + expanded, + numexpr, + protected, + unexpanded, + ifdefined, + detokenize, + } + ,moretexcs=[3]% plain/LaTeX/ConTeXt + { + approx, + begin,end, + cdot, + emph,empty, + item, + LaTeX, + makeatletter,makeatother,multicolumn, + newcommand,renewcommand,newdimen,newif,newcount,NewDocumentCommand, + usepackage,RequirePackage,ProvidesPackage, + raggedright, + rule, + space,stop, + smallskip,medskip,bigskip, + TeX, + textit,texttt, + usepackage, + usemodule, + @gobble,@gobbletwo,@firstofone,@empty, + ExplSyntaxOn,ExplSyntaxOff, + } + ,moretexcs=[4]% used packages + { + % xfp + fpeval, + % keyval + setkeys, + % kvsetkeys + kvsetkeys, + % color + textcolor, + % yax + defactiveparameter,storevalue,setparameterlist, + % graphics + includegraphics, + } + ,moretexcs=[5]% expkv macros + { + ekvdef,ekvdefNoVal,% + ekvlet,ekvletNoVal,ekvletkv,ekvletkvNoVal,% + ekvdefunknown,ekvdefunknownNoVal,% + ekvletunknown,ekvletunknownNoVal,% + ekvredirectunknown,ekvredirectunknownNoVal,% + ekvset,ekvsetSneaked,% + ekvsetdef,ekvsetSneakeddef,ekvsetdefSneaked,% + ekvcompile,% + ekvparse,% + ekvVersion,ekvDate,% + ekvifdefined,ekvifdefinedNoVal,ekvifdefinedset,% + ekvbreak,ekvbreakPreSneak,ekvbreakPostSneak,% + ekvsneak,ekvsneakPre,% + ekvchangeset,% + ekvmorekv,% + ekvoptarg,ekvoptargTF,% + ekvcsvloop,% + ekverr,% + ekv@name,% + } + ,moretexcs=[6]% expkv-cs macros + { + ekvcSecondaryKeys,% + ekvcSplit,ekvcSplitAndForward,ekvcSplitAndUse,% + ekvcHash,ekvcHashAndForward,ekvcHashAndUse,% + ekvcValue,ekvcValueFast,ekvcValueSplit,ekvcValueSplitFast,% + ekvcChange,% + ekvcPass,% + } + ,moretexcs=[7]% expkv-def macros + { + ekvdefinekeys + } + ,moretexcs=[8]% expkv-pop macros + { + ekvpNewParser, + ekvpDefType, + ekvpDefPrefix,ekvpDefAutoPrefix,ekvpDefPrefixStore,ekvpDefPrefixLet, + ekvpLet, + ekvpValueAlwaysRequired,ekvpDefNoValue,ekvpUseNoValueMarker, + ekvpDefNoValuePrefix,ekvpDefNoType,ekvpDefNoValue, + ekvpEOP,ekvpGobbleP,ekvpEOT,ekvpGobbleT,ekvpEOA,ekvpGobbleA, + ekvpIfNoVal, + ekvpAssertIf,ekvpAssertIfNot,ekvpAssertTF,ekvpAssertTFNot, + ekvpAssertValue,ekvpAssertNoValue, + ekvpAssertOneValue,ekvpAssertTwoValues, + ekvpParse, + } + ,moretexcs=[9]% expkv-opt macros + { + ekvoProcessOptions,% + ekvoProcessLocalOptions,ekvoProcessGlobalOptions,% + ekvoProcessFutureOptions,ekvoProcessOptionsList,% + ekvoUseUnknownHandlers,% + ekvoVersion,ekvoDate,% + } + ,alsoletter={_:} + ,moretexcs=[10]% expl3 + { + cs_new_eq:NN, + tl_count_tokens:n, + } + } +\colorlet{codeparam}{cyan!65!black} +\lstdefinestyle{expkv} + { + texcsstyle=*[5]{\bfseries\color{ekvred}} % expkv + ,texcsstyle=*[6]{\color{ekvred}} % expkv-cs + ,texcsstyle=*[7]{\color{ekvred}} % expkv-def + ,texcsstyle=*[8]{\color{ekvred}} % expkv-pop + ,texcsstyle=*[9]{\color{ekvred}} % expkv-opt + } +\lstdefinestyle{expkv-cs} + { + texcsstyle=*[5]{\color{ekvred}} % expkv + ,texcsstyle=*[6]{\bfseries\color{ekvred}} % expkv-cs + ,texcsstyle=*[7]{\color{ekvred}} % expkv-def + ,texcsstyle=*[8]{\color{ekvred}} % expkv-pop + ,texcsstyle=*[9]{\color{ekvred}} % expkv-opt + } +\lstdefinestyle{expkv-def} + { + texcsstyle=*[5]{\color{ekvred}} % expkv + ,texcsstyle=*[6]{\color{ekvred}} % expkv-cs + ,texcsstyle=*[7]{\bfseries\color{ekvred}} % expkv-def + ,texcsstyle=*[8]{\color{ekvred}} % expkv-pop + ,texcsstyle=*[9]{\color{ekvred}} % expkv-opt + } +\lstdefinestyle{expkv-pop} + { + texcsstyle=*[5]{\color{ekvred}} % expkv + ,texcsstyle=*[6]{\color{ekvred}} % expkv-cs + ,texcsstyle=*[7]{\color{ekvred}} % expkv-def + ,texcsstyle=*[8]{\bfseries\color{ekvred}} % expkv-pop + ,texcsstyle=*[9]{\color{ekvred}} % expkv-opt + } +\lstdefinestyle{expkv-opt} + { + texcsstyle=*[5]{\color{ekvred}} % expkv + ,texcsstyle=*[6]{\color{ekvred}} % expkv-cs + ,texcsstyle=*[7]{\color{ekvred}} % expkv-def + ,texcsstyle=*[8]{\color{ekvred}} % expkv-pop + ,texcsstyle=*[9]{\bfseries\color{ekvred}} % expkv-opt + } +\lstdefinestyle{expkv-all} + { + texcsstyle=*[5]{\bfseries\color{ekvred}} % expkv + ,texcsstyle=*[6]{\bfseries\color{ekvred}} % expkv-cs + ,texcsstyle=*[7]{\bfseries\color{ekvred}} % expkv-def + ,texcsstyle=*[8]{\bfseries\color{ekvred}} % expkv-pop + ,texcsstyle=*[9]{\bfseries\color{ekvred}} % expkv-opt + } +\lstset + { + ,language=expkv + ,style=expkv + ,flexiblecolumns=false + ,basewidth=\mylstwd + %,basicstyle=\fontfamily{lmtt}\selectfont + %,basicstyle=\fontfamily{GoMono-TLF}\selectfont%\itshape + %,basicstyle=\fontfamily{jkp}\selectfont%\itshape + %,basicstyle=\fontfamily{jkptt}\selectfont%\itshape + ,basicstyle=\fontfamily{txtt}\selectfont%\itshape + ,texcsstyle=*[1]{\bfseries\color{ekvgrey}} % primitives + ,texcsstyle=*[2]{\bfseries\color{ekvgrey}} % e-TeX + ,texcsstyle=*[3]{\bfseries\color{ekvgrey}} % plain/LaTeX macros + ,texcsstyle=*[4]{\bfseries\color{ekvgrey}} % macros of other packages + ,texcsstyle=*[10]{\bfseries\color{ekvgrey}} % macros of expl3 + ,commentstyle=\color[gray]{0.4} + ,literate= + {\{} {{\CodeUpBf\{}}{1} + {\}} {{\CodeUpBf\}}}{1} + {$} {{\CodeUpBf\$}}{1} + {[} {{\CodeUp[}}{1} + {]} {{\CodeUp]}}{1} + {(} {{\CodeUp(}}{1} + {)} {{\CodeUp)}}{1} + {*} {{$*$}}{1} + {1} {{\CodeUp{1}}}{1} + {2} {{\CodeUp{2}}}{1} + {3} {{\CodeUp{3}}}{1} + {4} {{\CodeUp{4}}}{1} + {5} {{\CodeUp{5}}}{1} + {6} {{\CodeUp{6}}}{1} + {7} {{\CodeUp{7}}}{1} + {8} {{\CodeUp{8}}}{1} + {9} {{\CodeUp{9}}}{1} + {0} {{\CodeUp{0}}}{1} + {##} {{\CodeColored{codeparam}{1}{\#}}}{1} + {##1}{{\CodeColored{codeparam}{2}{\#1}}}{2} + {##2}{{\CodeColored{codeparam}{2}{\#2}}}{2} + {##3}{{\CodeColored{codeparam}{2}{\#3}}}{2} + {##4}{{\CodeColored{codeparam}{2}{\#4}}}{2} + {##5}{{\CodeColored{codeparam}{2}{\#5}}}{2} + {##6}{{\CodeColored{codeparam}{2}{\#6}}}{2} + {##7}{{\CodeColored{codeparam}{2}{\#7}}}{2} + {##8}{{\CodeColored{codeparam}{2}{\#8}}}{2} + {##9}{{\CodeColored{codeparam}{2}{\#9}}}{2} + {<key>}{{$\langle$}key{$\rangle$}}{5} + {<set>}{{$\langle$}set{$\rangle$}}{5} + {<set1>}{{$\langle$}set1{$\rangle$}}{6} + {<set2>}{{$\langle$}set2{$\rangle$}}{6} + %,literate=*{<key>}{\key}{4}{<set>}{\set}{4} + } +\newcommand*\CodeColored[3]{\textcolor{#1}{\makebox[\dimexpr\mylstwd*#2]{#3}}} +\newcommand*\CodeUpBf[1]{\makebox[\mylstwd]{\textup{\textbf{#1}}}} +\newcommand*\CodeUp[1]{\makebox[\mylstwd]{\textup{#1}}} diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-noidx.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-noidx.tex new file mode 100644 index 00000000000..410db834839 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-noidx.tex @@ -0,0 +1,50 @@ +\DoNotIndex{\-,\:,\\,\~} +\DoNotIndex{\advance,\AtEndOfPackage} +\DoNotIndex{\ClassError,\CurrentOption} +\DoNotIndex{\DeclareCurrentRelease,\DeclareOption,\DeclareRelease} +\DoNotIndex{\directlua,\dimexpr} +\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} +\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long} +\DoNotIndex{\gdef,\global,\glueexpr,\gtoksapp,\gtokspre} +\DoNotIndex{\hbox} +\DoNotIndex{\IfFormatAtLeastTF} +\DoNotIndex{\key} +\DoNotIndex{\lccode,\lowercase} +\DoNotIndex{\MessageBreak} +\DoNotIndex{\N,\newcommand,\newcount,\newtoks,\number,\numexpr} +\DoNotIndex{\PackageError,\PackageWarning,\ProcessOptions} +\DoNotIndex{\requestedLaTeXdate,\RequirePackage} +\DoNotIndex{\setbox} +\DoNotIndex{\@classoptionslist,\@clsextension,\@currext,\@currname,\@empty} +\DoNotIndex{\@firstofone,\@firstoftwo,\@gobble,\@gobblethree,\@gobbletwo} +\DoNotIndex{\@ifstar,\@onlypreamble,\@raw@classoptionslist,\@secondoftwo} +\DoNotIndex{\@unprocessedoptions,\@unusedoptionlist} +\DoNotIndex{\pkgcls@parse@date@arg,\pkgcls@targetdate,\pkgcls@targetlabel} +\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space} +\DoNotIndex{\^,\@,\unexpanded,\string,\expanded,\detokenize,\meaning} +\DoNotIndex{\lastnamedcs,\romannumeral,\z@} +\DoNotIndex{\noexpand,\normalexpanded,\normalunexpanded} +\DoNotIndex{\par,\protect} +\DoNotIndex{\the,\toksapp,\tokspre} +\DoNotIndex{\unprotect,\unless,\usemodule} +\DoNotIndex{\V} +\DoNotIndex{\writestatus} +\DoNotIndex{\xdef} +\DoNotIndex{\Z,\z} +\DoNotIndex{\ifcsname} +\DoNotIndex{\if} +\DoNotIndex{\ifdefined} +\DoNotIndex{\ifodd} +\DoNotIndex{\iftrue} +\DoNotIndex{\ifx} +\DoNotIndex{\iffalse} +\DoNotIndex{\ifnum} +\DoNotIndex{\else} +\DoNotIndex{\fi} +\@gobble\fi % ignoring \ifx etc., but only one \fi +\@gobble\fi % ignoring \ifx etc., but only one \fi +\@gobble\fi % ignoring \ifx etc., but only one \fi +\@gobble\fi % ignoring \ifx etc., but only one \fi +\@gobble\fi % ignoring \ifx etc., but only one \fi +\@gobble\fi % ignoring \ifx etc., but only one \fi +\@gobble\fi % ignoring \ifx etc., but only one \fi diff --git a/Master/texmf-dist/doc/latex/expkv-bundle/preamble-prefixes.tex b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-prefixes.tex new file mode 100644 index 00000000000..a961fc3f244 --- /dev/null +++ b/Master/texmf-dist/doc/latex/expkv-bundle/preamble-prefixes.tex @@ -0,0 +1,36 @@ +\AddToHook{env/syntax/begin}{\let\prefixes\prefixline} +\newcount\prefixes@num +\newcommand\enfprefix{\textcolor{black}} +\newcommand\allprefix{\textcolor{gray}} +\newcommand\notprefix{\textcolor{red!80!black}} +\@ifdefinable\prefixline + {\protected\def\prefixline{\hfill\afterassignment\prefixes@\prefixes@num=}} +\ExplSyntaxOn +\newcommand\prefixes@place[1] + {% + \numexpr + \int_mod:nn { \int_div_truncate:nn \prefixes@num {#1} } { 10 } + \relax + } +\ExplSyntaxOff +\newcommand\printprefix[2] + {% + \ifcase\prefixes@place{#1}\or + \enfprefix{#2}\or + \allprefix{#2}\or + \notprefix{#2}\fi + } +\protected\def\prefixes@ + {% + \ifnum\prefixes@num>10 + \printprefix{1000}{new} + \printprefix {100}{also} + \printprefix {10}{protected} + \printprefix {1}{long}% + \else + \ifcase\prefixes@num\or + \enfprefix{long}\or + \allprefix{long}\or + \notprefix{long}\fi + \fi + } |