diff options
author | Norbert Preining <norbert@preining.info> | 2023-01-24 03:02:57 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2023-01-24 03:02:57 +0000 |
commit | b06c6695162b493aeec3b594c6beb518c15c98ba (patch) | |
tree | 0c1dc3cfc73ea789d6bdc4673d776f3ef652d6d8 /macros/generic | |
parent | 4451a2090261307c963209bc65fea9fde4eceeea (diff) |
CTAN sync 202301240302
Diffstat (limited to 'macros/generic')
37 files changed, 12163 insertions, 10057 deletions
diff --git a/macros/generic/expkv-bundle/README.md b/macros/generic/expkv-bundle/README.md new file mode 100644 index 0000000000..8d484d62d4 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/comparison.tex b/macros/generic/expkv-bundle/comparison.tex new file mode 100644 index 0000000000..7736f0a4f1 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/expkv-bundle.ins b/macros/generic/expkv-bundle/expkv-bundle.ins new file mode 100644 index 0000000000..648b1b2871 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-bundle.ins @@ -0,0 +1,101 @@ +\iffalse meta-comment + +Project: 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 file is part of the "expkv bundle" (The Work in LPPL) and all files in +that bundle must be distributed together. + +The released version of this bundle is available from CTAN. + +----------------------------------------------------------------------------- + +The development version of the bundle can be found at + + https://gitlab.com/islandoftex/texmf/expkv-bundle + +for those people who are interested. + +----------------------------------------------------------------------------- + +\fi + +\input l3docstrip.tex +\askforoverwritefalse +\preamble + +---------------------------------------------------------------- +expkv-bundle -- an expandable key=val implementation and friends +E-mail: jspratte@yahoo.de +Released under the LaTeX Project Public License v1.3c or later +See http://www.latex-project.org/lppl.txt +---------------------------------------------------------------- + +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. + +This work consists of the files expkv-bundle.ins + expkv.dtx + expkv-cs.dtx + expkv-def.dtx + expkv-opt.dtx + expkv-pop.dtx +and the derived files expkv.sty + expkv.tex + t-expkv.tex + expkv-cs.sty + expkv-cs.tex + t-expkv-cs.tex + expkv-def.sty + expkv-def.tex + t-expkv-def.tex + expkv-opt.sty + expkv-pop.sty + expkv-pop.tex + t-expkv-pop.tex + +as well as any documentation files shipped with it. + +\endpreamble +\postamble +\endpostamble + +\keepsilent + +\ifdefined\expkvDocNoGenerate\endinput\fi + +\generate + {% + \file {expkv.sty} {\from{expkv.dtx} {pkg}}% + \file {expkv.tex} {\from{expkv.dtx} {tex}}% + \file{t-expkv.tex} {\from{expkv.dtx} {ctx}}% + \file {expkv-cs.sty} {\from{expkv-cs.dtx} {pkg}}% + \file {expkv-cs.tex} {\from{expkv-cs.dtx} {tex}}% + \file{t-expkv-cs.tex} {\from{expkv-cs.dtx} {ctx}}% + \file {expkv-def.sty}{\from{expkv-def.dtx}{pkg}}% + \file {expkv-def.tex}{\from{expkv-def.dtx}{tex}}% + \file{t-expkv-def.tex}{\from{expkv-def.dtx}{ctx}}% + \file {expkv-pop.sty}{\from{expkv-pop.dtx}{pkg}}% + \file {expkv-pop.tex}{\from{expkv-pop.dtx}{tex}}% + \file{t-expkv-pop.tex}{\from{expkv-pop.dtx}{ctx}}% + \file {expkv-opt.sty}{\from{expkv-opt.dtx}{pkg}}% + } + +\endbatchfile diff --git a/macros/generic/expkv-bundle/expkv-bundle.pdf b/macros/generic/expkv-bundle/expkv-bundle.pdf Binary files differnew file mode 100644 index 0000000000..1d7023208a --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-bundle.pdf diff --git a/macros/generic/expkv-bundle/expkv-bundle.tex b/macros/generic/expkv-bundle/expkv-bundle.tex new file mode 100644 index 0000000000..f8110ea36a --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/expkv-cs.dtx b/macros/generic/expkv-bundle/expkv-cs.dtx new file mode 100644 index 0000000000..4cf7946a87 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-cs.dtx @@ -0,0 +1,1748 @@ +% \iffalse meta-comment +% +% File: expkv-cs.dtx 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 +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\expkvDocNoGenerate{} +\input expkv-bundle.ins +\generate{\file{expkv-cs.sty}{\from{expkv-cs.dtx}{pkg}}} +\generate{\file{expkv-cs.tex}{\from{expkv-cs.dtx}{tex}}} +\generate{\file{t-expkv-cs.tex}{\from{expkv-cs.dtx}{ctx}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkvc} +%^^A the LaTeX package >>= +% \subsection{The \LaTeX\ Package} +% Just like for \expkv\ we provide a small \LaTeX\ package that sets up things +% such that we behave nicely on \LaTeX\ packages and files system. It'll +% |\input| the generic code which implements the functionality. +% \gobbledocstriptag +%<*pkg> +% \begin{macrocode} +\RequirePackage{expkv-pop} +\def\ekvc@tmp + {% + \ProvidesFile{expkv-cs.tex}% + [% + \ekvcDate\space v\ekvcVersion\space + define expandable key=val macros using expkv% + ]% + } +\input{expkv-cs.tex} +\ProvidesPackage{expkv-cs}% + [% + \ekvcDate\space v\ekvcVersion\space + define expandable key=val macros using expkv% + ] +% \end{macrocode} +% \gobbledocstriptag +%</pkg> +%^^A=<< +%^^A the ConTeXt module >>= +% \subsection{The \ConTeXt\ module} +% \gobbledocstriptag +%<*ctx> +% \begin{macrocode} +\writestatus{loading}{ConTeXt User Module / expkv-cs} +\usemodule[expkv-pop] +\unprotect +\input expkv-cs.tex +\writestatus{loading} + {ConTeXt User Module / expkv-cs / Version \ekvcVersion\space loaded} +\protect\endinput +% \end{macrocode} +% \gobbledocstriptag +%</ctx> +%^^A=<< +%^^A main file >>= +% \subsection{The Generic Code} +% The rest of this implementation will be the generic code. +% \gobbledocstriptag +%<*tex> +% +% Load \expkv\ if the package didn't already do so -- since \expkv\ has +% safeguards against being loaded twice this does no harm and the overhead +% isn't that big. Also we reuse some of the internals of \expkv\ to save us from +% retyping them. +% \begin{macrocode} +\input expkv-pop +% \end{macrocode} +% +% We make sure that \file{expkv-cs.tex} is only input once: +% \begin{macrocode} +\expandafter\ifx\csname ekvcVersion\endcsname\relax +\else + \expandafter\endinput +\fi +% \end{macrocode} +% +% \begin{macro}{\ekvcVersion,\ekvcDate} +% We're on our first input, so lets store the version and date in a macro. +% \begin{macrocode} +\def\ekvcVersion{1.3} +\def\ekvcDate{2023-01-23} +% \end{macrocode} +% \end{macro} +% +% If the \LaTeX\ format is loaded we want to be a good file and report back who +% we are, for this the package will have defined |\ekvc@tmp| to use +% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. +% \begin{macrocode} +\csname ekvc@tmp\endcsname +% \end{macrocode} +% +% Store the category code of |@| to later be able to reset it and change it to +% 11 for now. +% \begin{macrocode} +\expandafter\chardef\csname ekvc@tmp\endcsname=\catcode`\@ +\catcode`\@=11 +% \end{macrocode} +% |\ekvc@tmp| will be reused later, but we don't need it to ever store +% information long-term after \expkvc\ was initialized. +% +% \begin{macro}[internal]{\ekvc@tripledots} +% This macro just serves as a marker for a comparison to allow the syntax +% for the unknown key handlers. +% \begin{macrocode} +\edef\ekvc@tripledots{\detokenize{...}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@keycount} +% We'll need to keep count how many keys must be defined for each macro in the +% |split| variants. +% \begin{macrocode} +\newcount\ekvc@keycount +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@long,\ekvc@any@long} +% Some macros will have to be defined long. These two will be let to |\long| +% when this should be the case. +% \begin{macrocode} +\let\ekvc@long\ekv@empty +\let\ekvc@any@long\ekv@empty +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@ifdefined} +% We want to test whether a macro is already defined. This test checks for a +% defined macro that isn't |\relax|. +% \begin{macrocode} +\long\def\ekvc@ifdefined#1% + {% + \ifdefined#1% + \ifx\relax#1% + \ekv@fi@gobble + \fi + \@firstofone + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@ekvset@pre@expander, +% \ekvc@ekvset@pre@expander@a, +% \ekvc@ekvset@pre@expander@b +% } +% This macro expands |\ekvset| twice so that the first two steps of expansion +% don't have to be made every time the \expkvc\ macros are used. We have to do +% a little magic trick to get the macro parameter |#1| for the macro +% definition this is used in, even though we're calling |\unexpanded|. We do +% that by splitting the expanded |\ekvset| at some marks and place |##1| in +% between. At this spot we also add |\ekv@alignsafe| and |\ekv@endalignsafe| +% to ensure that macros created with \expkvc\ are alignment safe. +% \begin{macrocode} +\def\ekvc@ekvset@pre@expander#1% + {% + \expandafter\ekvc@ekvset@pre@expander@a\ekvset{#1}\ekvc@stop\ekvc@stop + } +\def\ekvc@ekvset@pre@expander@a + {% + \expandafter\ekvc@ekvset@pre@expander@b + } +\def\ekvc@ekvset@pre@expander@b#1\ekvc@stop#2\ekvc@stop + {% + \ekv@unexpanded\expandafter{\ekv@alignsafe}% + \ekv@unexpanded{#1}##1\ekv@unexpanded{#2}% + \ekv@unexpanded\expandafter{\ekv@endalignsafe}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcSplitAndUse} +% The first user macro we want to set up can be reused for +% \cs[no-index]{ekvcSplitAndForward} and \cs[no-index]{ekvcSplit}. We'll split +% this one up so that the test whether the macro is already defined doesn't +% run twice. +% \begin{macrocode} +\protected\long\def\ekvcSplitAndUse#1#2% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcSplitAndUse@#1{}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvcSplitAndUse@} +% The actual macro setting up things. We need to set some variables, forward +% the key list to |\ekvc@SetupSplitKeys|, and afterwards define the front +% facing macro to call |\ekvset| and put the initials and the argument sorting +% macro behind it. The internals |\ekvc@any@long|, |\ekvc@initials| and +% |\ekvc@keycount| will be set correctly by |\ekvc@SetupSplitKeys|. +% \begin{macrocode} +\protected\long\def\ekvcSplitAndUse@#1#2#3% + {% + \edef\ekvc@set{\string#1}% + \ekvc@SetupSplitKeys{#3}% + \ekvc@helpers@needed + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded\expandafter + {\csname ekvc@split@\the\ekvc@keycount\endcsname}% + \ekv@unexpanded\expandafter{\ekvc@initials{}#2}% + }% + }% + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded{#2}% + \ekv@unexpanded\expandafter{\ekvc@initials}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\ekvcSplitAndForward} +% This just reuses |\ekvcSplitAndUse@| with a non-empty second argument, +% resulting in that argument to be called after the splitting. +% \begin{macrocode} +\protected\long\def\ekvcSplitAndForward#1#2#3% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcSplitAndUse@#1{{#2}}{#3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcSplit} +% The first half is just |\ekvcSplitAndForward| then we define the macro to +% which the parsed key list is forwarded. There we need to allow for up to +% nine arguments. +% \begin{macrocode} +\protected\long\def\ekvcSplit#1#2#3% + {% + \let\ekvc@helpers@needed\@secondoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {% + \expandafter + \ekvcSplitAndUse@\expandafter#1\csname ekvc@\string#1\endcsname{#2}% + \ifnum\ekvc@keycount<1 + \ekvc@any@long\expandafter\def\csname ekvc@\string#1\endcsname{#3}% + \else + \ifnum\ekvc@keycount>9 + \ekvc@err@toomany{#1}% + \let#1\ekvc@undefined + \else + \ekvcSplit@build@argspec + \ekvc@any@long\expandafter + \def\csname ekvc@\string#1\expandafter\endcsname\ekvc@tmp{#3}% + \fi + \fi + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvcSplit@build@argspec,\ekvcSplit@build@argspec@} +% \begin{macrocode} +\protected\def\ekvcSplit@build@argspec + {% + \begingroup + \edef\ekvc@tmp + {\endgroup\def\ekv@unexpanded{\ekvc@tmp}{\ekvcSplit@build@argspec@{1}}}% + \ekvc@tmp + } +\def\ekvcSplit@build@argspec@#1% + {% + \ifnum#1>\ekvc@keycount + \ekv@fi@gobble + \fi + \@firstofone + {% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@#1\endcsname####}#1% + \expandafter\ekvcSplit@build@argspec@\expandafter{\the\numexpr#1+1}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@SetupSplitKeys, \ekvc@SetupSplitKeys@a, \ekvc@SetupSplitKeys@b, +% \ekvc@SetupSplitKeys@unknown +% } +% These macros parse the list of keys and set up the key macros. First we need +% to initialise some macros and start an \expkvp\ parser. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys + {% + \ekvc@keycount=\ekv@zero + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty + \ekvpParse@unsafe\ekvp@@p@ekvc@setup@splitkeys + } +% \end{macrocode} +% We're calling a parser here without the safety nets testing for parser +% existence, so better define it now. +% \begin{macrocode} +\ekvpNewParser{ekvc@setup@splitkeys} +\ekvpDefType{ekvc@setup@splitkeys}{short} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupSplitKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefType{ekvc@setup@splitkeys}{long} + {% + \let\ekvc@long\long + \let\ekvc@any@long\long + \expandafter\ekvc@SetupSplitKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefNoType{ekvc@setup@splitkeys} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupSplitKeys@a\detokenize{#1}\ekv@stop{#3}% + } +% \end{macrocode} +% If no value was provided this could either be an error, or the unknown key +% forwarding. We have to check this (comparing against |...| inside +% |\ekvc@tripledots|) and if this is the unknown key list type, set it up +% accordingly (advancing the key count and setting up the unknown handlers of +% \expkv). Else we simply throw an error and ignore the incident. +% \begin{macrocode} +\ekvpDefNoValue{ekvc@setup@splitkeys} + {% + \begingroup + \edef\ekvc@tmp{\detokenize{#1}}% + \expandafter + \endgroup + \ifx\ekvc@tripledots\ekvc@tmp + \advance\ekvc@keycount1 +% \end{macrocode} +% The |\begingroup\expandafter\endgroup| ensures that the split mark isn't +% actually defined (even if it just were with meaning |\relax|). +% \begin{macrocode} + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupSplitKeys@unknown + \csname ekvc@splitmark@\the\ekvc@keycount\endcsname + \let\ekvc@any@long\long + \else + \ekvc@err@value@required{#1}% + \fi + } +% \end{macrocode} +% +% Now everything is parsed down to the point that we got the key name and its +% value. We step the key counter and build the mark within a group to not +% accidentally define the |\csname|. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@a + {% + \advance\ekvc@keycount1 + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupSplitKeys@b + \csname ekvc@splitmark@\the\ekvc@keycount\endcsname + } +\protected\long\def\ekvc@SetupSplitKeys@b#1#2\ekv@stop#3% + {% + \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% +% \end{macrocode} +% The short variant needs a bit of special treatment. The key macro will be +% short to throw the correct error, but since there might be long macros +% somewhere the reordering of arguments needs to be long, so for short keys we +% use a two step approach, first grabbing only the short argument, then +% reordering. +% \begin{macrocode} + \ifx\ekvc@long\long + \else + \expandafter\let\csname ekvc@\ekvc@set(#2)\endcsname\ekvc@tmp + \edef\ekvc@tmp##1% + {% + \ekv@unexpanded\expandafter{\csname ekvc@\ekvc@set(#2)\endcsname}% + {##1}% + }% + \fi + \ekvlet\ekvc@set{#2}\ekvc@tmp + \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{#3}}}% + \ekvc@helpers@needed + {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% + {}% + } +% \end{macrocode} +% +% Here we get everything readily set up, |#1| contains the mark for unknown +% keys and they only have to do some reordering. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@unknown#1% + {% + \long\def\ekvc@tmp##1##2##3##4#1##5{##4#1{##5, {##3} = {##1} }}% + \ekvletunknown\ekvc@set\ekvc@tmp + \long\def\ekvc@tmp##1##2##3#1##4{##3#1{##4, {##2} }}% + \ekvletunknownNoVal\ekvc@set\ekvc@tmp + \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{}}}% + \ekvc@helpers@needed + {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% + {}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@defarggobbler} +% This is needed to define a macro with 1-9 parameters programmatically. +% \LaTeX's \cs[no-index]{newcommand} does something similar for example. +% \begin{macrocode} +\protected\def\ekvc@defarggobbler#1{\def\ekvc@tmp##1#1##2##{##1#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@setup@splitmacro, +% \ekvc@split@1, \ekvc@split@2, \ekvc@split@3, +% \ekvc@split@4, \ekvc@split@5, \ekvc@split@6, +% \ekvc@split@7 +% } +% Since the first few split macros are different from the others we manually +% set those up now. All the others will be defined as needed (always +% globally). The split macros just read up until the correct split mark, move +% that argument into a list and reinsert the rest, calling the next split +% macro afterwards. +% \begin{macrocode} +\begingroup +\edef\ekvc@tmp + {% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@1\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + ##2##3% + {##3{##1}##2}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@2\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + ##3##4% + {##4{##1}{##2}##3}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@3\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + ##4##5% + {##5{##1}{##2}{##3}##4}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@4\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + ##5##6% + {##6{##1}{##2}{##3}{##4}##5}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@5\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% + ##6##7% + {##7{##1}{##2}{##3}{##4}{##5}##6}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@6\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@6\endcsname}##6% + ##7##8% + {##8{##1}{##2}{##3}{##4}{##5}{##6}##7}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@7\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@6\endcsname}##6% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@7\endcsname}##7% + ##8##9% + {##9{##1}{##2}{##3}{##4}{##5}{##6}{##7}##8}% + } +\ekvc@tmp +\endgroup +\protected\def\ekvc@setup@splitmacro#1% + {% + \ekv@ifdefined{ekvc@split@#1}{}% + {% + \begingroup + \def\ekvc@tmp##1% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@splitmark@\the\numexpr#1-##1\relax\endcsname}% + }% + \edef\ekvc@tmp + {% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@split@#1\endcsname}% + ####1% + \ekvc@tmp{6}####2% + \ekvc@tmp{5}####3% + \ekvc@tmp{4}####4% + \ekvc@tmp{3}####5% + \ekvc@tmp{2}####6% + \ekvc@tmp{1}####7% + \ekvc@tmp{0}####8% + ####9% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@split@\the\numexpr#1-7\relax\endcsname}% + ####1{{####2}{####3}{####4}{####5}{####6}{####7}{####8}####9}% + }% + }% + \ekvc@tmp + \endgroup + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcHashAndUse} +% |\ekvcHashAndUse| works just like |\ekvcSplitAndUse|. +% \begin{macrocode} +\protected\long\def\ekvcHashAndUse#1#2% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcHashAndUse@#1{}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvcHashAndUse@} +% This is more or less the same as |\ekvcSplitAndUse@|. Instead of an empty +% group we place a marker after the initials, we don't use the sorting macros +% of |split|, but instead pack all the values in one argument. +% \begin{macrocode} +\protected\long\def\ekvcHashAndUse@#1#2#3% + {% + \edef\ekvc@set{\string#1}% + \ekvc@SetupHashKeys{#3}% + \ekvc@helpers@needed + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded{\ekvc@hash@pack@argument}% + \ekv@unexpanded\expandafter{\ekvc@initials\ekvc@stop#2}% + }% + }% + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded{#2}% + \ekv@unexpanded\expandafter{\ekvc@initials\ekvc@stop}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcHashAndForward} +% |\ekvcHashAndForward| works just like |\ekvcSplitAndForward|. +% \begin{macrocode} +\protected\long\def\ekvcHashAndForward#1#2#3% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcHashAndUse@#1{{#2}}{#3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcHash} +% |\ekvcHash| does the same as |\ekvcSplit|, but has the advantage of not +% needing to count arguments, so the definition of the internal macro is a bit +% more straight forward. +% \begin{macrocode} +\protected\long\def\ekvcHash#1#2#3% + {% + \let\ekvc@helpers@needed\@secondoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {% + \expandafter + \ekvcHashAndUse@\expandafter#1\csname ekvc@\string#1\endcsname{#2}% + \ekvc@any@long\expandafter\def\csname ekvc@\string#1\endcsname + ##1\ekvc@stop + {#3}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@hash@pack@argument} +% All this macro does is pack the values into one argument and forward that to +% the next macro. +% \begin{macrocode} +\long\def\ekvc@hash@pack@argument#1\ekvc@stop#2{#2{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@SetupHashKeys,\ekvc@SetupHashKeys@a,\ekvc@SetupHashKeys@b, +% \ekvc@SetupHashKeys@unknown +% } +% This should look awfully familiar as well, since it's just the same as for +% the split keys with a few other names here and there. +% \begin{macrocode} +\protected\long\def\ekvc@SetupHashKeys + {% + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty + \ekvpParse@unsafe\ekvp@@p@ekvc@setup@hashkeys + } +\ekvpNewParser{ekvc@setup@hashkeys} +\ekvpDefType{ekvc@setup@hashkeys}{short} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupHashKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefType{ekvc@setup@hashkeys}{long} + {% + \let\ekvc@long\long + \let\ekvc@any@long\long + \expandafter\ekvc@SetupHashKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefNoType{ekvc@setup@hashkeys} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupHashKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefNoValue{ekvc@setup@hashkeys} + {% + \begingroup + \edef\ekvc@tmp{\detokenize{#1}}% + \expandafter + \endgroup + \ifx\ekvc@tripledots\ekvc@tmp + \ekvc@SetupHashKeys@unknown + \let\ekvc@any@long\long + \else + \ekvc@err@value@required{#1}% + \fi + } +% \end{macrocode} +% Again we build the marker, this time instead of a numbered one a named +% hashmark, inside a group to not actually define the macro used as a marker. +% \begin{macrocode} +\protected\long\def\ekvc@SetupHashKeys@a#1\ekv@stop + {% + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupHashKeys@b\csname ekvc@hashmark@#1\endcsname{#1}% + } +% \end{macrocode} +% Yes, even the defining macro looks awfully familiar. Instead of numbered we +% have named marks. Still the key macros grab everything up to their +% respective mark and reorder the arguments. The same quirk is applied for +% short keys. And instead of the |\ekvc@setup@splitmacro| we use +% |\ekvc@setup@hashmacro|. +% \begin{macrocode} +\protected\long\def\ekvc@SetupHashKeys@b#1#2#3% + {% + \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% + \ifx\ekvc@long\long + \else + \expandafter\let\csname ekvc@\ekvc@set(#2)\endcsname\ekvc@tmp + \edef\ekvc@tmp##1% + {% + \ekv@unexpanded\expandafter{\csname ekvc@\ekvc@set(#2)\endcsname}% + {##1}% + }% + \fi + \ekvlet\ekvc@set{#2}\ekvc@tmp + \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{#3}}}% + \ekvc@setup@hashmacro{#2}% + } +% \end{macrocode} +% Another temporary definition, this time to get the hashmarks for the unknown +% handler without defining them. +% \begin{macrocode} +\def\ekvc@SetupHashKeys@unknown#1% + {% + \protected\def\ekvc@SetupHashKeys@unknown + {% + \ekvletunknown\ekvc@set\ekvc@hash@unknown@kv + \ekvletunknownNoVal\ekvc@set\ekvc@hash@unknown@k + \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{}}}% + \ekvc@setup@hashmacro{...}% + }% + \long\def\ekvc@hash@unknown@kv##1##2##3##4#1##5{##4#1{##5, {##3} = {##1} }}% + \long\def\ekvc@hash@unknown@k##1##2##3#1##4{##3#1{##4, {##2} }}% + } +\begingroup\expandafter\endgroup +\expandafter\ekvc@SetupHashKeys@unknown + \csname ekvc@hashmark@\ekvc@tripledots\endcsname +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@setup@hashmacro} +% The safe hash macros will be executed inside of an |\unexpanded| expansion +% context, so they have to insert braces for that once they are done. +% Most of the tests which have to be executed will already be done, but we +% have to play safe if the hash doesn't show up in the hash list. Therefore we +% use some |\ekvc@mark|s and |\ekvc@stop| to throw errors if the hash isn't +% found in the right place. The fast variants have an easier life and just +% return the correct value. +% \begin{macrocode} +\protected\def\ekvc@setup@hashmacro#1% + {% + \ekv@ifdefined{ekvc@fasthash@#1}{}% + {% + \begingroup + \edef\ekvc@tmp + {% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@fasthash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@stop}% + {####2}% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@safehash@#1\endcsname}% + ####1% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@@safehash@#1\endcsname}% + ####1\ekv@unexpanded{\ekvc@mark}{}% + \ekv@unexpanded\expandafter + {% + \csname ekvc@hashmark@#1\endcsname{}% + \ekvc@mark{\ekvc@err@missing@hash{#1}}\ekvc@stop + }% + }% + \long\gdef + \ekv@unexpanded\expandafter + {\csname ekvc@@safehash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@mark}####4####5% + \ekv@unexpanded{\ekvc@stop}% + {% + ####4{####2}% + }% + \long\gdef\ekv@unexpanded\expandafter + {\csname ekvc@fastsplithash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@stop}####4% + {% + ####4{####2}% + }% + \long\gdef\ekv@unexpanded\expandafter + {\csname ekvc@safesplithash@#1\endcsname}####1% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@@safesplithash@#1\endcsname}% + ####1\ekv@unexpanded{\ekvc@mark\ekvc@safe@after@hash}% + \ekv@unexpanded\expandafter + {% + \csname ekvc@hashmark@#1\endcsname{}% + \ekvc@mark + {\ekvc@err@missing@hash{#1}\ekvc@safe@after@hash}% + \ekvc@stop + }% + }% + \long\gdef\ekv@unexpanded\expandafter + {\csname ekvc@@safesplithash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@mark}####4####5% + \ekv@unexpanded{\ekvc@stop}% + {% + ####4{####2}% + }% + }% + \ekvc@tmp + \endgroup + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcValue} +% \begin{macro}[internal]{\ekvcValue@} +% All this does is a few consistency checks on the first argument (not empty, +% hash macro exists) and then call that hash-grabbing macro that will also +% test whether the hash is inside of |#2| or not. +% \begin{macrocode} +\long\def\ekvcValue#1% + {% + \ekv@unexpanded + \expandafter\ekvcValue@\detokenize{#1}\ekvc@stop + } +\def\ekvcValue@#1\ekvc@stop + {% + \ekv@ifdefined{ekvc@safehash@#1}% + {\csname ekvc@safehash@#1\endcsname}% + {\ekvc@err@unknown@hash{#1}\@firstoftwo{{}}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcValueFast} +% To be as fast as possible, this doesn't test for anything, assuming the user +% knows best. +% \begin{macrocode} +\long\def\ekvcValueFast#1#2% + {\csname ekvc@fasthash@\detokenize{#1}\endcsname#2\ekvc@stop} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcValueSplit} +% \begin{macro}[internal]{\ekvcValueSplit@,\ekvcValueSplit@recover} +% This splits off a single value. +% \begin{macrocode} +\long\def\ekvcValueSplit#1% + {\expandafter\ekvcValueSplit@\detokenize{#1}\ekvc@stop} +\def\ekvcValueSplit@#1\ekvc@stop + {% + \ekv@ifdefined{ekvc@safesplithash@#1}% + {\csname ekvc@safesplithash@#1\endcsname}% + {\ekvc@err@unknown@hash{#1}\ekvcValueSplit@recover}% + } +\long\def\ekvcValueSplit@recover#1#2{#2{}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@safe@after@hash} +% \begin{macrocode} +\long\def\ekvc@safe@after@hash#1#2% + {% + #2{#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcValueSplitFast} +% Again a fast approach which doesn't provide too many safety measurements. +% This needs to build the hash function and expand it before passing the +% results to the next control sequence. The first step only builds the control +% sequence. +% \begin{macrocode} +\long\def\ekvcValueSplitFast#1#2% + {\csname ekvc@fastsplithash@\detokenize{#1}\endcsname#2\ekvc@stop} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@safehash@,\ekvc@fasthash@, +% \ekvc@safesplithash@,\ekvc@fastsplithash@ +% } +% At least in the empty hash case we can provide a meaningful error message +% without affecting performance by just defining the macro that would be build +% in that case. There is of course a downside to this, the error will not be +% thrown by |\ekvcValueFast| in three expansion steps. The safe hash variant +% has to also stop the |\unexpanded| expansion. +% \begin{macrocode} +\long\def\ekvc@safehash@#1{\ekvc@err@empty@hash{}} +\long\def\ekvc@fasthash@#1\ekvc@stop{\ekvc@err@empty@hash} +\long\def\ekvc@safesplithash@#1#2{\ekvc@err@empty@hash#2{}} +\long\def\ekvc@fastsplithash@#1\ekvc@stop#2{\ekvc@err@empty@hash#2{}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcSecondaryKeys} +% \begin{macro}[internal] +% {\ekvcSecondaryKeys@a,\ekvcSecondaryKeys@b,\ekvcSecondaryKeys@c} +% Secondary keys use yet another \expkvp\ parser, keys will be set up further +% down in their own subsection. +% \begin{macrocode} +\ekvpNewParser{ekvc@setup@secondary} +\ekvpValueAlwaysRequired{ekvc@setup@secondary} +\protected\long\def\ekvcSecondaryKeys#1% + {% + \edef\ekvc@set{\string#1}% + \let\ekvc@long\ekv@empty + \ekvpParse@unsafe\ekvp@@p@ekvc@setup@secondary + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcChange} +% This can be used to change the defaults of an \expkvc\ defined macro. It +% checks whether there is a set with the correct name and that the macro is +% defined. If both is true the real work is done by |\ekvc@change|. +% \begin{macrocode} +\protected\long\def\ekvcChange#1% + {% + \ekvifdefinedset{\string#1}% + {% + \ekvc@ifdefined#1% + {\ekvc@change#1}% + {\ekvc@err@no@key@macro#1\@gobble}% + }% + {\ekvc@err@no@key@macro#1\@gobble}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@change ,\ekvc@change@a,\ekvc@change@b, +% \ekvc@change@c,\ekvc@change@d,\ekvc@change@e +% } +% First we need to see whether the macro is currently |\long|. For this we get +% the meaning and will parse it. |#1| is the macro name in which we want to +% change the defaults. +% \begin{macrocode} +\protected\def\ekvc@change#1% + {\expandafter\ekvc@change@a\meaning#1\ekv@stop#1} +% \end{macrocode} +% A temporary definition to get the stringified |macro:|. |##1| will be the +% list of prefixes, we don't care for the exact contents of |##2| and |##3|. +% \begin{macrocode} +\def\ekvc@change@a#1% + {% + \protected\def\ekvc@change@a##1#1##2->##3\ekv@stop + {% + \ekvc@change@iflong{##1}% + {\ekvc@change@b{}}% + {\ekvc@change@b{\long}}% + }% + } +\expandafter\ekvc@change@a\expandafter{\detokenize{macro:}} +% \end{macrocode} +% Next we expand the macro once to get its contents (including the current +% default values with their markers) and place |\ekvc@stop| instead of an +% argument as a marker for the last step. |#1| is either |\long| or empty, +% |#2| is the macro. +% \begin{macrocode} +\protected\def\ekvc@change@b#1#2% + {\expandafter\ekvc@change@c\expandafter{#2\ekvc@stop}{#1}#2} +% \end{macrocode} +% Here we place an unbalanced closing brace after the expansion of the macro. +% Then we just parse the \kv-list with |\ekvset|, that will exchange the +% values behind the markers. Once those are changed we give control to +% |\ekvc@change@d|. The |\ekvset| step might horribly fail if the user defined +% some keys that don't behave nice. |#1| is the expansion of the macro, |#2| +% is either |\long| or empty, |#3| is the macro, and |#4| is the \kv-list +% containing the new defaults. +% \begin{macrocode} +\ekv@exparg{\protected\long\def\ekvc@change@c#1#2#3#4}% + {% + \expandafter\iffalse\expandafter{\expandafter{\expandafter\fi + \ekvset{\string#3}{#4}% + \ekvc@change@d{#2}{#3}% + #1% + }}% + } +% \end{macrocode} +% The final step needs to put an unbalanced opening brace after |\edef|. Also +% we have to protect everything from further expanding with the exception of +% the redefined macro's argument, which is why we placed the |\ekvc@stop| +% earlier. Then we need to also protect the rest of the contents from further +% expanding using |\unexpanded| with another unbalanced opening brace. +% |#1| will be either empty or |\long| and |#2| is the macro. +% \begin{macrocode} +\protected\def\ekvc@change@d#1#2% + {#1\edef#2##1{\expandafter\ekvc@change@e\iffalse}\fi} +\long\def\ekvc@change@e#1\ekvc@stop + {\ekv@unexpanded{#1}##1\ekv@unexpanded\expandafter{\iffalse}\fi} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@change@iflong,\ekvc@change@iflong@} +% Checking whether a string contains the string representation of |\long| can +% be done by gobbling everything up to the first |\long| and checking whether +% the result is completely empty. We need a temporary macro to get the result +% of |\string\long| inside the definitions. +% \begin{macrocode} +\def\ekvc@change@iflong#1% + {% + \protected\def\ekvc@change@iflong##1% + {\expandafter\ekv@ifempty\expandafter{\ekvc@change@iflong@##1#1}}% + \def\ekvc@change@iflong@##1#1{} + } +\expandafter\ekvc@change@iflong\expandafter{\string\long} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcPass} +% This macro can be used to pass a value to a key of some macro (this way +% more complicated key codes are possible that in the end pass processed +% values on to some macro). The implemantation is pretty straight forward. +% \begin{macrocode} +\long\def\ekvcPass#1#2% + {% + \ekvifdefined{\string#1}{#2}% + {\csname\ekv@name{\string#1}{#2}\endcsname}% + {\ekvc@err@unknown@key@or@macro{#1}{#2}\@gobble}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Secondary Key Types} +% +% There is a single prefix named |long| and set up pretty simple. +% \begin{macrocode} +\ekvpDefPrefixLet{ekvc@setup@secondary}{long}\ekvc@long\long\ekv@empty +% \end{macrocode} +% +% \begin{macro}[internal] +% { +% meta, nmeta, +% \ekvc@type@meta, \ekvc@type@meta@ +% } +% The |meta| and |nmeta| key types use |\ekvmorekv| to set other keys in +% the same macro's \set. If the \kv\ list ist fixed (|nmeta|) we can expand +% |\ekvmorekv| as far as possible (twice), else we expand it only once. This +% makes a difference only if the \val\ of the |meta| key isn't forwarded in +% braces and commas are active at the time of usage, in which case expanding +% |\ekvmorekv| twice would result in wrong behaviour. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{meta} + {\ekvc@type@meta\ekv@exparg\ekvc@long{##1}\ekvlet{#1}{#3}} +\ekvpDefType{ekvc@setup@secondary}{nmeta} + {\ekvc@assert@not@long\ekvc@type@meta\ekv@expargtwice{}{}\ekvletNoVal{#1}{#3}} +\protected\long\def\ekvc@type@meta#1#2#3#4#5#6% + {% + #1\ekvc@type@meta@{\ekvmorekv{#6}}{#2}{#3}% + #4\ekvc@set{#5}\ekvc@tmp + } +\protected\long\def\ekvc@type@meta@#1#2#3{#2\def\ekvc@tmp#3{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{alias} +% |alias| just checks whether there is a key and/or |NoVal| key defined with +% the target name and |\let| the key to those. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{alias} + {% + \ekvc@assert@not@long + \ekvc@assert@k@or@p@defined{#3}% + \ekvifdefined\ekvc@set{#3}{\ekvletkv\ekvc@set{#1}\ekvc@set{#3}}{}% + \ekvifdefinedNoVal\ekvc@set{#3}{\ekvletkvNoVal\ekvc@set{#1}\ekvc@set{#3}}{}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{default} +% The |default| key can be used to set a \Nkey\ for an existing \Vkey. It +% will just pass the \val\ to the key macro of that other key. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{default} + {% + \ekvc@assert@defined{#1}% + \ekvc@assert@not@long + \edef\ekvc@tmp + {% + \ekv@unexpanded\expandafter + {\csname\ekv@name\ekvc@set{#1}\endcsname{#3}}% + }% + \ekvletNoVal\ekvc@set{#1}\ekvc@tmp + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{enum,choice} +% \begin{macro}[internal] +% { +% \ekvc@type@enum,\ekvc@h@enum,\ekvc@h@enum@,\ekvc@enum@name, +% \ekvc@type@choice@ +% } +% Enums don't need to apply special trickery to make the parts of the names +% retrievable, so unlike in \expkvd\ we don't need catcode juggling. +% The setup of an |enum| requires unpacking the value in two different +% arguments so we need an auxiliary here. +% \begin{macrocode} +\def\ekvc@enum@name#1#2#3{ekvc#1(#2)#3} +\ekvpDefType{ekvc@setup@secondary}{enum} + {\ekvpAssertTwoValues{#3}\ekvc@type@enum\ekvc@type@enum@{#1}#3} +\ekvpDefType{ekvc@setup@secondary}{choice} + {\ekvpAssertTwoValues{#3}\ekvc@type@enum\ekvc@type@choice@{#1}#3} +\protected\long\def\ekvc@type@enum#1#2#3% + {% + \ekvc@assert@defined{#3}% +% \end{macrocode} +% At run time we need another helper and we need to expand the current +% |\ekvc@set| now. The helper will build a control sequence from each given +% value, those will be set up in the |\ekvcsvloop|. +% \begin{macrocode} + \ekvc@long\edef\ekvc@tmp##1% + {% + \ekv@unexpanded{\expandafter\ekvc@h@enum\detokenize}{##1}% + \ekv@unexpanded{\ekvc@stop}% + {\ekvc@set}{#2}% + }% + \ekvlet\ekvc@set{#2}\ekvc@tmp + \def\ekvc@tmp{0}% + \expandafter\ekvcsvloop\expandafter + {\expandafter#1\csname\ekv@name\ekvc@set{#3}\endcsname{#2}}% + } +% \end{macrocode} +% Here |#1| will be the key-macro of the underlying primary or secondary key, +% |#2| is the |enum| key's name, and |#3| will be the choice. The rest is +% pretty obvious. +% \begin{macrocode} +\ekv@exparg{\protected\long\def\ekvc@type@enum@#1#2#3}% + {% + \expandafter\expandafter\expandafter\edef\expandafter + \csname\ekvc@enum@name\ekvc@set{#2}{\detokenize{#3}}\endcsname + {\ekv@unexpanded{#1}{\ekvc@tmp}}% + \edef\ekvc@tmp{\the\numexpr\ekvc@tmp+1\relax}% + } +\ekv@exparg{\protected\long\def\ekvc@type@choice@#1#2#3}% + {% + \expandafter\expandafter\expandafter\edef\expandafter + \csname\ekvc@enum@name\ekvc@set{#2}{\detokenize{#3}}\endcsname + {\ekv@unexpanded{#1{#3}}}% + } +% \end{macrocode} +% The use-time helper will check if the macro for the passed in choice exists, +% if it doesn't throws an error, else calls that macro which will set the +% correct value. +% \begin{macrocode} +\ekv@if@lastnamedcs + {% + \ekv@exparg{\def\ekvc@h@enum#1\ekvc@stop#2#3}% + {% + \expandafter\ifcsname\ekvc@enum@name{#2}{#3}{#1}\endcsname + \expandafter\ekvc@h@enum@\lastnamedcs + \fi + \ekvc@err@unknown@enum{#2}{#3}{#1}% + } + \def\ekvc@h@enum@#1\fi\ekvc@err@unknown@enum#2#3#4% + {% + \fi + \ifx#1\relax + \ekvc@err@unknown@enum{#2}{#3}{#4}% + \expandafter\@gobble + \fi + #1% + } + } + {% + \def\ekvc@h@enum#1% + {% + \def\ekvc@h@enum##1\ekvc@stop##2##3% + {% + \expandafter\ekvc@h@enum@ + \csname\ifcsname#1\endcsname#1\else relax\fi\endcsname + {##2}{##3}{##1}% + }% + } + \expandafter\ekvc@h@enum\expandafter{\ekvc@enum@name{#2}{#3}{#1}} + \def\ekvc@h@enum@#1#2#3#4% + {% + \ifx#1\relax + \ekvc@err@unknown@enum{#2}{#3}{#4}% + \expandafter\@gobble + \fi + #1% + } + } +% \end{macrocode} +% We don't need |\ekvc@enum@name| anymore, so let's undefine it. +% \begin{macrocode} +\let\ekvc@enum@name\ekvc@undefined +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{aggregate,e-aggregate} +% Aggregating isn't easy to define. We'll have to extract the correct mark for +% the specified key, branch correctly for short and long keys, and use a small +% hack to have the correct arguments on the user interface (|#1| as the +% current contents, |#2| as the new value). This is split into a few steps +% here. +% +% First, assert that the user input is well-behaved. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{aggregate} + {% + \let\ekvc@type@aggregate@ifexpanded\@secondoftwo + \ekvc@type@aggregate{#1}{#3}% + } +\ekvpDefType{ekvc@setup@secondary}{e-aggregate} + {% + \let\ekvc@type@aggregate@ifexpanded\@firstoftwo + \ekvc@type@aggregate{#1}{#3}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvc@type@aggregate,\ekvc@type@aggregate@a,\ekvc@type@aggregate@b} +% The next step stores the user defined processing in a temporary macro that's +% used to do the parameter number swapping later. It also builds the names of +% the key macro and the helper which would be used for processing a short key. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate#1#2% + {% + \ekvc@assert@not@long + \ekvpAssertTwoValues{#2}% + \ekvc@type@aggregate@a + \ekvc@type@aggregate@long\ekvc@type@aggregate@short + {#1}#2% + } +\protected\long\def\ekvc@type@aggregate@a#1#2#3#4#5% + {% + \ekvc@assert@defined{#4}% + \def\ekvc@type@aggregate@tmp##1##2{#5}% + \begingroup\expandafter\endgroup + \expandafter\ekvc@type@aggregate@b + \csname\ekv@name\ekvc@set{#4}\expandafter\endcsname + \csname ekvc@\ekvc@set(#4)\endcsname + #1#2% + {#3}% + } +\protected\long\def\ekvc@type@aggregate@b#1#2#3#4% + {% + \ekvc@type@aggregate@check@long#1#2% + {#3#1}% + {#4#2}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@type@aggregate@check@long, +% \ekvc@type@aggregate@check@long@a,\ekvc@type@aggregate@check@long@b +% } +% To check whether the primary key is long we see whether its |\meaning| +% contains the helper which would only be there for short keys. For this we +% have to get the stringified name of the internal (using |\detokenize|), +% and afterwards get the |\meaning| of the macro. A temporary helper does the +% real test by gobbling and forwarding the result to |\ekv@ifempty|. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate@check@long#1#2% + {\expandafter\ekvc@type@aggregate@check@long@a\detokenize{#2}\ekv@stop#1} +\protected\long\def\ekvc@type@aggregate@check@long@a#1\ekv@stop#2% + {% + \def\ekvc@type@aggregate@check@long@@##1#1{}% + \expandafter\ekvc@type@aggregate@check@long@b\meaning#2\ekv@stop{#1}% + } +\protected\def\ekvc@type@aggregate@check@long@b#1\ekv@stop#2% + {\expandafter\ekv@ifempty\expandafter{\ekvc@type@aggregate@check@long@@#1#2}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@type@aggregate@long,\ekvc@type@aggregate@long@} +% The long variant just builds the split mark we extract, uses the hack to +% swap argument order, and then does the definition via |\ekvlet| and a +% temporary macro. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate@long#1% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@aggregate@long@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + } +\protected\long\def\ekvc@type@aggregate@long@#1#2#3% + {% + \ekvc@type@aggregate@ifexpanded + {% + \long\def\ekvc@type@aggregate@tmp##1##2#1##3% + {\ekv@expanded{\ekv@unexpanded{##2#1}{#2}}}% + }% + {\long\def\ekvc@type@aggregate@tmp##1##2#1##3{##2#1{#2}}}% + \ekvlet\ekvc@set{#3}\ekvc@type@aggregate@tmp + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvc@type@aggregate@short,\ekvc@type@aggregate@short@} +% The short variant will have to build the marker and the name of the helper +% function, and swap the user argument order. Hence here are a few more +% |\expandafter|s involved. But afterwards we can do the definition of the key +% and the helper macro directly. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate@short#1#2% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@aggregate@short@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \csname ekvc@\ekvc@set(#2)\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + {#2}% + } +\protected\long\def\ekvc@type@aggregate@short@#1#2#3#4% + {% + \ekvdef\ekvc@set{#4}{#2{##1}}% + \ekvc@type@aggregate@ifexpanded + {\long\def#2##1##2#1##3{\ekv@expanded{\ekv@unexpanded{##2#1}{#3}}}}% + {\long\def#2##1##2#1##3{##2#1{#3}}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{process} +% The |process| type can reuse much of |aggregate|, just the last step of +% definition differ. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{process} + {% + \ekvpAssertTwoValues{#3}% + \ifx\ekvc@long\long + \ekv@fi@firstoftwo + \fi + \@secondoftwo + {% + \ekvc@type@aggregate@a + \ekvc@type@process@long\ekvc@type@process@long + }% + {% + \ekvc@type@aggregate@a + \ekvc@type@process@short\ekvc@type@process@short + }% + {#1}#3% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@type@process@long,\ekvc@type@process@long@} +% This defines a temporary macro to grab the current value (found after the +% marker |#1|), executes the user code and puts everything back to where it +% belongs. Then |\ekvlet| is used to assign that meaning to the key macro. +% \begin{macrocode} +\protected\long\def\ekvc@type@process@long#1% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@process@long@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + } +\protected\long\def\ekvc@type@process@long@#1#2#3% + {% + \long\def\ekvc@type@aggregate@tmp##1##2#1##3{#2##2#1{##3}}% + \ekvlet\ekvc@set{#3}\ekvc@type@aggregate@tmp + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@type@process@short,\ekvc@type@process@short@} +% We define the key macro directly to just grab the argument once and forward +% it to the auxiliary. That one does essentially the same as the long variant. +% \begin{macrocode} +\protected\long\def\ekvc@type@process@short#1#2% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@process@short@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \csname ekvc@\ekvc@set(#2)\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + {#2}% + } +\protected\long\def\ekvc@type@process@short@#1#2#3#4% + {% + \ekvdef\ekvc@set{#4}{#2{##1}}% + \long\def#2##1##2#1##3{#3##2#1{##3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{flag-bool} +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{flag-bool} + {% + \ekvc@assert@not@long + \ekvpAssertOneValue{#3}% + \ifdefined#3\else\ekvcFlagNew#3\fi + \ekvdef\ekvc@set{#1}% + {% + \ekv@ifdefined{ekvc@flag@set@##1}% + {% + \csname ekvc@flag@set@##1\expandafter\endcsname + \ekvcFlagHeight#3\ekv@stop#3% + }% + {\ekvc@err@invalid@bool{##1}}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{flag-true,flag-false,flag-raise} +% \begin{macro}[internal]{\ekvc@type@flag} +% \begin{macrocode} +\protected\def\ekvc@type@flag#1#2#3% + {% + \ekvc@assert@not@long + \ekvpAssertOneValue{#3}% + \ifdefined#3\else\ekvcFlagNew#3\fi + \ekv@exparg{\ekvdefNoVal\ekvc@set{#2}}{#1#3}% + } +\ekvpDefType{ekvc@setup@secondary}{flag-true} + {\ekvc@type@flag\ekvcFlagSetTrue{#1}{#3}} +\ekvpDefType{ekvc@setup@secondary}{flag-false} + {\ekvc@type@flag\ekvcFlagSetFalse{#1}{#3}} +\ekvpDefType{ekvc@setup@secondary}{flag-raise} + {\ekvc@type@flag\ekvcFlagRaise{#1}{#3}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{Flags} +% +% The basic idea of flags is to store information by the fact that \TeX\ +% expandably assigns the meaning |\relax| to undefined control sequences which +% were built with |\csname|. This mechanism is borrowed from \pkg{expl3}. +% +% \begin{macro}[internal]{\ekvc@flag@name,\ekvc@flag@namescheme} +% Flags follow a simple naming scheme which we define here. |\ekvc@flag@name| +% will store the name of an internal function that is used to build names of +% the second naming scheme defined by |\ekvc@flag@namescheme|. +% \begin{macrocode} +\def\ekvc@flag@name{ekvcf\string} +\def\ekvc@flag@namescheme#1#2{ekvch#2#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagHeight} +% For semantic reasons we use |\number| with another name. +% \begin{macrocode} +\let\ekvcFlagHeight\number +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagNew} +% This macro defines a new flag. It stores the function build with the +% |\ekvc@flag@name| naming scheme after the internal function +% |\ekvc@flag@height| that'll determine the current flag height. It'll also +% define the macro named via |\ekvc@flag@name| to build names according to +% |\ekvc@flag@namescheme|. +% \begin{macrocode} +\protected\def\ekvcFlagNew#1% + {% + \edef#1% + {% + \ekv@unexpanded{\ekvc@flag@height}% + \ekv@unexpanded\expandafter{\csname\ekvc@flag@name#1\endcsname}% + }% + \ekv@expargtwice + {\expandafter\def\csname\ekvc@flag@name#1\endcsname##1}% + {\expandafter\ekvc@flag@namescheme\expandafter{\string#1}{##1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@flag@height,\ekvc@flag@height@} +% This macro gets the height of a flag by a simple loop. The first loop +% iteration differs a bit from the following in that it doesn't have to get +% the current iteration count. The space at the end of |\ekvc@flag@height| +% ends the |\number| evaluation. +% \begin{macrocode} +\def\ekvc@flag@height#1% + {% + \ifcsname#10\endcsname + \ekvc@flag@height@1\ekv@stop#1% + \fi + \@firstofone{0} % leave this space + } +\def\ekvc@flag@height@#1\ekv@stop#2\fi\@firstofone#3% + {% + \fi + \ifcsname#2{#1}\endcsname + \expandafter\ekvc@flag@height@\the\numexpr#1+1\relax\ekv@stop#2% + \fi + \@firstofone{#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagRaise} +% Raising a flag simply means letting the |\ekvc@flag@namescheme| macro for +% the current height to relax. The result of raising a flag is that its height +% is bigger by $1$. +% \begin{macrocode} +\ekv@exparg{\def\ekvcFlagRaise#1}% + {% + \expandafter\expandafter\expandafter\@gobble\expandafter + \csname\ekvc@flag@namescheme{\string#1}{\ekvcFlagHeight#1}\endcsname + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagSetTrue,\ekvcFlagSetFalse} +% \begin{macro}[internal]{\ekvc@flag@set@true,\ekvc@flag@set@false} +% A flag is considered true if its current height is odd, and as false if it +% is even. Therefore |\ekvcFlagSetTrue| and |\ekvcFlagSetFalse| only need to +% raise the flag if the opposing boolean value is the current one. +% \begin{macrocode} +\def\ekvcFlagSetTrue#1% + {\expandafter\ekvc@flag@set@true\ekvcFlagHeight#1\ekv@stop#1} +\def\ekvcFlagSetFalse#1% + {\expandafter\ekvc@flag@set@false\ekvcFlagHeight#1\ekv@stop#1} +% \end{macrocode} +% We can expand |\ekvc@flag@namescheme| at definition time here, which is why +% we're using a temporary definition to set up |\ekvc@flag@set@true| and +% |\ekvc@flag@set@false|. +% \begin{macrocode} +\def\ekvc@flag@set@true#1% + {% + \def\ekvc@flag@set@true##1\ekv@stop##2% + {% + \ifodd##1 + \ekv@fi@gobble + \fi + \@firstofone{\expandafter\@gobble\csname#1\endcsname}% + }% + \def\ekvc@flag@set@false##1\ekv@stop##2% + {% + \ifodd##1 + \ekv@fi@firstofone + \fi + \@gobble{\expandafter\@gobble\csname#1\endcsname}% + }% + } +\expandafter\ekvc@flag@set@true\expandafter + {\ekvc@flag@namescheme{\string#2}{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcFlagIf} +% As already explained, truthiness is defined as a flag's height being odd, so +% we just branch accordingly here. +% \begin{macrocode} +\def\ekvcFlagIf#1% + {% + \ifodd#1% + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagIfRaised} +% This macro uses flags as a switch, if a flag's current height is bigger than +% $0$ this test yields true. +% \begin{macrocode} +\ekv@exparg{\def\ekvcFlagIfRaised#1}% + {% + \expandafter\ifcsname\ekvc@flag@namescheme{\string#1}0\endcsname + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagReset,\ekvcFlagResetGlobal} +% \begin{macro}[internal]{\ekvc@flag@reset,\ekvc@flag@reset@} +% Resetting works by locally letting all the defined internal macros named +% after |\ekvc@flag@namescheme| to undefined. +% \begin{macrocode} +\protected\def\ekvcFlagReset#1% + {\expandafter\ekvc@flag@reset\csname\ekvc@flag@name#1\endcsname{}} +\protected\def\ekvcFlagResetGlobal#1% + {\expandafter\ekvc@flag@reset\csname\ekvc@flag@name#1\endcsname\global} +\protected\def\ekvc@flag@reset#1#2% + {% + \ifcsname#10\endcsname + #2\expandafter\let\csname#10\endcsname\ekvc@undefined + \ekvc@flag@reset@1\ekv@stop#1{#2}% + \fi + } +\protected\def\ekvc@flag@reset@#1\ekv@stop#2#3\fi + {% + \fi + \ifcsname#2{#1}\endcsname + #3\expandafter\let\csname#2{#1}\endcsname\ekvc@undefined + \expandafter\ekvc@flag@reset@\the\numexpr#1+1\relax\ekv@stop#2{#3}% + \fi + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcFlagGetHeight} +% \begin{macro}[internal]{\ekvc@flag@get@height@single} +% These are just small helpers, first getting the height of the flag and then +% passing it on to the user supplied code. +% \begin{macrocode} +\def\ekvcFlagGetHeight#1% + {\expandafter\ekvc@flag@get@height@single\ekvcFlagHeight#1\ekv@stop} +\long\def\ekvc@flag@get@height@single#1\ekv@stop#2{#2{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcFlagGetHeights} +% \begin{macro}[internal] +% {\ekvc@flag@get@heights,\ekvc@flag@get@heights@,\ekvc@flag@get@heights@done} +% This works by a simple loop that stops at |\ekv@stop|. As long as that +% marker isn't hit, get the next flags height and put it into a list after +% |\ekv@stop|. |\ekvc@flag@get@heights@| uses the same marker name for the +% end of the height, which shouldn't clash in any case. Once we're done we +% remove the remainder of the current iteration and leave the user supplied +% code in the input stream with all the flags' heights as a single argument. +% \begin{macrocode} +\def\ekvcFlagGetHeights#1% + {% + \ekvc@flag@get@heights#1\ekv@stop{}% + } +\def\ekvc@flag@get@heights#1% + {% + \ekv@gobbleto@stop#1\ekvc@flag@get@heights@done\ekv@stop + \expandafter\ekvc@flag@get@heights@\ekvcFlagHeight#1\ekv@stop + } +\def\ekvc@flag@get@heights@#1\ekv@stop#2\ekv@stop#3% + {\ekvc@flag@get@heights#2\ekv@stop{#3{#1}}} +\long\def\ekvc@flag@get@heights@done + \ekv@stop + \expandafter\ekvc@flag@get@heights@\ekvcFlagHeight\ekv@stop\ekv@stop#1#2% + {#2{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{Helper Macros} +% +% \begin{macro}[internal]{\ekvc@extract@mark,\ekvc@extract@mark@} +% This is used to extract the mark of a split or hash key from its definition. +% This is kind of fragile, it assumes |#1| is always a macro used for hashing +% or splitting. Also it assumes that the escape character is a backslash. +% \begin{macrocode} +\def\ekvc@extract@mark#1{\expandafter\ekvc@extract@mark@\meaning#1\ekv@stop} +\begingroup +\lccode`;=`# +\lccode`/=`\\ +\lowercase{\endgroup +\def\ekvc@extract@mark@#1:#2/#3 ;#4\ekv@stop{#3}% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Assertions} +% +% \begin{macro}[internal]{\ekvc@assert@not@long} +% Some keys don't want to be |long| and we have to educate the user, so let's +% throw an error if someone wanted these to be long. +% \begin{macrocode} +\ekv@exparg{\def\ekvc@assert@not@long}% + {\ekvpAssertIfNot{\ifx\ekvc@long\long}{`long' not accepted}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@assert@defined,\ekvc@assert@k@or@p@defined} +% Since some keys interact with existing other keys we need to assert those +% exist. +% \begin{macrocode} +\long\def\ekvc@assert@defined#1% + {\ekvpAssertTF{\ekvifdefined\ekvc@set{#1}}{undefined key `#1'}} +\long\def\ekvc@assert@k@or@p@defined#1% + {% + \ekvpAssertTF + {\ekvifdefined\ekvc@set{#1}\@firstoftwo{\ekvifdefinedNoVal\ekvc@set{#1}}}% + {undefined key `#1'}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Messages} +% +% \begin{macro}[internal] +% { +% \ekvc@errm, +% \ekvc@err@toomany,\ekvc@err@value@required, +% \ekvc@err@already@defined,\ekvc@err@no@key@macro, +% } +% Boring unexpandable error messages. +% \begin{macrocode} +\protected\long\def\ekvc@errm#1{\errmessage{expkv-cs Error: #1}} +\protected\long\def\ekvc@err@toomany#1% + {\ekvc@errm{Too many keys for macro `\string#1'}} +\protected\long\def\ekvc@err@value@required#1% + {\ekvc@errm{Missing value for key `\ekv@unexpanded{#1}'}} +\protected\long\def\ekvc@err@already@defined#1% + {\ekvc@errm{Macro `\string#1' already defined}} +\protected\long\def\ekvc@err@no@key@macro#1% + {\ekvc@errm{\string#1 is no key=val macro}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@err} +% We need a way to throw error messages expandably in some contexts. +% \begin{macrocode} +\ekv@exparg{\long\def\ekvc@err#1}{\ekverr{expkv-cs}{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@err@unknown@hash,\ekvc@err@empty@hash,\ekvc@err@missing@hash, +% \ekvc@err@invalid@bool,\ekvc@err@unknown@key@or@macro, +% \ekvc@err@unknown@enum +% } +% And here are the expandable error messages. +% \begin{macrocode} +\long\def\ekvc@err@unknown@hash#1{\ekvc@err{unknown hash `#1'}} +\long\def\ekvc@err@missing@hash#1{\ekvc@err{hash `#1' not found}} +\long\def\ekvc@err@empty@hash{\ekvc@err{empty hash}} +\def\ekvc@err@invalid@bool#1{\ekvc@err{invalid boolean value `#1'}} +\long\def\ekvc@err@unknown@key@or@macro#1#2% + {\ekvc@err{unknown key `#2' for #1}} +\def\ekvc@err@unknown@enum#1#2#3% + {\ekvc@err{unknown choice `#3' for `#2' in #1}} +% \end{macrocode} +% \end{macro} +% +% +% Now everything that's left is to reset the category code of |@|. +% \begin{macrocode} +\catcode`\@=\ekvc@tmp +% \end{macrocode} +% +% \gobbledocstriptag +%</tex> +%^^A=<< diff --git a/macros/generic/expkv-bundle/expkv-def.dtx b/macros/generic/expkv-bundle/expkv-def.dtx new file mode 100644 index 0000000000..33933425e6 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-def.dtx @@ -0,0 +1,1369 @@ +% \iffalse meta-comment +% +% File: expkv-def.dtx 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 +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\expkvDocNoGenerate{} +\input expkv-bundle.ins +\generate{\file{expkv-def.sty}{\from{expkv-def.dtx}{pkg}}} +\generate{\file{expkv-def.tex}{\from{expkv-def.dtx}{tex}}} +\generate{\file{t-expkv-def.tex}{\from{expkv-def.dtx}{ctx}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkvd} +%^^A the LaTeX package >>= +% \subsection{The \LaTeX\ Package} +% Just like for \expkv\ we provide a small \LaTeX\ package that sets up things +% such that we behave nicely on \LaTeX\ packages and files system. It'll +% |\input| the generic code which implements the functionality. +% \gobbledocstriptag +%<*pkg> +% \begin{macrocode} +\RequirePackage{expkv-pop} +\def\ekvd@tmp + {% + \ProvidesFile{expkv-def.tex}% + [\ekvdDate\space v\ekvdVersion\space a key-defining frontend for expkv]% + } +\input{expkv-def.tex} +\ProvidesPackage{expkv-def}% + [\ekvdDate\space v\ekvdVersion\space a key-defining frontend for expkv] +% \end{macrocode} +% \gobbledocstriptag +%</pkg> +%^^A=<< +%^^A the ConTeXt module >>= +% \subsection{The \ConTeXt\ module} +% \gobbledocstriptag +%<*ctx> +% \begin{macrocode} +\writestatus{loading}{ConTeXt User Module / expkv-def} +\usemodule[expkv-pop] +\unprotect +\input expkv-def.tex +\writestatus{loading} + {ConTeXt User Module / expkv-def / Version \ekvdVersion\space loaded} +\protect\endinput +% \end{macrocode} +% \gobbledocstriptag +%</ctx> +%^^A=<< +%^^A main file >>= +% \subsection{The Generic Code} +% The rest of this implementation will be the generic code. +% \gobbledocstriptag +%<*tex> +% +% Load \expkv\ if the package didn't already do so -- since \expkv\ has +% safeguards against being loaded twice this does no harm and the overhead +% isn't that big. Also we reuse some of the internals of \expkv\ to save us from +% retyping them. Additionally load \expkvp, which aids in defining the type +% system (and \expkvp\ will actually do the \expkv\ loading). +% \begin{macrocode} +\input expkv-pop +% \end{macrocode} +% +% We make sure that \file{expkv-def.tex} is only input once: +% \begin{macrocode} +\expandafter\ifx\csname ekvdVersion\endcsname\relax +\else + \expandafter\endinput +\fi +% \end{macrocode} +% +% \begin{macro}{\ekvdVersion,\ekvdDate} +% We're on our first input, so lets store the version and date in a macro. +% \begin{macrocode} +\def\ekvdVersion{1.0} +\def\ekvdDate{2023-01-23} +% \end{macrocode} +% \end{macro} +% +% If the \LaTeX\ format is loaded we want to be a good file and report back who +% we are, for this the package will have defined |\ekvd@tmp| to use +% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. +% \begin{macrocode} +\csname ekvd@tmp\endcsname +% \end{macrocode} +% +% Store the category code of |@| to later be able to reset it and change it to +% 11 for now. +% \begin{macrocode} +\expandafter\chardef\csname ekvd@tmp\endcsname=\catcode`\@ +\catcode`\@=11 +% \end{macrocode} +% |\ekvd@tmp| will be reused later to handle expansion during the key +% defining. But we don't need it to ever store information long-term after +% \expkvd\ was initialized. +% +% \begin{macro}[internal]{\ekvd@ifprimitive} +% \begin{macrocode} +\protected\long\def\ekvd@ifprimitive#1% + {% + \begingroup + \edef\ekvd@tmpa{\string #1}% + \edef\ekvd@tmpb{\meaning#1}% + \expandafter + \endgroup + \ifx\ekvd@tmpa\ekvd@tmpb + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvd@long,\ekvd@prot,\ekvd@clear@prefixes,\ekvd@ifalso} +% \expkvd\ will use |\ekvd@long|, |\ekvd@prot|, and |\ekvd@ifalso| to store +% whether a key should be defined as |\long| or |\protected| or adds an action +% to an existing key, and we have to clear them for every new key. By default +% |long| and |protected| will just be empty, |ifalso| will be +% |\@secondoftwo|, and |ifnew| will just use its third argument. +% \begin{macrocode} +\protected\def\ekvd@clear@prefixes + {% + \let\ekvd@long\ekv@empty + \let\ekvd@prot\ekv@empty + \let\ekvd@ifalso\@secondoftwo + \let\ekvd@do@new\@gobbletwo + } +\ekvd@clear@prefixes +% \end{macrocode} +% \end{macro} +% +% We define the parser for our front-facing macro and its \prefix{}es here: +% \begin{macrocode} +\ekvpNewParser{ekvd@definekeys} +\ekvpDefNoValuePrefix{ekvd@definekeys} + {\let\ekvd@ifnoarg\@firstoftwo} + {\let\ekvd@ifnoarg\@secondoftwo} +\ekvpDefPrefixLet{ekvd@definekeys}{long} \ekvd@long\long \ekv@empty +\ekvpDefPrefixLet{ekvd@definekeys}{protected}\ekvd@prot\protected\ekv@empty +\ekvpDefPrefixLet{ekvd@definekeys}{protect} \ekvd@prot\protected\ekv@empty +\ekvpDefPrefixLet{ekvd@definekeys}{also}\ekvd@ifalso\@firstoftwo\@secondoftwo +\ekvpDefPrefixLet{ekvd@definekeys}{new}\ekvd@do@new\ekvd@assert@new\@gobbletwo +% \end{macrocode} +% We ease the process of error throwing a bit for now by using our own macro +% instead of relying on \expkvp's argument forwarding. +% \begin{macrocode} +\ekvpDefAutoPrefix{ekvd@definekeys}{\edef\ekvd@cur{\detokenize{#3}}}{} +% \end{macrocode} +% +% \begin{macro}{\ekvdefinekeys} +% This is the one front-facing macro which provides the interface to define +% keys. It stores the \set\ for which the keys should be defined in +% |\ekvd@set| and calls a parser defined with \expkvp. +% \begin{macrocode} +\protected\edef\ekvdefinekeys#1% + {% + \ekv@unexpanded{\def\ekvd@set}{#1}% + \ekv@unexpanded{\ekvpParse@unsafe@auto\ekvp@@p@ekvd@definekeys}% + \ekv@unexpanded\expandafter{\csname\ekvp@@p@ekvd@definekeys{ppa}\endcsname}% + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Key Types} +% +% \begin{macro}[internal]{\ekvd@def@type,\ekvd@def@type@fwd} +% To reduce some typing the following is a shortcut to |\ekvpDefType|. The +% |@fwd| variant will forward the key name and value and remove the +% unprocessed key. The other variant automatically sets up a helper macro, +% unfortunately this is necessary due to the design decision of \expkvp\ to +% not |\detokenize| the key names while \expkvd\ used to do this very early. +% \begin{macrocode} +\protected\def\ekvd@def@type#1#2% + {% + \ekv@exparg{\ekvpDefType{ekvd@definekeys}{#1}} + {% + \expandafter\expandafter\csname ekvd@th@#1\endcsname + \detokenize{##1}\ekv@stop{##2}{##3}% + }% + \long\expandafter\def\csname ekvd@th@#1\endcsname##1\ekv@stop##2##3{#2}% + } +\protected\long\def\ekvd@def@type@fwd#1#2% + {\ekvpDefType{ekvd@definekeys}{#1}{\ekv@exparg{#2}{\detokenize{##1}}{##3}}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{set} +% \begin{macro}[internal]{\ekvd@type@set} +% \begin{macrocode} +\ekvd@def@type{set} + {\ekvd@ifnoarg{\ekvd@type@set{#1}{#1}}{\ekvd@type@set{#1}{#3}}} +\protected\def\ekvd@type@set#1#2% + {% + \ekvd@assert@not@long + \ekvd@assert@not@protected + \ekvd@do@new{NoVal}{#1}% + \ekv@ifempty{#2}% + {\ekvd@err@missing@definition}% + {% + \ekvd@ifalso + {% + \ekv@expargtwice{\ekvd@add@noval{#1}}% + {\ekvchangeset{#2}}% + \ekvd@assert@not@protected@also + }% + {\ekv@expargtwice{\ekvdefNoVal\ekvd@set{#1}}{\ekvchangeset{#2}}}% + }% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{noval,enoval} +% \begin{macro}[internal]{\ekvd@type@noval} +% Another pretty simple type, |noval| just needs to assert that there is a +% definition and that |long| wasn't specified. +% There are types where the difference in the variants is so small, that we +% define a common handler for them, those common handlers are named with +% |@type@|. |noval| and |enoval| are so similar that we can use such a |@type@| +% macro, even if we could've done |noval| in a slightly faster way without it. +% \begin{macrocode} +\ekvd@def@type@fwd{noval}{\ekvd@type@noval\def} +\ekvd@def@type@fwd{enoval}{\ekvd@type@noval\edef} +\protected\long\def\ekvd@type@noval#1#2#3% + {% + \ekvd@do@new{NoVal}{#2}% + \ekvd@assert@arg + \ekvd@assert@not@long + \ekvd@prot#1\ekvd@tmp{#3}% + \ekvd@ifalso + {\ekv@exparg{\ekvd@add@noval{#2}}\ekvd@tmp{}}% + {\ekvletNoVal\ekvd@set{#2}\ekvd@tmp}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{code,ecode} +% \begin{macro}[internal]{\ekvd@type@code} +% |code| is simple as well, |ecode| has to use |\edef| on a temporary macro, +% since \expkv\ doesn't provide an |\ekvedef|. +% \begin{macrocode} +\ekvd@def@type@fwd{code}{\ekvd@type@code\def} +\ekvd@def@type@fwd{ecode}{\ekvd@type@code\edef} +\protected\long\def\ekvd@type@code#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@arg + \ekvd@prot\ekvd@long#1\ekvd@tmp##1{#3}% + \ekvd@ifalso + {\ekv@exparg{\ekvd@add@val{#2}}{\ekvd@tmp{##1}}{}}% + {\ekvlet\ekvd@set{#2}\ekvd@tmp}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{default,qdefault,odefault,fdefault,edefault} +% \begin{macro}[internal] +% { +% \ekvd@type@default,\ekvd@t@default,\ekvd@t@qdefault,\ekvd@t@odefault, +% \ekvd@t@fdefault +% } +% |\ekvd@type@default| asserts there was an argument, also the key for which one +% wants to set a default has to be already defined (this is not so important for +% |default|, but |qdefault| requires it). If everything is good, |\edef| a +% temporary macro that expands |\ekvd@set| and the |\csname| for the key. +% The different expansion variants are implemented via |\ekv@unexpanded| and +% some |\expandafter|s. +% \begin{macrocode} +\ekvd@def@type@fwd{default}{\ekvd@type@default{\ekv@unexpanded\expandafter}{}} +\ekvd@def@type@fwd{odefault} + {\ekvd@type@default{\ekv@unexpanded\expandafter\expandafter\expandafter}{}} +\ekvpLet{ekvd@definekeys}{type}{qdefault}{odefault} +\ekvd@def@type@fwd{fdefault} + {\ekvd@type@default{\ekv@unexpanded\expandafter}{\romannumeral`\^^@}} +\protected\long\def\ekvd@type@default#1#2#3#4% + {% + \ekvd@assert@arg + \ekvd@do@new{NoVal}{#3}% + \ekvd@assert@not@long + \ekvifdefined\ekvd@set{#3}% + {% + \ekvd@prot\edef\ekvd@tmp + {#1{#2\csname\ekv@name\ekvd@set{#3}\endcsname{#4}}}% + \ekvd@ifalso + {\ekv@exparg{\ekvd@add@noval{#3}}\ekvd@tmp{}}% + {\ekvletNoVal\ekvd@set{#3}\ekvd@tmp}% + }% + {\ekvd@err@undefined@key{#3}}% + } +\ekvd@def@type{edefault} + {% + \ekvd@assert@arg + \ekvd@do@new{NoVal}{#1}% + \ekvd@assert@not@long + \ekvifdefined\ekvd@set{#1}% + {% + \ekvd@prot\edef\ekvd@tmp + {% + \ekv@unexpanded\ekv@expanded + {{\csname\ekv@name\ekvd@set{#1}\endcsname{#3}}}% + }% + \ekvd@ifalso + {\ekv@exparg{\ekvd@add@noval{#1}}\ekvd@tmp{}}% + {\ekvletNoVal\ekvd@set{#1}\ekvd@tmp}% + }% + {\ekvd@err@undefined@key{#1}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{initial,oinitial,finitial,einitial} +% \begin{macro}[internal]{\ekvd@type@initial} +% \begin{macrocode} +\ekvd@def@type@fwd{initial}{\ekvd@type@initial\@firstofone} +\ekvd@def@type@fwd{oinitial}{\ekvd@type@initial\ekv@exparg} +\ekvd@def@type@fwd{einitial}{\ekvd@type@initial\ekv@expandedarg} +\ekvd@def@type@fwd{finitial}{\ekvd@type@initial\ekv@fexparg} +\long\def\ekvd@type@initial#1#2#3% + {% + \ekvd@assert@not@new + \ekvd@assert@not@also + \ekvd@assert@not@long + \ekvd@assert@not@protected + \ekvd@ifnoarg + {% + \ekvifdefinedNoVal\ekvd@set{#2}% + {\csname\ekv@name\ekvd@set{#2}N\endcsname}% + {\ekvd@err@undefined@noval{#2}}% + }% + {% + \ekvifdefined\ekvd@set{#2}% + {#1{\csname\ekv@name\ekvd@set{#2}\endcsname}{#3}}% + {\ekvd@err@undefined@key{#2}}% + }% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{bool,gbool,boolTF,gboolTF,invbool,ginvbool,invboolTF,ginvboolTF} +% \begin{macro}[internal]{\ekvd@type@bool} +% The boolean types are a quicker version of a |choice| that accept |true| and +% |false|, and set up the |NoVal| action to be identical to \texttt{\key=true}. +% The |true| and |false| actions are always just |\let|ting the macro in |#7| to +% some other macro (\emph{e.g.}, \cs[no-index]{iftrue}). +% \begin{macrocode} +\ekvd@def@type@fwd{bool}{\ekvd@type@bool{}\iftrue\iffalse} +\ekvd@def@type@fwd{gbool}{\ekvd@type@bool\global\iftrue\iffalse} +\ekvd@def@type@fwd{boolTF}{\ekvd@type@bool{}\@firstoftwo\@secondoftwo} +\ekvd@def@type@fwd{gboolTF}{\ekvd@type@bool\global\@firstoftwo\@secondoftwo} +\ekvd@def@type@fwd{invbool}{\ekvd@type@bool{}\iffalse\iftrue} +\ekvd@def@type@fwd{ginvbool}{\ekvd@type@bool\global\iffalse\iftrue} +\ekvd@def@type@fwd{invboolTF}{\ekvd@type@bool{}\@secondoftwo\@firstoftwo} +\ekvd@def@type@fwd{ginvboolTF}{\ekvd@type@bool\global\@secondoftwo\@firstoftwo} +\protected\def\ekvd@type@bool#1#2#3#4#5% + {% + \ekvd@do@new{}{#4}% + \ekvd@do@new{NoVal}{#4}% + \ekvd@assert@filledarg{#5}% + \ekvd@newlet#5#3% + \ekvd@type@choice{#4}% + \protected\ekvdefNoVal\ekvd@set{#4}{#1\let#5#2}% + \protected\expandafter\def + \csname\ekvd@choice@name\ekvd@set{#4}{true}\endcsname + {#1\let#5#2}% + \protected\expandafter\def + \csname\ekvd@choice@name\ekvd@set{#4}{false}\endcsname + {#1\let#5#3}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{boolpair,gboolpair,boolpairTF,gboolpairTF} +% \begin{macro}[internal]{\ekvd@type@boolpair} +% The boolean pair types are essentially the same as the boolean types, but set +% two macros instead of one. +% \begin{macrocode} +\ekvd@def@type{boolpair} + {\ekvd@assert@twoargs{#3}\ekvd@type@boolpair{}\iftrue\iffalse{#1}#3} +\ekvd@def@type{gboolpair} + {\ekvd@assert@twoargs{#3}\ekvd@type@boolpair\global\iftrue\iffalse{#1}#3} +\ekvd@def@type{boolpairTF} + {\ekvd@assert@twoargs{#3}\ekvd@type@boolpair{}\@firstoftwo\@secondoftwo{#1}#3} +\ekvd@def@type{gboolpairTF} + {% + \ekvd@assert@twoargs{#3}% + \ekvd@type@boolpair\global\@firstoftwo\@secondoftwo{#1}#3% + } +\protected\def\ekvd@type@boolpair#1#2#3#4#5#6% + {% + \ekvd@do@new{}{#4}% + \ekvd@do@new{NoVal}{#4}% + \ekvd@newlet#5#3% + \ekvd@newlet#6#2% + \ekvd@type@choice{#4}% + \protected\ekvdefNoVal\ekvd@set{#4}{#1\let#5#2#1\let#6#3}% + \protected\expandafter\def + \csname\ekvd@choice@name\ekvd@set{#4}{true}\endcsname + {#1\let#5#2#1\let#6#3}% + \protected\expandafter\def + \csname\ekvd@choice@name\ekvd@set{#4}{false}\endcsname + {#1\let#5#3#1\let#6#2}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{data,gdata,dataT,gdataT} +% \begin{macro}[internal]{\ekvd@type@data} +% \begin{macrocode} +\ekvd@def@type@fwd{data} + {\ekvd@type@data\@secondoftwo\edef{####2}{\ekv@unexpanded{##1}}} +\ekvd@def@type@fwd{edata} + {% + \ekvd@type@data\@secondoftwo\edef + {####2}{\ekv@unexpanded\ekv@expanded{{##1}}}% + } +\ekvd@def@type@fwd{gdata} + {\ekvd@type@data\@secondoftwo\xdef{####2}{\ekv@unexpanded{##1}}} +\ekvd@def@type@fwd{xdata} + {% + \ekvd@type@data\@secondoftwo\xdef + {####2}{\ekv@unexpanded\ekv@expanded{{##1}}}% + } +\ekvd@def@type@fwd{dataT}{\ekvd@type@data\@gobble\edef{}{\ekv@unexpanded{##1}}} +\ekvd@def@type@fwd{edataT} + {\ekvd@type@data\@gobble\edef{}{\ekv@unexpanded\ekv@expanded{{##1}}}} +\ekvd@def@type@fwd{gdataT}{\ekvd@type@data\@gobble\xdef{}{\ekv@unexpanded{##1}}} +\ekvd@def@type@fwd{xdataT} + {\ekvd@type@data\@gobble\xdef{}{\ekv@unexpanded\ekv@expanded{{##1}}}} +\protected\def\ekvd@type@data#1#2#3#4#5#6% + {% + \ekvd@do@new{}{#5}% + \ekvd@assert@filledarg{#6}% + \ekvd@newlet#6#1% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#5}{\long#2#6####1#3{####1{#4}}}{}% + }% + {% + \protected\ekvd@long\ekvdef\ekvd@set{#5}% + {\long#2#6####1#3{####1{#4}}}% + }% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{box,gbox} +% \begin{macro}[internal]{\ekvd@type@box} +% Set up our boxes. Though we're a generic package we want to be colour safe, so +% we put an additional grouping level inside the box contents, for the case that +% someone uses \pkg{color}. |\ekvd@newreg| is a small wrapper which tests +% whether the first argument is defined and if not does +% |\csname new#2\endcsname#1|. +% \begin{macrocode} +\ekvd@def@type@fwd{box}{\ekvd@type@box{}} +\ekvd@def@type@fwd{gbox}{\ekvd@type@box\global} +\protected\def\ekvd@type@box#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@filledarg{#3}% + \ekvd@newreg#3{box}% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#2}{#1\setbox#3=\hbox{\begingroup##1\endgroup}}{}% + }% + {% + \protected\ekvd@long\ekvdef\ekvd@set{#2}% + {#1\setbox#3=\hbox{\begingroup##1\endgroup}}% + }% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{toks,gtoks} +% \begin{macro}[internal]{\ekvd@type@toks} +% Similar to |box|, but set the |toks|. +% \begin{macrocode} +\ekvd@def@type@fwd{toks}{\ekvd@type@toks{}} +\ekvd@def@type@fwd{gtoks}{\ekvd@type@toks\global} +\protected\def\ekvd@type@toks#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@filledarg{#3}% + \ekvd@newreg#3{toks}% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#2}{#1#3={##1}}{}% + }% + {\protected\ekvd@long\ekvdef\ekvd@set{#2}{#1#3={##1}}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvd@type@preapptoks,\ekvd@t@apptoks,\ekvd@t@gapptoks} +% Just like |toks|, but expand the current contents of the |toks| register to +% append the new contents. +% \begin{macrocode} +\ekvd@ifprimitive\toksapp + {% + \ekvd@def@type@fwd{apptoks}{\ekvd@type@preapptoks\toksapp} + \ekvd@def@type@fwd{gapptoks}{\ekvd@type@preapptoks\gtoksapp} + \ekvd@def@type@fwd{pretoks}{\ekvd@type@preapptoks\tokspre} + \ekvd@def@type@fwd{gpretoks}{\ekvd@type@preapptoks\gtokspre} + \protected\def\ekvd@type@preapptoks#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@filledarg{#3}% + \ekvd@newreg#3{toks}% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#2}{#1#3{##1}}{}% + }% + {\protected\ekvd@long\ekvdef\ekvd@set{#2}{#1#3{##1}}}% + } + } + {% + \ekvd@def@type@fwd{apptoks}{\ekvd@type@apptoks{}} + \ekvd@def@type@fwd{gapptoks}{\ekvd@type@apptoks\global} + \protected\def\ekvd@type@apptoks#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@filledarg{#3}% + \ekvd@newreg#3{toks}% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#2}{#1#3=\expandafter{\the#3##1}}{}% + }% + {% + \protected\ekvd@long\ekvdef\ekvd@set{#2}% + {#1#3=\expandafter{\the#3##1}}% + }% + } + \ekvd@def@type@fwd{pretoks}{\ekvd@type@pretoks{}} + \ekvd@def@type@fwd{gpretoks}{\ekvd@type@pretoks\global} + \newtoks\ekvd@toks + \protected\def\ekvd@type@pretoks#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@filledarg{#3}% + \ekvd@newreg#3{toks}% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#2}% + {#1#3=\ekv@expanded{{\ekv@unexpanded{##1}\the#3}}}% + {}% + }% + {% + \protected\ekvd@long\ekvdef\ekvd@set{#2}% + {#1#3=\ekv@expanded{{\ekv@unexpanded{##1}\the#3}}}% + }% + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% {int,eint,gint,xint,dimen,edimen,gdimen,xdimen,skip,eskip,gskip,xskip} +% \begin{macro}[internal]{\ekvd@type@register} +% The |\ekvd@type@register| can handle all the types for which the assignment will +% just be \texttt{\meta{register}=\meta{value}}. +% \begin{macrocode} +\ekvd@def@type@fwd {int}{\ekvd@type@register{count}{}{}} +\ekvd@def@type@fwd{eint}{\ekvd@type@register{count}{}\numexpr} +\ekvd@def@type@fwd{gint}{\ekvd@type@register{count}\global{}} +\ekvd@def@type@fwd{xint}{\ekvd@type@register{count}\global\numexpr} +\ekvd@def@type@fwd {dimen}{\ekvd@type@register{dimen}{}{}} +\ekvd@def@type@fwd{edimen}{\ekvd@type@register{dimen}{}\dimexpr} +\ekvd@def@type@fwd{gdimen}{\ekvd@type@register{dimen}\global{}} +\ekvd@def@type@fwd{xdimen}{\ekvd@type@register{dimen}\global\dimexpr} +\ekvd@def@type@fwd {skip}{\ekvd@type@register{skip}{}{}} +\ekvd@def@type@fwd{eskip}{\ekvd@type@register{skip}{}\glueexpr} +\ekvd@def@type@fwd{gskip}{\ekvd@type@register{skip}\global{}} +\ekvd@def@type@fwd{xskip}{\ekvd@type@register{skip}\global\glueexpr} +\protected\def\ekvd@type@register#1#2#3#4#5% + {% + \ekvd@do@new{}{#4}% + \ekvd@assert@filledarg{#5}% + \ekvd@newreg#5{#1}% + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#4}{#2#5=#3##1\relax}{}% + }% + {\protected\ekvd@long\ekvdef\ekvd@set{#4}{#2#5=#3##1\relax}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{store,gstore,estore,xstore} +% \begin{macro}[internal]{\ekvd@type@store} +% The none-expanding |store| types use an |\edef| or |\xdef| and |\unexpanded| +% to be able to also store |#| easily. +% \begin{macrocode} +\ekvd@def@type@fwd{store}{\ekvd@type@store\edef{\ekv@unexpanded{##1}}} +\ekvd@def@type@fwd{gstore}{\ekvd@type@store\xdef{\ekv@unexpanded{##1}}} +\ekvd@def@type@fwd{estore} + {\ekvd@type@store\edef{\ekv@unexpanded\ekv@expanded{{##1}}}} +\ekvd@def@type@fwd{xstore} + {\ekvd@type@store\xdef{\ekv@unexpanded\ekv@expanded{{##1}}}} +\protected\def\ekvd@type@store#1#2#3#4% + {% + \ekvd@do@new{}{#3}% + \ekvd@assert@filledarg{#4}% + \ekvd@newlet#4\ekv@empty + \ekvd@ifalso + {% + \let\ekvd@prot\protected + \ekvd@add@val{#3}{#1#4{#2}}{}% + }% + {\protected\ekvd@long\ekvdef\ekvd@set{#3}{#1#4{#2}}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{meta,nmeta} +% \begin{macro}[internal]{\ekvd@type@meta} +% |meta| sets up things such that another instance of |\ekvset| will be run on +% the argument, with the same \set. +% \begin{macrocode} +\ekvd@def@type@fwd{meta} + {\ekvd@type@meta{}{##1}\ekvd@add@val{{##1}}{}\ekv@exparg} +\ekvd@def@type@fwd{nmeta} + {% + \ekvd@assert@not@long + \ekvd@type@meta + {NoVal}{}\ekvd@add@noval{}\ekvd@assert@not@long@also\ekv@expargtwice + } +\protected\long\def\ekvd@type@meta#1#2#3#4#5#6#7#8% + {% + \ekvd@do@new{#1}{#7}% + \ekvd@assert@filledarg{#8}% + #6{\ekvd@prot\ekvd@long\def\ekvd@tmp#2}{\ekvmorekv{#8}}% + \ekvd@ifalso + {\ekv@exparg{#3{#7}}{\ekvd@tmp#4}{#5}}% + {\csname ekvlet#1\endcsname\ekvd@set{#7}\ekvd@tmp}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{smeta,snmeta} +% \begin{macro}[internal] +% {\ekvd@type@smeta,\ekvd@type@smeta@} +% |smeta| is pretty similar to |meta|, but needs two arguments inside of \val, +% such that the first is the \set\ for which the sub-|\ekvset| and the second is +% the \kv\ list. +% \begin{macrocode} +\ekvd@def@type@fwd{smeta}{\ekvd@type@smeta{}{##1}\ekvd@add@val{{##1}}{}} +\ekvd@def@type@fwd{snmeta} + {% + \ekvd@assert@not@long + \ekvd@type@smeta{NoVal}{}\ekvd@add@noval{}\ekvd@assert@not@long@also + } +\protected\long\def\ekvd@type@smeta#1#2#3#4#5#6#7% + {% + \ekvd@do@new{#1}{#6}% + \ekvd@assert@twoargs{#7}% + \ekvd@type@smeta@#7{#2}% + \ekvd@ifalso + {\ekv@exparg{#3{#6}}{\ekvd@tmp#4}{#5}}% + {\csname ekvlet#1\endcsname\ekvd@set{#6}\ekvd@tmp}% + } +\protected\long\def\ekvd@type@smeta@#1#2#3% + {\ekvd@prot\ekvd@long\ekv@expargtwice{\def\ekvd@tmp#3}{\ekvset{#1}{#2}}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{choice} +% \begin{macro}[internal]{\ekvd@type@choice,\ekvd@populate@choice} +% The real key definition of a |choice| type is pretty simple, the heavy +% lifting is done by a helper macro at run time. Though setting up the choices +% needs a bit of work. First the key macro definition. +% \begin{macrocode} +\protected\def\ekvd@type@choice#1% + {% + \ekvd@assert@not@long + \ekv@expargtwice{\ekvd@prot\def\ekvd@tmp##1}% + {% + \expandafter\expandafter\expandafter + \ekvd@h@choice + \expandafter\expandafter\expandafter + {\expandafter\ekvd@choice@name\expandafter{\ekvd@set}{#1}{##1}}% + }% + \ekvd@ifalso + {% + \ekvd@assert@val{#1}% + \ekvd@if@not@already@choice{#1}% + {% + \ekv@exparg + {% + \expandafter\ekvd@add@aux + \csname\ekv@name\ekvd@set{#1}\endcsname{{##1}}{#1}% + }% + {\ekvd@tmp{##1}}% + {\ekvd@long\ekvdef}\ekvd@assert@not@long@also + }% + }% + {\ekvlet\ekvd@set{#1}\ekvd@tmp}% + } +% \end{macrocode} +% The set up of different choices is done through another parser, that one +% needs relatively few types though. +% \begin{macrocode} +\ekvpNewParser{ekvd@populate@choice} +\ekvpValueAlwaysRequired{ekvd@populate@choice} +\ekvpDefType{ekvd@populate@choice}{protect} + {\ekvd@populate@choice\protected{#1}{#3}} +\ekvpDefType{ekvd@populate@choice}{unprotect}{\ekvd@populate@choice{}{#1}{#3}} +\ekvpDefNoType{ekvd@populate@choice} {\ekvd@populate@choice{}{#1}{#3}} +\ekvpLet{ekvd@populate@choice}{type}{protected}{protect} +\ekvpLet{ekvd@populate@choice}{type}{unprotected}{unprotect} +% \end{macrocode} +% The choice definition is done by the following little helper: +% \begin{macrocode} +\protected\long\def\ekvd@populate@choice#1#2#3% + {% + #1\expandafter\edef + \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname + {\ekv@unexpanded{#3}}% + } +% \end{macrocode} +% And we define the real type: +% \begin{macrocode} +\ekvd@def@type{choice} + {% + \ekvd@do@new{}{#1}% + \ekvd@assert@arg + \ekvd@type@choice{#1}% + \def\ekvd@set@choice{#1}% + \ekvpParse@unsafe\ekvp@@p@ekvd@populate@choice{#3}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{choice-store,choice-enum} +% These two types define a special kind of |choice| key and are quite similar, +% the only difference is what the different choices do (hence they use a +% shared initialisation which differs in the chosen |populate| step). +% \begin{macrocode} +\ekvd@def@type@fwd{choice-store} + {\ekvd@type@choicespecial\ekvd@populate@choicestore} +\ekvd@def@type@fwd{choice-enum} + {\ekvd@type@choicespecial\ekvd@populate@choiceenum} +% \end{macrocode} +% \begin{macro}[internal]{\ekvd@type@choicespecial} +% Initialise similar to a |choice| key. The difference is that we require two +% arguments (which we assert), a macro to store things in, and a |csv|-list +% containing the allowed values. |#1| is the |populate| macro according to the +% type used. +% \begin{macrocode} +\protected\long\def\ekvd@type@choicespecial#1#2#3% + {% + \ekvd@do@new{}{#2}% + \ekvd@assert@twoargs{#3}% + \ekvd@type@choice{#2}% + \def\ekvd@set@choice{#2}% + #1#3% + } +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% {\ekvd@populate@choicestore,\ekvd@populate@choicestore@} +% We initialise the storing macro if it doesn't yet exist, and then we loop +% over the value list. The |\edef|s with |\unexpanded| are both necessary to +% be able to store macro parameter tokens (the outer protects at define time, +% the inner at use time). +% \begin{macrocode} +\protected\long\def\ekvd@populate@choicestore#1% + {% + \ekvd@newlet#1\ekv@empty + \ekvparse{\ekvd@populate@choicestore@k#1}{\ekvd@populate@choicestore@kv#1}% + } +\protected\long\def\ekvd@populate@choicestore@kv#1#2#3% + {% + \protected\expandafter\edef + \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname + {\ekv@unexpanded{\edef#1{\ekv@unexpanded{#3}}}}% + } +\ekv@exparg{\protected\long\def\ekvd@populate@choicestore@k#1#2}% + {\ekvd@populate@choicestore@kv{#1}{#2}{#2}} +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% {\ekvd@populate@choiceenum,\ekvd@populate@choiceenum@} +% This is similar to the population of a |choice-store| type, but instead of +% storing the values in a macro this initialises a count and stores the +% position of the value in the list inside that count (zero-indexed). The +% space is necessary to terminate the number scanning, which is the reason we +% use |\@firstofone| (so that the space after the macro name isn't gobbled by +% \TeX). +% \begin{macrocode} +\protected\long\def\ekvd@populate@choiceenum#1% + {% + \ekvd@newreg#1{count}% + \def\ekvd@tmp{0}% + \ekvcsvloop{\ekvd@populate@choiceenum@#1}% + } +\protected\long\def\ekvd@populate@choiceenum@#1#2% + {% + \protected\expandafter\edef + \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname + {#1=\@firstofone{\ekvd@tmp} }% + \edef\ekvd@tmp{\the\numexpr\ekvd@tmp+1\relax}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{unknown-choice} +% \begin{macrocode} +\ekvd@def@type{unknown-choice} + {% + \ekvd@do@new@for@name{\ekvd@unknown@choice@name\ekvd@set{#1}}% + \ekvd@assert@arg + \ekvd@assert@not@long + \ekvd@assert@not@also + \ekvd@prot\expandafter + \def\csname\ekvd@unknown@choice@name\ekvd@set{#1}\endcsname##1{#3}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{unknown} +% \begin{macro}[internal]{\ekvd@type@unknown@code,\ekvd@type@unknown@noval} +% The |unknown| type has different subtypes which would be the key names for +% other types. It is first checked whether that subtype is defined, if it +% isn't throw an error, else use that subtype. +% \begin{macrocode} +\ekvd@def@type{unknown} + {% + \ekv@ifdefined{ekvd@type@unknown@\detokenize{#1}}% + {\csname ekvd@type@unknown@\detokenize{#1}\endcsname{#3}}% + \ekvd@err@misused@unknown + } +% \end{macrocode} +% The |unknown noval| type can use |\ekvdefunknownNoVal| directly (after +% asserting some prefixes). +% \begin{macrocode} +\protected\long\def\ekvd@type@unknown@noval#1% + {% + \ekvd@do@new@for@name{\ekv@name\ekvd@set{}uN}% + \ekvd@assert@arg + \ekvd@assert@not@also + \ekvd@prot\ekvd@long\ekvdefunknownNoVal\ekvd@set{#1}% + } +% \end{macrocode} +% The |unknown code| type uses some trickery during the definition in order to +% swap out |#1| and |#2| in the user supplied definition. This is done via a +% temporary macro that stores the definition but gets the parameter numbers +% reversed while the real definition is done. +% \begin{macrocode} +\protected\long\def\ekvd@type@unknown@code#1% + {% + \ekvd@do@new@for@name{\ekv@name\ekvd@set{}u}% + \ekvd@assert@arg + \ekvd@assert@not@also + \begingroup + \def\ekvd@tmp##1##2##3{#1}% + \ekv@exparg + {% + \endgroup + \ekvd@prot\ekvd@long\ekvdefunknown\ekvd@set + }% + {\ekvd@tmp{##2}{##1}{##3}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvd@type@unknown@redirect, +% \ekvd@type@unknown@redirect-code, +% \ekvd@type@unknown@redirect-noval +% } +% The |unknown redirect| types also just forward to |\ekvredirectunknown| +% after asserting some prefixes. +% \begin{macrocode} +\protected\edef\ekvd@type@unknown@redirect#1% + {% + \expandafter\noexpand\csname ekvd@type@unknown@redirect-code\endcsname{#1}% + \expandafter\noexpand\csname ekvd@type@unknown@redirect-noval\endcsname{#1}% + } +\protected\expandafter\def\csname ekvd@type@unknown@redirect-code\endcsname#1% + {% + \ekvd@do@new@for@name{\ekv@name\ekvd@set{}u}% + \ekvd@assert@arg + \ekvd@assert@not@also + \ekvd@assert@not@protected + \expandafter\ekvredirectunknown\expandafter{\ekvd@set}{#1}% + } +\protected\expandafter\def\csname ekvd@type@unknown@redirect-noval\endcsname#1% + {% + \ekvd@do@new@for@name{\ekv@name\ekvd@set{}uN}% + \ekvd@assert@arg + \ekvd@assert@not@also + \ekvd@assert@not@protected + \expandafter\ekvredirectunknownNoVal\expandafter{\ekvd@set}{#1}% + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Key Type Helpers} +% There are some keys that might need helpers during their execution (not during +% their definition, which are gathered as |@type@| macros). These helpers are +% named |@h@|. +% +% \begin{macro}[internal]{\ekvd@h@choice,\ekvd@h@choice@} +% The |choice| helper will just test whether the given choice was defined, if +% not throw an error expandably, else call the macro which stores the code for +% this choice. +% \begin{macrocode} +\def\ekvd@h@choice#1% + {% + \expandafter\ekvd@h@choice@ + \csname\ifcsname#1\endcsname#1\else relax\fi\endcsname + {#1}% + } +\def\ekvd@h@choice@#1#2% + {% + \ifx#1\relax + \ekvd@err@choice@invalid{#2}% + \expandafter\@gobble + \fi + #1% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Handling \texttt{also}} +% +% \begin{macro}[internal] +% {\ekvd@add@val,\ekvd@add@noval,\ekvd@add@aux,\ekvd@add@aux@} +% \begin{macrocode} +\protected\long\def\ekvd@add@val#1#2#3% + {% + \ekvd@assert@val{#1}% + \expandafter\ekvd@add@aux\csname\ekv@name\ekvd@set{#1}\endcsname{{##1}}% + {#1}{#2}{\ekvd@long\ekvdef}{#3}% + } +\protected\long\def\ekvd@add@noval#1#2#3% + {% + \ekvd@assert@noval{#1}% + \expandafter\ekvd@add@aux\csname\ekv@name\ekvd@set{#1}N\endcsname{}% + {#1}{#2}\ekvdefNoVal{#3}% + } +\protected\long\def\ekvd@add@aux#1#2% + {% + \ekvd@extract@prefixes#1% + \expandafter\ekvd@add@aux@\expandafter{#1#2}% + } +% \end{macrocode} +% Once we're done with adding something to the definition of a key we need to +% clear the prefixes. Maybe it would be better to only restore them if they +% were changed by the |\ekvd@extract@prefixes| mechanism, but I think this is +% fine with just resetting all (there should be no code dependent on any of +% the prefix storing macros after this was run). +% \begin{macrocode} +\protected\long\def\ekvd@add@aux@#1#2#3#4#5% + {% + #5% + \ekvd@prot#4\ekvd@set{#2}{#1#3}% + \ekvd@clear@prefixes + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvd@extract@prefixes,\ekvd@extract@prefixes@, +% \ekvd@extract@prefixes@long,\ekvd@extract@prefixes@prot +% } +% This macro checks which prefixes were used for the definition of a macro and +% sets |\ekvd@long| and |\ekvd@prot| accordingly. +% \begin{macrocode} +\protected\def\ekvd@extract@prefixes#1% + {% + \expandafter\ekvd@extract@prefixes@\meaning#1\ekvd@stop + } +% \end{macrocode} +% In the following definition |#1| will get replaced by |macro:|, |#2| by +% |\long| and |#3| by |\protected| (in each, all tokens will have category +% other). This allows us to parse the |\meaning| of a macro for those strings. +% \begin{macrocode} +\protected\def\ekvd@extract@prefixes@#1#2#3% + {% + \protected\def\ekvd@extract@prefixes@##1#1##2\ekvd@stop + {% + \ekvd@extract@prefixes@long + ##1\ekvd@mark\@firstofone#2\ekvd@mark\@gobble\ekvd@stop + {\let\ekvd@long\long}% + \ekvd@extract@prefixes@prot + ##1\ekvd@mark\@firstofone#3\ekvd@mark\@gobble\ekvd@stop + {\let\ekvd@prot\protected}% + }% + \protected\def\ekvd@extract@prefixes@long##1#2##2\ekvd@mark##3##4\ekvd@stop + {##3}% + \protected\def\ekvd@extract@prefixes@prot##1#3##2\ekvd@mark##3##4\ekvd@stop + {##3}% + } +% \end{macrocode} +% We use a temporary macro to expand the three arguments of +% |\ekvd@extract@prefixes@|, which will set up the real meaning of itself and +% the parsing for |\long| and |\protected|. +% \begin{macrocode} +\begingroup +\edef\ekvd@tmp + {% + \endgroup + \ekvd@extract@prefixes@ + {\detokenize{macro:}}% + {\string\long}% + {\string\protected}% + } +\ekvd@tmp +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Tests} +% +% \begin{macro}[internal]{\ekvd@newlet,\ekvd@newreg} +% These macros test whether a control sequence is defined, if it isn't they +% define it, either via |\let| or via the correct \cs[no-index]{new\meta{reg}}. +% \begin{macrocode} +\protected\def\ekvd@newlet#1#2% + {% + \ifdefined#1% + \ifx#1\relax\ekv@fi@gobble\fi\@firstofone + \ekv@fi@gobble + \fi + \@firstofone{\let#1#2}% + } +\protected\def\ekvd@newreg#1#2% + {% + \ifdefined#1% + \ifx#1\relax\ekv@fi@gobble\fi\@firstofone + \ekv@fi@gobble + \fi + \@firstofone{\csname new#2\endcsname#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvd@assert@twoargs,\ekvd@ifnottwoargs,\ekvd@ifempty@gtwo} +% A test for exactly two tokens can be reduced for an empty-test after gobbling +% two tokens, in the case that there are fewer tokens than two in the argument, +% only macros will be gobbled that are needed for the true branch, which doesn't +% hurt, and if there are more this will not be empty. +% \begin{macrocode} +\long\def\ekvd@assert@twoargs#1% + {\ekvd@ifnottwoargs{#1}{\ekvd@err@missing@definition\ekvpGobbleT}{}} +\long\def\ekvd@ifnottwoargs#1% + {% + \ekvd@ifempty@gtwo#1\ekv@ifempty@B + \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo + } +\long\def\ekvd@ifempty@gtwo#1#2{\ekv@ifempty@\ekv@ifempty@A} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvd@assert@val,\ekvd@assert@val@,\ekvd@assert@noval,\ekvd@assert@noval@, +% \ekvd@extract@args,\ekvd@extracted@args,\ekvd@one@arg@string +% } +% Assert that a given key is defined as a value taking key or a |NoVal| key with +% the correct argument structure, respectively. +% \begin{macrocode} +\protected\def\ekvd@assert@val#1% + {% + \ekvifdefined\ekvd@set{#1}% + {\expandafter\ekvd@assert@val@\csname\ekv@name\ekvd@set{#1}\endcsname}% + {% + \ekvifdefinedNoVal\ekvd@set{#1}% + \ekvd@err@add@val@on@noval + {\ekvd@err@undefined@key{#1}}% + \ekvpGobbleT + }% + } +\protected\def\ekvd@assert@val@#1% + {% + \expandafter\ekvd@extract@args\meaning#1\ekvd@stop + \unless\ifx\ekvd@extracted@args\ekvd@one@arg@string + \ekvd@err@unsupported@arg + \expandafter\ekvpGobbleT + \fi + }% +\protected\def\ekvd@assert@noval#1% + {% + \ekvifdefinedNoVal\ekvd@set{#1}% + {\expandafter\ekvd@assert@noval@\csname\ekv@name\ekvd@set{#1}N\endcsname}% + {% + \ekvifdefined\ekvd@set{#1}% + \ekvd@err@add@noval@on@val + {\ekvd@err@undefined@key{#1}}% + \ekvpGobbleT + }% + } +\protected\def\ekvd@assert@noval@#1% + {% + \expandafter\ekvd@extract@args\meaning#1\ekvd@stop + \unless\ifx\ekvd@extracted@args\ekv@empty + \ekvd@err@unsupported@arg + \expandafter\ekvpGobbleT + \fi + } +\protected\def\ekvd@extract@args#1% + {% + \protected\def\ekvd@extract@args##1#1##2->##3\ekvd@stop + {\def\ekvd@extracted@args{##2}}% + } +\expandafter\ekvd@extract@args\expandafter{\detokenize{macro:}} +\edef\ekvd@one@arg@string{\string#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvd@assert@arg,\ekvd@ifnoarg} +% The |\ekvd@ifnoarg| macro is initialised as |\@secondoftwo|. Each time a key +% without an argument is encountered it will be set to |\@firstoftwo| for the +% scope of that key's parsing. +% \begin{macrocode} +\def\ekvd@assert@arg{\ekvd@ifnoarg{\ekvd@err@missing@definition\ekvpGobbleT}{}} +\let\ekvd@ifnoarg\@secondoftwo +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvd@assert@filledarg,\ekvd@ifnoarg@or@empty} +% \begin{macrocode} +\long\def\ekvd@assert@filledarg#1% + {\ekvd@ifnoarg@or@empty{#1}{\ekvd@err@missing@definition\ekvpGobbleT}{}} +\long\def\ekvd@ifnoarg@or@empty#1% + {% + \ekvd@ifnoarg + \@firstoftwo + {\ekv@ifempty{#1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvd@assert@not@long,\ekvd@assert@not@protected,\ekvd@assert@not@also, +% \ekvd@assert@not@long@also,\ekvd@assert@not@protected@also, +% \ekvd@assert@new,\ekvd@assert@not@new +% } +% Some key-types don't want to be |also|, |\long| or |\protected|, so we provide +% macros to test this and throw an error, this could be silently ignored but now +% users will learn to not use unnecessary stuff which slows the compilation +% down. +% \begin{macrocode} +\def\ekvd@assert@not@long{\ifx\ekvd@long\long\ekvd@err@no@prefix{long}\fi} +\def\ekvd@assert@not@protected + {\ifx\ekvd@prot\protected\ekvd@err@no@prefix{protected}\fi} +\def\ekvd@assert@not@also{\ekvd@ifalso{\ekvd@err@no@prefix{also}}{}} +\def\ekvd@assert@not@long@also + {\ifx\ekvd@long\long\ekvd@err@no@prefix@also{long}\fi} +\def\ekvd@assert@not@protected@also + {\ifx\ekvd@prot\protected\ekvd@err@no@prefix@also{protected}\fi} +\def\ekvd@assert@new#1#2% + {% + \csname ekvifdefined#1\endcsname\ekvd@set{#2}% + {\ekvd@err@not@new\ekvpGobbleT}% + {}% + } +\def\ekvd@assert@not@new + {\ifx\ekvd@do@new\ekvd@assert@new\ekvd@err@no@prefix{new}\fi} +\def\ekvd@do@new@for@name#1% + {% + \ifx\ekvd@do@new\ekvd@assert@new + \ekv@fi@firstofone + \fi + \@gobble{\ekv@ifdefined{#1}{\ekvd@err@not@new\ekvpGobbleT}{}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvd@if@not@already@choice, \ekvd@if@not@already@choice@a, +% \ekvd@if@not@already@choice@b +% } +% It is bad to use |also| on a key that already contains a |choice|, as both +% choices would share the same valid values and thus lead to each callback being +% used twice. The following is a rudimentary test against this. +% \begin{macrocode} +\protected\def\ekvd@if@not@already@choice#1% + {% + \expandafter\ekvd@if@not@already@choice@a + \csname\ekv@name\ekvd@set{#1}\endcsname + {}\ekvd@h@choice\ekvd@stop + } +\protected\def\ekvd@if@not@already@choice@a + {% + \expandafter\ekvd@if@not@already@choice@b + } +\long\protected\def\ekvd@if@not@already@choice@b#1\ekvd@h@choice#2\ekvd@stop + {% + \ekv@ifempty{#2}\@firstofone\@gobble + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvd@ifspace,\ekvd@ifspace@} +% Yet another test which can be reduced to an if-empty, this time by gobbling +% everything up to the first space. +% \begin{macrocode} +\long\def\ekvd@ifspace#1% + {% + \ekvd@ifspace@#1 \ekv@ifempty@B + \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo + } +\long\def\ekvd@ifspace@#1 % keep this space + {% + \ekv@ifempty@\ekv@ifempty@A + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Messages} +% +% Most messages of \expkvd\ are not expandable, since they only appear during +% key-definition, which is not expandable anyway. +% +% \begin{macro}[internal] +% { +% \ekvd@errm,\ekvd@err@missing@definition, +% \ekvd@err@missing@type,\ekvd@err@undefined@prefix,\ekvd@err@undefined@key, +% \ekvd@err@no@prefix,\ekvd@err@no@prefix@also, +% \ekvd@err@add@val@on@noval,\ekvd@err@add@noval@on@val, +% \ekvd@err@unsupported@arg,\ekvd@err@not@new +% } +% The non-expandable error messages are boring, so here they are: +% \begin{macrocode} +\protected\long\def\ekvd@errm#1{\errmessage{expkv-def Error: #1}} +\protected\def\ekvd@err@missing@definition + {\ekvd@errm{Missing definition for key `\ekvd@cur'}} +\protected\def\ekvd@err@missing@type + {\ekvd@errm{Missing type prefix for key `\ekvd@cur'}} +\protected\def\ekvd@err@undefined@prefix#1% + {% + \ekvd@errm + {% + Undefined prefix `\ekv@unexpanded{#1}' found while processing + `\ekvd@cur'% + }% + } +\protected\def\ekvd@err@undefined@key#1% + {% + \ekvd@errm + {Undefined key `\ekv@unexpanded{#1}' found while processing `\ekvd@cur'}% + } +\protected\def\ekvd@err@undefined@noval#1% + {% + \ekvd@errm + {% + Undefined noval key `\ekv@unexpanded{#1}' found while processing + `\ekvd@cur'% + }% + } +\protected\def\ekvd@err@no@prefix#1% + {\ekvd@errm{prefix `#1' not accepted in `\ekvd@cur'}} +\protected\def\ekvd@err@no@prefix@also#1% + {\ekvd@errm{`\ekvd@cur' not allowed with a `#1' key}} +\protected\def\ekvd@err@add@val@on@noval + {\ekvd@errm{`\ekvd@cur' not allowed with a NoVal key}} +\protected\def\ekvd@err@add@noval@on@val + {\ekvd@errm{`\ekvd@cur' not allowed with a value taking key}} +\protected\def\ekvd@err@unsupported@arg + {% + \ekvd@errm + {% + Existing key-macro has the unsupported argument string + `\ekvd@extracted@args' for key `\ekvd@cur'% + }% + } +\protected\def\ekvd@err@not@new + {\ekvd@errm{The key for `\ekvd@cur' is already defined}} +\protected\long\def\ekvd@err@misused@unknown + {\ekvd@errm{Misuse of the unknown type found while processing `\ekvd@cur'}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvd@err@choice@invalid,\ekvd@err@choice@invalid@,\ekvd@choice@name, +% \ekvd@unknown@choice@name +% } +% |\ekvd@err@choice@invalid| will have to use this mechanism to throw its +% message. Also we have to retrieve the name parts of the choice in an easy way, +% so we use parentheses of catcode 8 here, which should suffice in most cases to +% allow for a correct separation. +% \begin{macrocode} +\def\ekvd@err@choice@invalid#1% + {% + \ekvd@err@choice@invalid@#1% + } +\begingroup +\catcode40=8 +\catcode41=8 +\@firstofone{\endgroup +\def\ekvd@choice@name#1#2#3% + {% + ekvd#1(#2)\detokenize{#3}% + } +\def\ekvd@unknown@choice@name#1#2% + {% + ekvd:u:#1(#2)% + } +\def\ekvd@err@choice@invalid@ ekvd#1(#2)\detokenize#3% + {% + \ekv@ifdefined{\ekvd@unknown@choice@name{#1}{#2}}% + {\csname\ekvd@unknown@choice@name{#1}{#2}\endcsname{#3}}% + {\ekvd@err{invalid choice `#3' for `#2' in set `#1'}}% + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvd@err} +% The expandable error messages use |\ekvd@err|, which is just like |\ekv@err| +% from \expkv. It uses a runaway argument to start the error message. +% \begin{macrocode} +\ekv@exparg{\long\def\ekvd@err#1}{\ekverr{expkv-def}{#1}} +% \end{macrocode} +% \end{macro} +% +% Now everything that's left is to reset the category code of |@|. +% \begin{macrocode} +\catcode`\@=\ekvd@tmp +% \end{macrocode} +% +% \gobbledocstriptag +%</tex> +%^^A=<< diff --git a/macros/generic/expkv-opt/expkv-opt-2020-10-10.dtx b/macros/generic/expkv-bundle/expkv-opt-2020-10-10.dtx index ab2dcec41a..2bc84dafaa 100644 --- a/macros/generic/expkv-opt/expkv-opt-2020-10-10.dtx +++ b/macros/generic/expkv-bundle/expkv-opt-2020-10-10.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -% File: expkv-opt.dtx Copyright (C) 2020 Jonathan P. Spratte +% File: expkv-opt.dtx 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 @@ -27,7 +27,7 @@ Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt -------------------------------------------------------------- -Copyright (C) 2020 Jonathan P. Spratte +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 @@ -42,6 +42,8 @@ This work is "maintained" (as per LPPL maintenance status) by This work consists of the file expkv-opt.dtx and the derived files expkv-opt.pdf expkv-opt.sty +and the file expkv-opt-2020-10-10.dtx +and the derived file expkv-opt-2020-10-10.sty \endpreamble % stop docstrip adding \endinput @@ -51,7 +53,7 @@ and the derived files expkv-opt.pdf \ifx\fmtname\nameofplainTeX \expandafter\endbatchfile \else - \expandafter\endgroup + \expandafter\endgroup\expandafter\stop \fi % \IfFileExists{expkv-opt.sty}{\RequirePackage{expkv-opt}}{} @@ -481,7 +483,7 @@ and the derived files expkv-opt.pdf % \begin{macro}{\ekvoVersion,\ekvoDate} % Store the packages version and date in two macros. % \begin{macrocode} -\newcommand*\ekvoVersion{0.1b} +\newcommand*\ekvoVersion{0.1c} \newcommand*\ekvoDate{2020-10-10} % \end{macrocode} % \end{macro} @@ -502,11 +504,12 @@ and the derived files expkv-opt.pdf % {\ekvo@CurrentOption@loop,\ekvo@CurrentOption@loop@,\ekvo@end@loop} % We'll need some loop which can iterate over a comma separated list. The loop % is very basic and only works for commas of category~12. First we insert the -% delimiters for the actual loop. +% delimiters for the actual loop. The |\ekv@set@other| is necessary to get a +% functional |\ekvmorekv| in this loop. % \begin{macrocode} \protected\long\def\ekvo@CurrentOption@loop#1#2% {% - \ekvo@CurrentOption@loop@#2\ekv@mark#1,\ekv@stop,\ekvo@tail + \ekvo@CurrentOption@loop@#2\ekv@set@other\ekv@mark#1,\ekv@stop,\ekvo@tail } % \end{macrocode} % The actual loop checks whether the final element has been read and if so @@ -514,16 +517,16 @@ and the derived files expkv-opt.pdf % the macro which parses the list elements called. Then call the next % iteration. % \begin{macrocode} -\long\def\ekvo@CurrentOption@loop@#1#2,% +\long\def\ekvo@CurrentOption@loop@#1\ekv@set@other#2,% {% \ekv@gobble@from@mark@to@stop#2\ekvo@end@loop\ekv@stop \ekv@ifblank{#2}% {}% {% - \edef\CurrentOption{\unexpanded\expandafter{\@gobble#2}}% + \edef\CurrentOption{\unexpanded\expandafter{\ekv@gobble@mark#2}}% #1{#2}% }% - \ekvo@CurrentOption@loop@#1\ekv@mark + \ekvo@CurrentOption@loop@#1\ekv@set@other\ekv@mark } \long\def\ekvo@end@loop#1\ekvo@tail{} % \end{macrocode} @@ -727,10 +730,9 @@ and the derived files expkv-opt.pdf % \begin{macrocode} \protected\long\def\ekvo@parse#1% {% - \ekv@eq@other#1\ekv@nil\ekv@mark\ekv@parse@eq@other@a - =\ekv@mark\ekv@parse@eq@active - \ekvo@set@k\ekvo@set@kv - \ekvo@tail + \ekv@expanded + {\ekv@unexpanded{\ekvparse\ekvo@set@k\ekvo@set@kv}\expandafter}% + \expandafter{\ekv@gobble@mark#1}\ekvo@tail } % \end{macrocode} % \end{macro} diff --git a/macros/generic/expkv-bundle/expkv-opt.dtx b/macros/generic/expkv-bundle/expkv-opt.dtx new file mode 100644 index 0000000000..d2b51d0cb0 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-opt.dtx @@ -0,0 +1,796 @@ +% \iffalse meta-comment +% +% File: expkv-opt.dtx 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 +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\expkvDocNoGenerate{} +\input expkv-bundle.ins +\generate{\file{expkv-opt.sty}{\from{expkv-opt.dtx}{pkg}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkvo} +% +% \gobbledocstriptag +%<*pkg> +% +% First we check whether the \LaTeXe\ kernel supports raw options. If it doesn't +% we check whether a specific version was requested, and if that's not the case +% we manually run |\pkgcls@parse@date@arg| with the last version that supported +% non-raw options. +% \begin{macrocode} +\IfFormatAtLeastTF{2021/05/01} + {} + {% + \ifx\pkgcls@targetlabel\@empty + \ifnum\requestedLaTeXdate=\pkgcls@targetdate + \pkgcls@parse@date@arg{=v0.1}% + \fi + \fi + } +% \end{macrocode} +% Then we tell \LaTeXe\ where to find which release so that the package rollback +% code of \LaTeXe\ can do its thing. +% \begin{macrocode} +\DeclareRelease{v0.1}{2020/10/10}{expkv-opt-2020-10-10.sty} +\DeclareCurrentRelease{v0.2}{2021/04/04} +% \end{macrocode} +% +% Start the package with the typical \LaTeX\ standards. +% +% \begin{macro}{\ekvoVersion,\ekvoDate} +% Store the package's version and date in two macros. +% \begin{macrocode} +\newcommand*\ekvoVersion{1.0} +\newcommand*\ekvoDate{2023-01-23} +% \end{macrocode} +% \end{macro} +% And we report who we are and what we need. +% \begin{macrocode} +\ProvidesPackage{expkv-opt} + [% + \ekvoDate\space v\ekvoVersion\space + parse class and package options with expkv% + ] +\RequirePackage{expkv} +% \end{macrocode} +% +% +% \subsection{Loop} +% +% \begin{macro}[internal] +% {\ekvo@CurrentOption@loop,\ekvo@CurrentOption@loop@,\ekvo@end@loop} +% We'll need some loop which can iterate over a comma separated list. The loop +% is very basic and only works for commas of category~12. First we insert the +% delimiters for the actual loop. The |\ekv@set@other| is necessary to get a +% functional |\ekvmorekv| in this loop. +% \begin{macrocode} +\protected\long\def\ekvo@CurrentOption@loop#1#2% + {% + \ekvo@CurrentOption@loop@#2\ekv@set@other\ekv@mark#1,\ekv@stop,\ekvo@tail + } +% \end{macrocode} +% The actual loop checks whether the final element has been read and if so +% ends the loop. Else blank elements are ignored, |\CurrentOption| is set and +% the macro which parses the list elements called. Then call the next +% iteration. +% \begin{macrocode} +\long\def\ekvo@CurrentOption@loop@#1\ekv@set@other#2,% + {% + \ekv@gobble@from@mark@to@stop#2\ekvo@end@loop\ekv@stop + \ekv@ifblank{#2}% + {}% + {% + \edef\CurrentOption{\unexpanded\expandafter{\ekv@gobble@mark#2}}% + #1{#2}% + }% + \ekvo@CurrentOption@loop@#1\ekv@set@other\ekv@mark + } +\long\def\ekvo@end@loop#1\ekvo@tail{} +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Tests} +% +% \begin{macro}[internal]{\ekvo@ifx@TF,\ekvo@ifx@T,\ekvo@ifx@F} +% We'll need branching |\ifx| tests so that user input containing unbalanced +% \TeX\ ifs doesn't break (at least not because of us, everything else is the +% fault of \LaTeXe). +% \begin{macrocode} +\def\ekvo@ifx@TF#1#2{\ifx#1#2\ekv@fi@firstoftwo\fi\@secondoftwo} +\def\ekvo@ifx@T#1#2{\ifx#1#2\ekv@fi@firstofone\fi\@gobble} +\def\ekvo@ifx@F#1#2{\ifx#1#2\ekv@fi@gobble\fi\@firstofone} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@do@with@set,\ekvo@name,\ekvo@setname} +% This test checks whether the \set\ is defined. If it is we store it in +% |\ekvo@setname| and set |\ekvo@name| to a short cut to get the \key's +% callback name. Next we execute the code in |#2|, if the \set\ isn't defined +% |#2| is gobbled. +% \begin{macrocode} +\protected\def\ekvo@do@with@set#1#2% + {% + \ekvifdefinedset{#1}% + {% + \expandafter + \let\expandafter\ekvo@name\csname\ekv@undefined@set{#1}\endcsname + \def\ekvo@setname{#1}% + #2% + }% + {\ekvo@err@undefined@set{#1}}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Key handlers} +% +% \expkvo\ uses handlers specifying what happens if a parsed \key\ is defined or +% undefined. +% +% \begin{macro}[internal] +% {\ekvo@handle@undefined@k@pkg,\ekvo@handle@undefined@kv@pkg} +% The case for undefined keys in a local list of a package is easy, just throw +% appropriate errors. +% \begin{macrocode} +\protected\long\def\ekvo@handle@undefined@k@pkg#1% + {% + \ekv@ifdefined{\ekvo@name{#1}}% + {\ekvo@err@value@required{#1}}% + {\ekvo@err@undefined@key{#1}}% + } +\def\ekvo@handle@undefined@kv@pkg#1#2% + {% + \ekv@ifdefined{\ekvo@name{#1}N}% + {\ekvo@err@value@forbidden{#1}}% + {\ekvo@err@undefined@key{#1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@addto@unused@one,\ekvo@addto@unused@two, +% \ekvo@rmfrom@unused@one,\ekvo@rmfrom@unused@two +% } +% These macros will add or remove the |\CurrentOption| to or from the list of +% unused global options. Since |\ekvo@do@unusedoptionlist| will have some +% overhead before calling the list changing macro in filtering the current +% option, we use an optimization here in that we check whether the list is +% empty before calling the |rmfrom| function. +% \begin{macrocode} +\long\def\ekvo@addto@unused@one#1{\ekvo@do@unusedoptionlist\ekvo@addnewto@list} +\long\def\ekvo@addto@unused@two#1#2{\ekvo@do@unusedoptionlist\ekvo@addnewto@list} +\long\def\ekvo@rmfrom@unused@one#1% + {% + \ekvo@ifx@F\@unusedoptionlist\@empty + {\ekvo@do@unusedoptionlist\ekvo@rmfrom@list}% + } +\long\def\ekvo@rmfrom@unused@two#1#2% + {% + \ekvo@ifx@F\@unusedoptionlist\@empty + {\ekvo@do@unusedoptionlist\ekvo@rmfrom@list}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@do@unusedoptionlist, +% \ekvo@prepare@unusedoption, +% \ekvo@prepare@unusedoption@a, +% \ekvo@prepare@unusedoption@b, +% \ekvo@prepare@unusedoption@c +% } +% The way the new \LaTeXe\ kernel handles the unused option list changed. Now +% not the entire |\CurrentOption| is listed, but just everything up to the +% first equals sign, and spaces got zapped, doesn't matter whether the raw +% option list gets used or not. So we have to zap spaces and remove everything +% from the first equals sign onwards. The code used here will fail if the +% current option contains an |\ekv@mark| or |\ekv@stop| before the first +% equals sign (this seems rather unlikely). +% \begin{macrocode} +\protected\def\ekvo@do@unusedoptionlist#1% + {% + \let\ekvo@unpreparedCurrentOption\CurrentOption + \edef\CurrentOption + {\expandafter\ekvo@prepare@unusedoption\CurrentOption=\ekv@mark}% + #1\@unusedoptionlist + \let\CurrentOption\ekvo@unpreparedCurrentOption + } +\def\ekvo@prepare@unusedoption{\ekvo@prepare@unusedoption@a\@empty} +\def\ekvo@prepare@unusedoption@a#1% + {% + \long\def\ekvo@prepare@unusedoption@a##1=##2\ekv@mark + {% + \ekvo@prepare@unusedoption@b##1\ekv@stop + \ekv@mark\ekvo@prepare@unusedoption@b + #1\ekv@mark\ekvo@prepare@unusedoption@c + }% + } +\ekvo@prepare@unusedoption@a{ } +\long\def\ekvo@prepare@unusedoption@b#1 #2\ekv@mark#3{#3#1#2\ekv@mark#3} +\long\def\ekvo@prepare@unusedoption@c + #1\ekv@stop + \ekv@mark\ekvo@prepare@unusedoption@b\ekv@mark\ekvo@prepare@unusedoption@c + {\unexpanded\expandafter{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@set@handlers@local,\ekvo@set@handlers@global, +% \ekvo@set@handlers@list +% } +% These macros are boring. They just set up the handlers to respect the rules +% documented earlier. +% \begin{macrocode} +\protected\def\ekvo@set@handlers@local + {% + \ekvo@if@need@handlers + {% + \ifx\@currext\@clsextension + \ifx\@classoptionslist\relax + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + \else + \expandafter + \ifx + \csname @raw@opt@\@currname.\@currext\endcsname + \@raw@classoptionslist + \let\ekvo@handle@undefined@k\ekvo@addto@unused@one + \let\ekvo@handle@undefined@kv\ekvo@addto@unused@two + \unless\ifx\@unusedoptionlist\@empty + \let\ekvo@handle@defined@k\ekvo@rmfrom@unused@one + \let\ekvo@handle@defined@kv\ekvo@rmfrom@unused@two + \fi + \else + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + \fi + \fi + \else + \let\ekvo@handle@undefined@k\ekvo@handle@undefined@k@pkg + \let\ekvo@handle@undefined@kv\ekvo@handle@undefined@kv@pkg + \fi + }% + } +\protected\def\ekvo@set@handlers@global + {% + \unless\ifx\@unusedoptionlist\@empty + \let\ekvo@handle@defined@k\ekvo@rmfrom@unused@one + \let\ekvo@handle@defined@kv\ekvo@rmfrom@unused@two + \fi + \ekvo@if@need@handlers + {% + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + }% + } +\protected\def\ekvo@set@handlers@list + {% + \ekvo@if@need@handlers + {% + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@if@need@handlers,\ekvo@dont@need@handlers} +% If the user specifies handlers this macro will be let to +% |\ekvo@dont@need@handlers|, which will act like |\@gobble| and also let it +% to |\@firstofone| afterwards. +% \begin{macrocode} +\let\ekvo@if@need@handlers\@firstofone +\protected\long\def\ekvo@dont@need@handlers#1% + {% + \let\ekvo@if@need@handlers\@firstofone + }% +% \end{macrocode} +% \end{macro} +% +% We have to set the default for the handlers of defined keys, because they +% don't necessarily get defined before a list is parsed. +% \begin{macrocode} +\let\ekvo@handle@defined@k\@gobble +\let\ekvo@handle@defined@kv\@gobbletwo +% \end{macrocode} +% +% +% \subsection{Processing list elements} +% +% \begin{macro}[internal]{\ekvo@process@common} +% All the key processing frontend macros use the same basic structure. |#1| +% will be a simple test, deciding whether the list will really be parsed or +% not, |#3| will be the \set, and |#2| will be the individual code of the +% frontend macro which should be executed if both the test in |#1| is true and +% the \set\ is defined. +% \begin{macrocode} +\protected\def\ekvo@process@common#1#2#3% + {% + #1{\ekvo@do@with@set{#3}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@process@list} +% This macro only expands the list holding macro and forwards it to the +% loop macro. +% \begin{macrocode} +\protected\def\ekvo@process@list#1% + {% + \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@parse + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@parse, +% \ekvo@parse@kv,\ekvo@parse@kv@,\ekvo@expansion@kv,\ekvo@expansion@kv@, +% \ekvo@parse@k, \ekvo@parse@k@, \ekvo@expansion@k, \ekvo@expansion@k@ +% } +% This macro calls internals of \expkv\ such that the input is split at an +% equals sign of category other. +% \begin{macrocode} +\long\def\ekvo@parse#1% + {% + \ekv@eq@other#1\ekv@nil\ekv@mark\ekvo@parse@kv + =\ekv@mark\ekvo@parse@k + } +% \end{macrocode} +% If there was an equals sign, this will be called and remove the remainder of +% the split. Afterwards the \expnotation\ is checked and possibly executed, +% and the \kv\ pair is set. While reinsertion works via the |Rr| +% \expansion-rule, it might affect the unused global option list. +% \begin{macrocode} +\long\def\ekvo@parse@kv#1\ekv@stop#2\ekv@nil=\ekv@mark\ekvo@parse@k + {\ekv@strip{#2}{\ekv@strip{#1}\ekvo@parse@kv@}} +\long\def\ekvo@parse@kv@#1#2% + {% + \ekv@ifexp{#1}% + {\ekvo@expansion@kv{#2}}% + {\ekvo@set@kv{#1}{#2}}% + } +% \end{macrocode} +% Check for \expansion-rules was true, now we need to execute them, and use +% the result in |\ekvo@set@kv|. Also we have to handle the key as if it was a +% defined key if the |\r| \expansion-rule was found (which potentially removes +% it from the list of global unused options). +% \begin{macrocode} +\long\def\ekvo@expansion@kv#1#2#3#4% + {% + \ekv@expansion@rule@{#1}#2\ekv@mark\ekv@stop{#3}{}% + {\ekvo@handle@defined@kv{#3}{#1}\ekvmorekv}% + \ekvo@expansion@kv@ + } +\long\def\ekvo@expansion@kv@#1#2{\ekvo@set@kv{#1}{#2}} +% \end{macrocode} +% And basically the same two biggish steps, but for the case that no equals +% sign is found. +% \begin{macrocode} +\long\def\ekvo@parse@k#1\ekv@nil\ekv@mark\ekvo@parse@kv\ekv@stop\ekv@mark + {\ekv@strip{#1}\ekvo@parse@k@} +\long\def\ekvo@parse@k@#1% + {% + \ekv@ifexp{#1}% + \ekvo@expansion@k + {\ekvo@set@k{#1}}% + } +\long\def\ekvo@expansion@k#1#2#3% + {% + \ekv@expansion@rule@{#2}#1\ekv@mark\ekv@stop{}{}% + {\ekvo@handle@defined@k{#2}\ekvmorekv}% + \ekvo@expansion@k@ + } +\long\def\ekvo@expansion@k@#1#2{\ekvo@set@k{#2}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@set@k,\ekvo@set@kv} +% These two macros check whether the key is defined and if so call the handler +% for defined keys and execute the key, else the handler for undefined keys is +% called. +% \begin{macrocode} +\protected\def\ekvo@set@k#1% + {% + \ekv@ifdefined{\ekvo@name{#1}N}% + {% + \ekvo@handle@defined@k{#1}% + \csname\ekvo@name{#1}N\endcsname + }% + {\ekvo@handle@undefined@k{#1}}% + } +\protected\def\ekvo@set@kv#1#2% + {% + \ekv@ifdefined{\ekvo@name{#1}}% + {% + \ekvo@handle@defined@kv{#1}{#2}% + \csname\ekvo@name{#1}\endcsname{#2}% + }% + {\ekvo@handle@undefined@kv{#1}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{List variable helpers} +% +% \begin{macro}[internal]{\ekvo@addto@list} +% This macro is rather simple. If the list to which the |\CurrentOption| +% should be added is empty we can just let the list to the |\CurrentOption|. +% Else we have to expand the list once and the |\CurrentOption| once. +% \begin{macrocode} +\protected\def\ekvo@addto@list#1% + {% + \ekvo@ifx@TF#1\@empty + {\let#1\CurrentOption}% + {% + \edef#1% + {% + \unexpanded\expandafter{#1},% + \unexpanded\expandafter{\CurrentOption}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@addnewto@list} +% This works just like |\ekvo@addto@list|, but it only adds elements which are +% not yet part of the list, hence we check if the option is already in the +% list. +% \begin{macrocode} +\protected\def\ekvo@addnewto@list#1% + {% + \ekvo@ifx@TF#1\@empty + {\let#1\CurrentOption}% + {% + \ekvo@if@in@list#1% + {}% + {% + \edef#1% + {% + \unexpanded\expandafter{#1},% + \unexpanded\expandafter{\CurrentOption}% + }% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvo@if@in@list,\ekvo@if@in@list@,\ekvo@if@in@list@result} +% This is a slow but robust test whether some option is already contained in a +% list. Using argument gobbling logic would be faster, but that way wouldn't +% be robust for elements containing |{|, |}|, or |#|. Just loop over the code +% and compare each element, end early if a match is found. +% \begin{macrocode} +\protected\def\ekvo@if@in@list#1% + {% + \ekvo@ifx@TF#1\@empty + \@secondoftwo + {% + \let\ekvo@curropt\CurrentOption + \let\ekvo@if@in@list@result\@secondoftwo + \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@if@in@list@ + \let\CurrentOption\ekvo@curropt + \ekvo@if@in@list@result + }% + } +\protected\long\def\ekvo@if@in@list@#1% + {% + \ekvo@ifx@T\ekvo@curropt\CurrentOption + {% + \let\ekvo@if@in@list@result\@firstoftwo + \ekvo@end@loop + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@rmfrom@list,\ekvo@rmfrom@list@} +% This works by looping over every list item and comparing it to +% |\ekvo@curropt| which stores the real |\CurrentOption|. This is +% comparatively slow, but works for items containing braces unlike what +% \LaTeXe\ does. We could be faster for items not containing braces, though. +% \begin{macrocode} +\protected\def\ekvo@rmfrom@list#1% + {% + \ekvo@ifx@F#1\@empty + {% + \let\ekvo@tmp@list\@empty + \let\ekvo@curropt\CurrentOption + \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@rmfrom@list@ + \let\CurrentOption\ekvo@curropt + \let#1\ekvo@tmp@list + }% + } +\protected\long\def\ekvo@rmfrom@list@#1% + {% + \ekvo@ifx@F\CurrentOption\ekvo@curropt + {\ekvo@addto@list\ekvo@tmp@list}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Errors} +% +% \begin{macro}[internal] +% { +% \ekvo@err@undefined@key,\ekvo@err@value@required, +% \ekvo@err@value@forbidden,\ekvo@err@undefined@set +% } +% Just some macros to throw errors in the few cases an error has to be thrown. +% \begin{macrocode} +\protected\def\ekvo@err@undefined@key#1% + {% + \ekvo@pkg@cls@error + {Undefined option `\detokenize{#1}'}% + {The used option was not defined. Perhaps you misspelled it?}% + } +\protected\def\ekvo@err@value@required#1% + {% + \ekvo@pkg@cls@error + {Missing value for option `\detokenize{#1}'}% + {The used option requires a value or you misspelled its name.}% + } +\protected\def\ekvo@err@value@forbidden#1% + {% + \ekvo@pkg@cls@error + {Unwanted value for option `\detokenize{#1}'}% + {The used option doesn't support a value or you misspelled its name.}% + } +\protected\def\ekvo@err@undefined@set#1% + {% + \PackageError{expkv-opt}% + {Undefined set `#1'}% + {The set for which you try to parse options isn't defined in expkv.}% + } +\protected\def\ekvo@pkg@cls@error + {% + \ekvo@ifx@TF\@currext\@clsextension + \ClassError + \PackageError + \@currname + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{User Interface} +% +% The user interface macros just put together the bits and pieces. +% +% \begin{macro}{\ekvoProcessOptions} +% First we check if user-defined handlers were used. If that's the case we +% need to store them and restore them for each list processor. Else just call +% them sequentially. +% \begin{macrocode} +\protected\def\ekvoProcessOptions#1% + {% + \ekvo@if@need@handlers + {% + \ekvoProcessGlobalOptions{#1}% + \ekvoProcessLocalOptions {#1}% + \ekvoProcessFutureOptions{#1}% + \@gobbletwo + }% + \@firstofone + {% + \let\ekvo@tmpa\ekvo@handle@undefined@k + \let\ekvo@tmpb\ekvo@handle@undefined@kv + \ekvoUseUnknownHandlers\ekvo@tmpa\ekvo@tmpb + \ekvoProcessGlobalOptions{#1}% + \ekvoUseUnknownHandlers\ekvo@tmpa\ekvo@tmpb + \ekvoProcessLocalOptions {#1}% + \ekvoUseUnknownHandlers\ekvo@tmpa\ekvo@tmpb + \ekvoProcessFutureOptions{#1}% + \let\ekvo@tmpa\ekvo@undefined + \let\ekvo@tmpb\ekvo@undefined + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessLocalOptions} +% \begin{macrocode} +\protected\def\ekvoProcessLocalOptions + {% + \ekvo@process@common + {\ekv@ifdefined{@raw@opt@\@currname.\@currext}\@firstofone\@gobble}% + {% + \ekvo@set@handlers@local + \expandafter + \ekvo@process@list\csname @raw@opt@\@currname.\@currext\endcsname + \AtEndOfPackage{\let\@unprocessedoptions\relax}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessGlobalOptions} +% \begin{macrocode} +\protected\def\ekvoProcessGlobalOptions + {% + \ekvo@process@common{\ekvo@ifx@F\@classoptionslist\relax}% + {% + \ekvo@set@handlers@global + \ekvo@process@list\@raw@classoptionslist + \let\ekvo@handle@defined@k\@gobble + \let\ekvo@handle@defined@kv\@gobbletwo + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessUnusedGlobalOptions} +% \begin{macrocode} +\protected\def\ekvoProcessUnusedGlobalOptions#1% + {% + \PackageError{expkv-opt}% + {% + \noexpand + \ekvoProcessUnusedGlobalOptions no longer works because of changes in% + \MessageBreak + the LaTeX2e kernel% + }% + {}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessFutureOptions} +% Parsing future options (without patching kernel internals) is only possible +% with kernel versions after 2022-11-01. +% The user macro needs to store the user-set handlers for later if +% |\ekvoUseUnknownHandlers| was used. Then it stores the correct code to +% process future options in the |opt@handler@| variable. +% \begin{macrocode} +\IfFormatAtLeastTF{2022/11/01} + {% + \protected\def\ekvoProcessFutureOptions#1% + {% + \ekvo@if@need@handlers\@gobbletwo + \@firstofone + {% + \expandafter\let + \csname ekvo@future@undefined@k@\@currname.\@currext\endcsname + \ekvo@handle@undefined@k + \expandafter\let + \csname ekvo@future@undefined@kv@\@currname.\@currext\endcsname + \ekvo@handle@undefined@kv + }% + \protected\expandafter + \xdef\csname opt@handler@\@currname.\@currext\endcsname + {\ekv@unexpanded{\ekvo@future{#1}}}% + } +% \end{macrocode} +% \begin{macro}[internal]{\ekvo@future,\ekvo@set@handlers@future} +% Key parsing is pretty similar to |\ekvoProcessLocalOptions|, only the +% handlers are defined differently, namely it is checked whether they are +% user-specified, if not we reuse the undefined handlers for packages (which +% will throw an error if an unknown key is found). +% \begin{macrocode} + \protected\def\ekvo@future + {% + \ekvo@process@common + {\ekv@ifdefined{@raw@opt@\@currname.\@currext}\@firstofone\@gobble}% + {% + \ekvo@set@handlers@future + \expandafter\ekvo@process@list + \csname @raw@opt@\@currname.\@currext\endcsname + \AtEndOfPackage{\let\@unprocessedoptions\relax}% + }% + } + \protected\def\ekvo@set@handlers@future + {% + \expandafter\let\expandafter + \ekvo@handle@undefined@k + \csname + \ekv@ifdefined{ekvo@future@undefined@k@\@currname.\@currext}% + {ekvo@future@undefined@k@\@currname.\@currext}% + {ekvo@handle@undefined@k@pkg}% + \endcsname + \expandafter\let\expandafter + \ekvo@handle@undefined@kv + \csname + \ekv@ifdefined{ekvo@future@undefined@kv@\@currname.\@currext}% + {ekvo@future@undefined@kv@\@currname.\@currext}% + {ekvo@handle@undefined@kv@pkg}% + \endcsname + } +% \end{macrocode} +% \end{macro} +% \begin{macrocode} + } + {% + \protected\def\ekvoProcessFutureOptions#1% + {% + \PackageWarning{expkv-opt}% + {LaTeX-kernel is too old to process future options.\@gobbletwo}% + \protected\gdef\ekvoProcessFutureOptions##1{}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessOptionsList} +% \begin{macrocode} +\protected\def\ekvoProcessOptionsList#1% + {% + \ekvo@process@common{\ekvo@ifx@F#1\@empty}% + {% + \ekvo@set@handlers@list + \ekvo@process@list#1% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoUseUnknownHandlers} +% \begin{macro}[internal]{\ekvoUseUnknownHandlers@n,\ekvoUseUnknownHandlers@s} +% \begin{macrocode} +\protected\def\ekvoUseUnknownHandlers + {% + \let\ekvo@if@need@handlers\ekvo@dont@need@handlers + \@ifstar\ekvoUseUnknownHandlers@s\ekvoUseUnknownHandlers@n + } +\protected\def\ekvoUseUnknownHandlers@s + {% + \long\def\ekvo@handle@undefined@k##1% + {% + \ekv@ifdefined{\ekvo@name{}uN}% + {% + \csname\ekvo@name{}uN\expandafter\endcsname\expandafter + {\detokenize{##1}}{##1}% + }% + {\@gobble}% + }% + \long\def\ekvo@handle@undefined@kv##1##2% + {% + \ekv@ifdefined{\ekvo@name{}u}% + {% + \csname\ekvo@name{}u\ekv@expanded{\endcsname + {\ekv@unexpanded{##2}}{\detokenize{##1}}}{##1}% + }% + {}% + }% + } +\protected\def\ekvoUseUnknownHandlers@n#1#2% + {% + \let\ekvo@handle@undefined@k#1\relax + \let\ekvo@handle@undefined@kv#2\relax + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% All user interface macros should be only used in the preamble. +% \begin{macrocode} +\@onlypreamble\ekvoProcessOptions +\@onlypreamble\ekvoProcessLocalOptions +\@onlypreamble\ekvoProcessGlobalOptions +\@onlypreamble\ekvoProcessFutureOptions +\@onlypreamble\ekvoProcessOptionsList +\@onlypreamble\ekvoUseUnknownHandlers +% \end{macrocode} +% +% \gobbledocstriptag +%</pkg> diff --git a/macros/generic/expkv-bundle/expkv-pop.dtx b/macros/generic/expkv-bundle/expkv-pop.dtx new file mode 100644 index 0000000000..dc05d6bd0b --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-pop.dtx @@ -0,0 +1,700 @@ +% \iffalse meta-comment +% +% File: expkv-pop.dtx 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 +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\expkvDocNoGenerate{} +\input expkv-bundle.ins +\generate{\file{expkv-pop.sty}{\from{expkv-pop.dtx}{pkg}}} +\generate{\file{expkv-pop.tex}{\from{expkv-pop.dtx}{tex}}} +\generate{\file{t-expkv-pop.tex}{\from{expkv-pop.dtx}{ctx}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkvp} +%^^A the LaTeX package >>= +% \subsection{The \LaTeX\ Package} +% Set up the \LaTeX\ package. +% \gobbledocstriptag +%<*pkg> +% \begin{macrocode} +\RequirePackage{expkv} +\def\ekvp@tmp + {% + \ProvidesFile{expkv-pop.tex}% + [\ekvpDate\space v\ekvpVersion\space a prefix oriented parser]% + } +\input{expkv-pop.tex} +\ProvidesPackage{expkv-pop}% + [\ekvpDate\space v\ekvpVersion\space a prefix oriented parser] +% \end{macrocode} +% \gobbledocstriptag +%</pkg> +%^^A=<< +%^^A the ConTeXt module >>= +% \subsection{The \ConTeXt\ module} +% Set up the \ConTeXt\ module. +% \gobbledocstriptag +%<*ctx> +% \begin{macrocode} +\writestatus{loading}{ConTeXt User Module / expkv-pop} +\usemodule[expkv] +\unprotect +\input expkv-pop.tex +\writestatus{loading} + {ConTeXt User Module / expkv-pop / Version \ekvpVersion\space loaded} +\protect\endinput +% \end{macrocode} +% \gobbledocstriptag +%</ctx> +%^^A=<< +%^^A main file >>= +% \subsection{The Generic Code} +% And another generic code package we need to set up. +% \gobbledocstriptag +%<*tex> +% +% Loading the generic \expkv\ package if it wasn't already loaded, utilizing +% that \expkv\ prevents itself from loading multiple times. +% \begin{macrocode} +\input expkv +% \end{macrocode} +% +% Introduce an own guard against being loaded multiple times: +% \begin{macrocode} +\expandafter\ifx\csname ekvpVersion\endcsname\relax +\else + \expandafter\endinput +\fi +% \end{macrocode} +% +% \begin{macro}{\ekvpVersion,\ekvpDate} +% Specify the own version and date +% \begin{macrocode} +\def\ekvpVersion{1.0} +\def\ekvpDate{2023-01-23} +% \end{macrocode} +% \end{macro} +% +% Reporting back who we are for \LaTeX\ (the package will have set up things for +% us). +% \begin{macrocode} +\csname ekvp@tmp\endcsname +% \end{macrocode} +% +% Package internal category code setup (stored to restore it at the end -- we +% have to be careful to not lose this definition of |\ekvp@tmp| inside the +% package). +% \begin{macrocode} +\expandafter\chardef\csname ekvp@tmp\endcsname=\catcode`\@ +\catcode`\@=11 +% \end{macrocode} +% +% \subsubsection{Parsing}^^A>>= +% +% \begin{macro}{\ekvpParse} +% \begin{macro}[internal] +% { +% \ekvpParse@a,\ekvpParse@b,\ekvpParse@c, +% \ekvpParse@unsafe,\ekvpParse@unsafe@auto +% } +% Parsing should be done in two steps of expansion, hence we put +% |\unexpanded\expanded| around it. Next we check whether the parser is +% defined, and afterwards run |\ekvparse|. For that we expand it once and +% remove the |\unexpanded| which it uses itself. +% \begin{macrocode} +\long\def\ekvpParse#1#2% + {% + \ekv@unexpanded\ekv@expanded + {{\expandafter\ekvpParse@a\detokenize{#1}\ekv@mark{#2}}}% + } +\def\ekvpParse@a#1\ekv@mark + {% + \ekv@ifdefined{ekvp@@p@#1}% + {\expandafter\ekvpParse@b\csname ekvp@@p@#1\endcsname}% + {\ekvp@err@unknownparser{#1}\@gobble}% + } +\def\ekvpParse@b#1% + {% + \ekv@ifdefined{#1{ppa}}% + {% + \expandafter\ekvpParse@c\expandafter + {\expandafter#1\csname#1{ppa}\endcsname}% + }% + {\ekvpParse@c{#1{}}}% + } +\def\ekvpParse@c#1#2#3{#2#3} +\ekv@expargtwice{\long\def\ekvpParse@c#1#2}% + {\expandafter\ekvpParse@c\ekvparse{\ekvp@k#1}{\ekvp@p#1}{#2}} +\long\def\ekvpParse@unsafe#1#2#3{\ekv@unexpanded\ekv@expanded{{#2#3}}} +\ekv@expargtwice{\long\def\ekvpParse@unsafe@auto#1#2#3} + {\expandafter\ekvpParse@unsafe\ekvparse{\ekvp@k#1#2}{\ekvp@p#1#2}{#3}} +\ekv@expargtwice{\long\def\ekvpParse@unsafe#1#2}% + {\expandafter\ekvpParse@unsafe\ekvparse{\ekvp@k#1{}}{\ekvp@p#1{}}{#2}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@k} +% For |NoVal| we use the parser specific rule. +% \begin{macrocode} +\def\ekvp@k#1{\csname #1k\endcsname#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@ifspace,\ekvp@ifspace@} +% This checks whether a space is inside its argument (gobbling up to the first +% space and checking for an empty remainder). +% \begin{macrocode} +\long\def\ekvp@ifspace#1% + {% + \ekvp@ifspace@#1 \ekv@ifempty@B\ekv@ifempty@false + \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo + } +\long\def\ekvp@ifspace@#1 % keep this space + {\ekv@ifempty@\ekv@ifempty@A} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@p} +% \begin{macro}[internal]{\ekvp@prefix} +% \begin{macro}[internal]{\ekvp@prefix@a,\ekvp@prefix@b} +% If there should be a prefix there needs to be a space, so we check for one. +% Then we split the first prefix of the rest. +% \begin{macrocode} +\ekv@exparg{\long\def\ekvp@prefix#1}% + {% + \ekvp@ifspace{#1}% + {\ekvp@prefix@a#1\ekv@stop}% + {\ekvp@noprefix{#1}}% + } +% \end{macrocode} +% A prefix is parsed with a few additional logical groups. The input |#1| is +% the parser's name macro, |#2| is the full item which should be parsed. And +% we place an additional group in which the prefix macros will be collected. +% \begin{macrocode} +\ekv@exparg{\long\def\ekvp@p#1#2#3}{\ekvp@prefix{#3}#1{#2}{#3}} +% \end{macrocode} +% The prefix is |\detokenize|d and forwarded. Then we check whether it's a +% defined type or prefix. +% \begin{macrocode} +\long\def\ekvp@prefix@a#1 % keep this space + {\expandafter\ekvp@prefix@b\detokenize{#1}\ekv@mark{#1}} +\ekv@exparg{\long\def\ekvp@prefix@b#1\ekv@mark#2#3\ekv@stop#4}% + {% + \ekv@ifdefined{#4{pt}@#1} + {\expandafter\ekvp@prefix@pt\csname #4{pt}@#1\endcsname{#1}}% + {% + \ekv@ifdefined{#4{pp}@#1}% + {\expandafter\ekvp@prefix@pp\csname #4{pp}@#1\endcsname}% + {\@firstoftwo{\ekvp@noprefix{#2 #3}}}% + }% + {#3}#4% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@prefix@pt,\ekvp@prefix@pt@} +% A type ends the prefix parsing (every item can only have one type). The +% argument |#1| is that type macro, and in |#2| is the types name. |#3| then +% contains the name of the element, |#4| is the parser's name macro (which is +% no longer needed here), |#5| is the list of prefix macros, |#6| the entire +% unchanged item, and |#7| is the value provided to the current item. The +% prefix macro list will be stepped through, each macro will get the type +% name, element name, and complete unprocessed item, whereas the type macro +% will not receive the type string (but instead the value), which is why we +% put a |\@firstoftwo| there to remove the type. |\ekvp@prefix@pt@| is a +% helper to fetch the first prefix macro (or the type should |#5| be empty). +% \begin{macrocode} +\long\def\ekvp@prefix@pt#1#2#3#4#5#6#7% + {% + \ekvp@prefix@pt@{#2}{#3}{#6}#5{\@firstoftwo#1}{#7}\ekvpEOT + \ekv@unexpanded{\ekvpEOA{#6}}% + } +\long\def\ekvp@prefix@pt@#1#2#3#4{#4{#1}{#2}{#3}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@prefix@pp} +% A prefix macro is added to the list of prefixes (argument |#4|) and the next +% prefix is searched. +% \begin{macrocode} +\ekv@exparg{\long\def\ekvp@prefix@pp#1#2#3#4}{\ekvp@prefix{#2}#3{#4#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@noprefix} +% If no prefix was found (either because there was no space, or the first +% space delimited thing wasn't a valid prefix) we check if there is a notype +% rule or throw an error. +% \begin{macrocode} +\long\def\ekvp@noprefix#1#2% + {% + \ekv@ifdefined{#2{pn}} + {\expandafter\ekvp@prefix@pt\csname #2{pn}\endcsname{}{#1}} + \ekvp@err@missingtype + #2% + } +% \end{macrocode} +% \end{macro} +% +%^^A=<< +% +% \subsubsection{Defining Parsers}^^A>>= +% +% \begin{macro}{\ekvpNewParser} +% \begin{macro}[internal]{\ekvpNewParser@} +% To define a new parser we need to assert that it doesn't already exists (if +% so we throw an error) and define the parser name. A parser name is defined +% as the macro \cs[no-index]{ekvp@@p@\meta{name}}, that macro will take one +% argument and build the name of a prefix or type. Additionally a new parser +% gets its |NoVal| rule set up as the default rule. +% \begin{macrocode} +\protected\long\def\ekvpNewParser#1% + {\expandafter\ekvpNewParser@\detokenize{#1}\ekv@mark} +\protected\def\ekvpNewParser@#1\ekv@mark + {% + \ekv@ifdefined{ekvp@@p@#1}% + {\ekvp@errm{Parser `#1' already defined}}% + {% + \expandafter\def\csname ekvp@@p@#1\endcsname##1{ekvp@@##1@#1} + \expandafter + \let\csname\csname ekvp@@p@#1\endcsname k\endcsname\ekvp@k@default + }% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpValueAlwaysRequired} +% \begin{macro}[internal]{\ekvpValueAlwaysRequired@} +% This just changes the default |NoVal| rule to throw an error. +% \begin{macrocode} +\protected\long\def\ekvpValueAlwaysRequired#1% + {\ekvp@parser@def{#1}\ekvpValueAlwaysRequired@{}} +\protected\def\ekvpValueAlwaysRequired@#1% + {\expandafter\let\csname#1k\endcsname\ekvp@err@noval} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpDefNoValue} +% \begin{macro}[internal]{\ekvpDefNoValue@} +% \begin{macrocode} +\protected\long\def\ekvpDefNoValue#1% + {\ekvp@parser@def{#1}\ekvpDefNoValue@\@gobble} +\protected\long\def\ekvpDefNoValue@#1#2% + {% + \def\ekvp@tmp##1{\ekv@unexpanded{#2}}% + \ekv@exparg{\long\expandafter\def\csname#1k\endcsname##1##2##3}% + {\ekvp@tmp{##3}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpUseNoValueMarker} +% \begin{macro}[internal]{\ekvpUseNoValueMarker@} +% +% \begin{macrocode} +\protected\long\def\ekvpUseNoValueMarker#1% + {\ekvp@parser@def{#1}\ekvpUseNoValueMarker@\@gobble} +\protected\long\def\ekvpUseNoValueMarker@#1#2% + {% + \long\expandafter\edef\csname#1k\endcsname##1##2##3% + {\ekv@unexpanded{\ekvp@p}##1{##2}{##3}{\ekv@unexpanded{#2}}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpDefNoValuePrefix} +% \begin{macro}[internal]{\ekvpDefNoValuePrefix@,\ekvpDefNoValuePrefix@@} +% This might be the most complicated of the |NoVal|-behaviour changes. The +% first two steps should be clear. +% \begin{macrocode} +\protected\long\def\ekvpDefNoValuePrefix#1% + {\ekvp@parser@def{#1}\ekvpDefNoValuePrefix@\@gobbletwo} +\protected\def\ekvpDefNoValuePrefix@#1% + {\expandafter\ekvpDefNoValuePrefix@@\csname#1{ppn}\endcsname#1} +% \end{macrocode} +% This step is the complicated one combining the effects of \cs{ekvpDefPrefix} +% with the standard |NoVal|-rule (but for which the no-value marker might have +% been changed), so here's what happens: +% We set up a temporary meaning for the control sequence name +% forwarded as |#1| to extract the no-value marker of the current parser. Then +% we set up the |NoVal|-macro of the current parser to expand to the normal +% parsing route, there we inject |#1| as the first prefix macro in the +% corresponding first argument of \cs[no-index]{ekvp@p}, and we extract the +% no-value marker with the |\expandafter#1|-chain. This last step might +% actually fail badly if \cs{ekvpValueAlwaysRequired} or \cs{ekvpDefNoValue} +% was used. +% \begin{macrocode} +\protected\long\def\ekvpDefNoValuePrefix@@#1#2% + {% + \long\def#1\ekvp@p##1##2##3% + {% + \ekv@unexpanded{\ekvp@p}% + ####1{####2\ekv@unexpanded{#1}}{####3}{\ekv@unexpanded{##3}}% + }% + \long\expandafter\edef\csname#2k\endcsname##1##2##3% + {\expandafter\expandafter\expandafter#1\csname#2k\endcsname{}{}{}}% + \ekvp@parser@def@prefix#1% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpDefAutoPrefix} +% \begin{macro}[internal]{\ekvpDefAutoPrefix@} +% \begin{macrocode} +\protected\long\def\ekvpDefAutoPrefix#1% + {\ekvp@parser@def{#1}\ekvpDefAutoPrefix@\@gobbletwo} +\protected\long\def\ekvpDefAutoPrefix@#1% + {\expandafter\ekvp@parser@def@prefix\csname#1{ppa}\endcsname} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvp@parser@def,\ekvp@parser@def@} +% This just makes sure that the parser is defined and builds the parser name. +% |#1| should be the user-level parser name, |#2| the macro that gets the +% code-level parser forwarded, and |#3| should gobble all additional arguments +% needed by whichever frontend macro this is used for in case of an error. +% \begin{macrocode} +\long\def\ekvp@parser@def#1% + {\expandafter\ekvp@parser@def@\detokenize{#1}\ekv@mark} +\def\ekvp@parser@def@#1\ekv@mark#2#3% + {% + \ekv@ifdefined{ekvp@@p@#1}% + {\ekv@exparg{#2}{\csname ekvp@@p@#1\endcsname}}% + {\ekvp@errm{Parser `#1' not defined}#3}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvpDefPrefix} +% \begin{macro}[internal]{\ekvpDefPrefix@,\ekvp@parser@def@prefix} +% A prefix is stored as a macro. At first we define a temporary meaning just +% to make sure that the user uses only the first three arguments. +% The real macro is a bit more complicated. It places the user provided before +% action (|#3|) where it currently is, the after action (|#4|) after +% |\ekvpEOT| (so after the type action). It also fetches the next prefix or +% the type macro and forwards the arguments to it. +% \begin{macrocode} +\protected\long\def\ekvpDefPrefix#1% + {\ekvp@parser@def{#1}\ekvpDefPrefix@\@gobblethree} +\protected\long\def\ekvpDefPrefix@#1#2% + {\expandafter\ekvp@parser@def@prefix\csname#1{pp}@\detokenize{#2}\endcsname} +\protected\long\def\ekvp@parser@def@prefix#1#2#3% + {% + \def#1##1##2##3{#2#3}% + \long\def#1##1##2##3##4##5\ekvpEOT + {\ekv@unexpanded{#2}##4{##1}{##2}{##3}##5\ekvpEOT\ekv@unexpanded{#3}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpDefPrefixLet,\ekvpDefPrefixStore} +% These are just special cases of |\ekvpDefPrefix|, so nothing complicated +% here. +% \begin{macrocode} +\ekv@exparg{\protected\long\def\ekvpDefPrefixLet#1#2#3#4#5}% + {\ekvpDefPrefix{#1}{#2}{\ekvpProtect{\let#3= #4}}{\ekvpProtect{\let#3= #5}}} +\ekv@exparg{\protected\long\def\ekvpDefPrefixStore#1#2#3#4#5}% + {% + \ekvpDefPrefix{#1}{#2}% + {\ekvpProtect{\edef#3{\ekv@unexpanded{#4}}}}% + {\ekvpProtect{\edef#3{\ekv@unexpanded{#5}}}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvpDefType,\ekvpDefNoType} +% \begin{macro}[internal]{\ekvpDefType@,\ekvpDefNoType@,\ekvpDefType@NoType} +% A type macro and a notype-macro are pretty similar, the whole difference is +% the naming scheme. The macro leaves the user definition in the input and +% places the |\ekvpEOP| and |\ekvpEOT| markers. +% \begin{macrocode} +\protected\long\def\ekvpDefType#1{\ekvp@parser@def{#1}\ekvpDefType@\@gobbletwo} +\protected\long\def\ekvpDefType@#1#2% + {\ekvpDefType@NoType{#1{pt}@\detokenize{#2}}}% +\protected\long\def\ekvpDefNoType#1{\ekvp@parser@def{#1}\ekvpDefNoType@\@gobble} +\protected\long\def\ekvpDefNoType@#1{\ekvpDefType@NoType{#1{pn}}} +\protected\long\def\ekvpDefType@NoType#1#2% + {% + \long\expandafter\def\csname#1\endcsname##1##2##3\ekvpEOT + {\ekv@unexpanded{\ekvpEOP{##2}#2\ekvpEOT{##2}}}% + }% +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpLet} +% \begin{macro}[internal]{\ekvpLet@a,\ekvpLet@b,\ekvpLet@c,\ekvpLet@d} +% Letting has an optional argument, so we can't use |\ekvp@parser@def| +% directly here, first we need to convert the optional argument to a normal +% one so that we can gobble it normally. Then we check that the two parsers +% are defined, and afterwards that the copy macro is. If all this is correct +% we do the |\let|. Since arguments are shuffled around here, I'll list them +% each time they get reordered (some might be curried). +% +% |#1| is the parser name, |#2| the type, |#3| the new \prefix/\type\ name, +% |#4| the optional other parser name, |#5| the already existing +% \prefix/\type\ which should be copied. +% \begin{macrocode} +\protected\long\def\ekvpLet#1#2#3{\ekvoptarg{\ekvpLet@a{#1}{#2}{#3}}{#1}} +\protected\long\def\ekvpLet@a#1% + {\ekvp@parser@def{#1}\ekvpLet@b{\@firstoftwo\@gobblethree}} +\protected\long\def\ekvpLet@b#1#2#3#4% + {\ekvp@parser@def{#4}\ekvpLet@c{\@firstoftwo\@gobblethree}#1{#2}{#3}} +% \end{macrocode} +% |#1| the other parser's macro, |#2| the parser's macro, |#3| the type, |#4| +% the new \prefix/\type, |#5| the already existing \prefix/\type. +% \begin{macrocode} +\protected\long\def\ekvpLet@c#1#2#3% + {% + \ekv@ifdefined{ekvpLet@@\detokenize{#3}}% + {\expandafter\ekvpLet@d\csname ekvpLet@@\detokenize{#3}\endcsname{#1}{#2}}% + {\ekvp@errm{unknown type \detokenize{#3}}\@gobbletwo}% + } +% \end{macrocode} +% |#1| the type's macro, |#2| the other parser's macro, |#3| the parser's +% macro, |#4| the new \prefix/\type, |#5| the already existing \prefix/\type. +% \begin{macrocode} +\protected\long\def\ekvpLet@d#1#2#3#4#5% + {% + \ekv@ifdefined{#2{#1}@\detokenize{#5}}% + {% + \expandafter\let + \csname#3{#1}@\detokenize{#4}\expandafter\endcsname + \csname#2{#1}@\detokenize{#5}\endcsname + }% + {\ekvp@errm{Undefined prefix/type \detokenize{#5} can't be copied}}% + } +\def\ekvpLet@@prefix{pp} +\def\ekvpLet@@type{pt} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +%^^A=<< +% +% \subsubsection{\texttt{NoVal} Handling}^^A>>= +% +% The idea of |NoVal| handling is taken from \pkg{expl3}. We define a marker +% (or directly use \pkg{expl3}'s). The defined test is not as robust as +% \pkg{expl3}'s, but pretty fast (and based on |\ekv@ifempty|). +% +% \begin{macro}{\ekvpIfNoVal} +% \begin{macro}[internal]{\ekvp@novalflag,\ekvp@ifnoval} +% \begin{macro}[internal]{\ekvp@k@default} +% \begin{macrocode} +\ekv@ifdefined{c_novalue_tl} + {\expandafter\let\expandafter\ekvp@novalflag\csname c_novalue_tl\endcsname} + {% + \begingroup + \lccode`\Z=`\- + \lccode`\:=`\- + \lccode`\N=\z@ + \lccode`\V=\z@ + \lowercase{\endgroup\def\ekvp@novalflag{ZNoValue:}}% + } +\def\ekvpIfNoVal#1% + {% + \long\def\ekvpIfNoVal##1% + {% + \ekvp@ifnoval{}##1{}?!#1??!\ekv@ifempty@B\ekv@ifempty@true + \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo + }% + \long\def\ekvp@ifnoval##1#1##2?##3?!{\ekv@ifempty@\ekv@ifempty@A##1##2}% + \long\def\ekvp@k@default##1##2##3{\ekvp@p##1{##2}{##3}{#1}}% + } +\expandafter\ekvpIfNoVal\expandafter{\ekvp@novalflag} +\let\ekvp@novalflag\ekvp@undefined +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +%^^A=<< +% +% \subsubsection{Assertions}^^A>>= +% +% Assertions are just wrappers around user provided code to throw errors. Each +% of them +% +% \begin{macro}{\ekvpAssertIf,\ekvpAssertIfNot} +% \begin{macro}[internal]{\ekvpAssertIf@,\ekvpAssertIf@good,\ekvpAssert@notgood} +% \begin{macrocode} +\def\ekvpAssertIf{\romannumeral\ekv@alignsafe\ekvoptarg{\ekvpAssertIf@{}}{EOT}} +\def\ekvpAssertIfNot + {\romannumeral\ekv@alignsafe\ekvoptarg{\ekvpAssertIf@\else}{EOT}} +\long\def\ekvpAssertIf@#1#2#3% + {% + #3#1\ekvpAssertIf@good\fi + \ekvpAssert@notgood{#2}% + } +\long\def\ekvpAssertIf@good\fi\ekvpAssert@notgood#1#2% + {\fi\ekv@endalignsafe\ekv@zero} +\long\def\ekvpAssert@notgood#1% + {% + \ekv@ifdefined{ekvpAssert@@#1}% + {\csname ekvpAssert@@#1\endcsname}% + {\ekvp@err@unknownmarker{#1}\ekvpAssert@@EOA}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvpAssertTF,\ekvpAssertTFNot} +% \begin{macro}[internal]{\ekvpAssertTF@} +% \begin{macrocode} +\def\ekvpAssertTF{\ekvoptarg{\ekvpAssertTF@{}}{EOT}} +\def\ekvpAssertTFNot{\ekvoptarg{\ekvpAssertTF@{\@secondoftwo\@firstoftwo}}{EOT}} +\long\def\ekvpAssertTF@#1#2#3% + {% + \ekv@alignsafe + #3#1% + {\ekv@endalignsafe\@gobble}% + {\romannumeral\ekvpAssert@notgood{#2}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \ekvpAssertValue,\ekvpAssertNoValue,\ekvpAssertOneValue, +% \ekvpAssertTwoValues +% } +% \begin{macro}[internal]{\ekvpAssert@further} +% \begin{macro}[internal]{\ekvp@assert@num@args} +% These here are special cases of assertions. First we define a helper, the +% following might look like a recursive call, but we'll redefine the helper +% later and need this definition only temporary. +% \begin{macrocode} +\ekv@exparg{\def\ekvpAssert@further#1#2#3}% + {\ekvoptarg{\ekvpAssert@further{#1}{#2}{#3}}{EOT}} +% \end{macrocode} +% Now the definitions, we just need to set up the tests and corresponding +% error messages. All this passes through the \cs{ekvpAssertTF} check (which +% we shortcut to its internal auxiliary function here). +% \begin{macrocode} +\ekv@exparg{\def\ekvpAssertValue}% + {\ekvpAssert@further{\@secondoftwo\@firstoftwo}{\ekvpIfNoVal}{missing value}} +\ekv@exparg{\def\ekvpAssertNoValue}% + {\ekvpAssert@further{}{\ekvpIfNoVal}{superfluous value}} +\ekv@exparg{\def\ekvpAssertOneValue}% + {\ekvpAssert@further{}{\ekvp@assert@num@args\@gobble}{argument count != 1}} +\ekv@exparg{\def\ekvpAssertTwoValues}% + {\ekvpAssert@further{}{\ekvp@assert@num@args\@gobbletwo}{argument count != 2}} +\long\def\ekvp@assert@num@args#1#2% + {% + \expandafter\ekv@ifempty@\expandafter\ekv@ifempty@A#1#2\ekv@ifempty@B + \ekv@ifempty@true\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo + } +% \end{macrocode} +% And finally redefine our auxiliary that just does a bit of argument +% reordering for us. +% \begin{macrocode} +\long\def\ekvpAssert@further#1#2#3#4#5{\ekvpAssertTF@{#1}{#4}{#2{#5}}{#3}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvpAssert@@EOP,\ekvpAssert@@EOT,\ekvpAssert@@EOA} +% \begin{macrocode} +\def\ekvpAssert@@EOA#1#2% + {\long\def#1##1##2#2##3{\ekvp@err{##1 at `##3'}\ekv@endalignsafe\ekv@zero}} +\ekvpAssert@@EOA\ekvpAssert@@EOP\ekvpEOP +\ekvpAssert@@EOA\ekvpAssert@@EOT\ekvpEOT +\ekvpAssert@@EOA\ekvpAssert@@EOA\ekvpEOA +% \end{macrocode} +% \end{macro} +% +%^^A=<< +% +% \subsubsection{Markers}^^A>>= +% +% \begin{macro} +% {\ekvpGobbleP,\ekvpGobbleT,\ekvpGobbleA,\ekvpEOP,\ekvpEOT,\ekvpEOA} +% Since after the |\ekvpEOT|, \emph{etc.} markers the full element is placed, +% they are defined as |\@gobble|, and the functions gobbling up to them gobble +% that argument as well. +% \begin{macrocode} +\long\def\ekvpGobbleP#1\ekvpEOP#2{} +\long\def\ekvpGobbleT#1\ekvpEOT#2{} +\long\def\ekvpGobbleA#1\ekvpEOA#2{} +\let\ekvpEOP\@gobble +\let\ekvpEOT\@gobble +\let\ekvpEOA\@gobble +% \end{macrocode} +% \end{macro} +% +%^^A=<< +% +% \subsubsection{Miscellaneous Auxiliaries}^^A>>= +% +% \begin{macro}{\ekvpProtect} +% \begin{macro}[internal]{\ekvpProtect@,\ekvpProtect@@} +% The idea of this macro is pretty simple. In contexts in which a |\protected| +% macro isn't expanded the |\ekvpProtect@@| expansion will just expand to +% itself and in the process protect the argument using |\unexpanded|. If a +% |\protected| macro would be expanded the |\ekvpProtect@| will remove +% |\ekvpProtect@@| and unpack the argument. +% \begin{macrocode} +\def\ekvpProtect{\ekvpProtect@\ekvpProtect@@} +\protected\long\def\ekvpProtect@\ekvpProtect@@#1{#1} +\long\def\ekvpProtect@@#1{\ekv@unexpanded{\ekvpProtect@@{#1}}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +%^^A=<< +% +% \subsubsection{Error messages}^^A>>= +% +% \begin{macro}[internal] +% { +% \ekvp@err,\ekvp@err@unknownparser,\ekvp@err@noval,\ekvp@err@missingtype, +% \ekvp@errm +% } +% These should be pretty straight forward. We use |\ekverr| to throw +% expandable errors. +% \begin{macrocode} +\protected\long\def\ekvp@errm#1{\errmessage{expkv-pop Error: #1}} +\ekv@exparg{\long\def\ekvp@err#1}{\ekverr{expkv-pop}{#1}} +\def\ekvp@err@unknownparser#1{\ekvp@err{unknown parser `#1'}} +\long\def\ekvp@err@noval#1#2#3{\ekvp@err{missing value for `#3'}} +\long\def\ekvp@err@missingtype#1#2#3#4{\ekvp@err{missing type in `#3'}} +\long\def\ekvp@err@unknownmarker#1{\ekvp@err{unknown marker `#1'}} +% \end{macrocode} +% \end{macro} +% +%^^A=<< +% +% Undefine the no longer needed |\ekvp@prefix| and restore the category code of +% |@|: +% \begin{macrocode} +\let\ekvp@prefix\ekvp@undefined +\catcode`\@=\ekvp@tmp +% \end{macrocode} +% +% \gobbledocstriptag +%</tex> +%^^A=<< diff --git a/macros/generic/expkv-bundle/expkv.dtx b/macros/generic/expkv-bundle/expkv.dtx new file mode 100644 index 0000000000..1c883b4346 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv.dtx @@ -0,0 +1,2375 @@ +% \iffalse meta-comment +% +% File: expkv.dtx 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 +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\expkvDocNoGenerate{} +\input expkv-bundle.ins +\generate{\file{expkv.sty}{\from{expkv.dtx}{pkg}}} +\generate{\file{expkv.tex}{\from{expkv.dtx}{tex}}} +\generate{\file{t-expkv.tex}{\from{expkv.dtx}{ctx}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkv} +%^^A the LaTeX package >>= +% \subsection{The \LaTeX\ Package} +% First we set up the \LaTeX\ package. That one doesn't really do much except +% |\input|ting the generic code and identifying itself as a package. Additional +% an |all| option is declared to load all the sub modules in one go. +% \gobbledocstriptag +%<*pkg> +% \begin{macrocode} +\def\ekv@tmp + {% + \ProvidesFile{expkv.tex}% + [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]% + } +\input{expkv.tex} +\ProvidesPackage{expkv}% + [\ekvDate\space v\ekvVersion\space an expandable key=val implementation] +\let\ekv@tmp\@empty +\DeclareOption{all} + {% + \def\ekv@tmp + {\RequirePackage{expkv-pop,expkv-cs,expkv-def,expkv-opt}}% + } +\ProcessOptions\relax +\ekv@tmp +% \end{macrocode} +% \gobbledocstriptag +%</pkg> +%^^A=<< +%^^A the ConTeXt module >>= +% \subsection{The \ConTeXt\ module} +% This is pretty straight forward, we just have to change the error throwing +% mechanism for \ConTeXt\ (the approach taken for plain and \LaTeX\ breaks in +% \ConTeXt, effectively breaking \ConTeXt, dropping you in an interactive \TeX\ +% session with almost no means of escape). +% \gobbledocstriptag +%<*ctx> +% \begin{macrocode} +\writestatus{loading}{ConTeXt User Module / expkv} +\unprotect +\input expkv.tex +\long\def\ekv@err@collect#1\par#2% + {\directlua{tex.error[[\detokenize{#2} Error: #1]]}} +\writestatus{loading} + {ConTeXt User Module / expkv / Version \ekvVersion\space loaded} +\protect\endinput +% \end{macrocode} +% \gobbledocstriptag +%</ctx> +%^^A=<< +%^^A main file >>= +% \subsection{The Generic Code} +% The rest of this implementation will be the generic code. +% \gobbledocstriptag +%<*tex> +% +% We make sure that it's only input once: +% \begin{macrocode} +\expandafter\ifx\csname ekvVersion\endcsname\relax +\else + \expandafter\endinput +\fi +% \end{macrocode} +% +% Check whether \eTeX\ and |\expanded| are available -- \expkv\ requires \eTeX. +% \begin{macrocode} +\begingroup + \edef\ekvtmpa{\string\expanded} + \edef\ekvtmpb{\meaning\expanded} + \expandafter +\endgroup +\ifx\ekvtmpa\ekvtmpb + \expandafter\let\csname ekv@expanded\endcsname\expanded + \expandafter\let\csname ekv@unexpanded\endcsname\unexpanded +\else + \begingroup + \edef\ekvtmpa{\string\expanded} + \edef\ekvtmpb{\meaning\normalexpanded} + \expandafter + \endgroup + \ifx\ekvtmpa\ekvtmpb + \expandafter\let\csname ekv@expanded\endcsname\normalexpanded + \expandafter\let\csname ekv@unexpanded\endcsname\normalunexpanded + \else + \errmessage + {expkv Error: e-TeX and the \noexpand\expanded primitive required}% + \expandafter\endinput + \fi +\fi +% \end{macrocode} +% +% \begin{macro}{\ekvVersion,\ekvDate} +% We're on our first input, so lets store the version and date in a macro. +% \begin{macrocode} +\def\ekvVersion{2.0} +\def\ekvDate{2023-01-23} +% \end{macrocode} +% \end{macro} +% +% If the \LaTeX\ format is loaded we want to be a good file and report back who +% we are, for this the package will have defined |\ekv@tmp| to use +% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. +% \begin{macrocode} +\csname ekv@tmp\endcsname +% \end{macrocode} +% +% Store the category code of |@| to later be able to reset it and change it to +% 11 for now. +% \begin{macrocode} +\expandafter\chardef\csname ekv@tmp\endcsname=\catcode`\@ +\catcode`\@=11 +% \end{macrocode} +% |\ekv@tmp| might later be reused to gobble any prefixes which might be +% provided to |\ekvdef| and similar in case the names are invalid, we just +% temporarily use it here as means to store the current category code of |@| to +% restore it at the end of the file, we never care for the actual definition of +% it. +% +% \begin{macro}[internal]{\ekv@if@lastnamedcs} +% If the primitive |\lastnamedcs| is available, we can be a bit faster than +% without it. So we test for this and save the test's result in this macro. +% \begin{macrocode} +\begingroup + \edef\ekv@tmpa{\string \lastnamedcs} + \edef\ekv@tmpb{\meaning\lastnamedcs} + \ifx\ekv@tmpa\ekv@tmpb + \def\ekv@if@lastnamedcs{\long\def\ekv@if@lastnamedcs##1##2{##1}} + \else + \def\ekv@if@lastnamedcs{\long\def\ekv@if@lastnamedcs##1##2{##2}} + \fi + \expandafter +\endgroup +\ekv@if@lastnamedcs +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@empty} +% Sometimes we have to introduce a token to prevent accidental brace stripping. +% This token would then need to be removed by |\@gobble| or similar. Instead we +% can use |\ekv@empty| which will just expand to nothing, that is faster than +% gobbling an argument. +% \begin{macrocode} +\def\ekv@empty{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \@gobble,\@gobbletwo,\@gobblethree, +% \@firstofone,\@firstoftwo,\@secondoftwo, +% \ekv@fi@gobble,\ekv@fi@firstofone,\ekv@fi@firstoftwo,\ekv@fi@secondoftwo, +% \ekv@gobble@mark,\ekv@gobbleto@stop,\ekv@gobble@from@mark@to@stop +% } +% Since branching tests are often more versatile than |\if...\else...\fi| +% constructs, we define helpers that are branching pretty fast. Also here are +% some other utility functions that just grab some tokens. The ones that are +% also contained in \LaTeX\ don't use the |ekv| prefix. Not all of the ones +% defined here are really needed by \expkv\ but are provided because packages +% like \expkvd\ or \expkvo\ need them (and I don't want to define them in each +% package which might need them). +% \begin{macrocode} +\long\def\@gobble#1{} +\long\def\@gobbletwo#1#2{} +\long\def\@gobblethree#1#2#3{} +\long\def\@firstofone#1{#1} +\long\def\@firstoftwo#1#2{#1} +\long\def\@secondoftwo#1#2{#2} +\long\def\ekv@fi@gobble\fi\@firstofone#1{\fi} +\long\def\ekv@fi@firstofone\fi\@gobble#1{\fi#1} +\long\def\ekv@fi@firstoftwo\fi\@secondoftwo#1#2{\fi#1} +\long\def\ekv@fi@secondoftwo\fi\@firstoftwo#1#2{\fi#2} +\def\ekv@gobble@mark\ekv@mark{} +\long\def\ekv@gobbleto@stop#1\ekv@stop{} +\long\def\ekv@gobble@from@mark@to@stop\ekv@mark#1\ekv@stop{} +% \end{macrocode} +% \end{macro} +% As you can see |\ekv@gobbleto@stop| uses a special marker |\ekv@stop|. The +% package will use three such markers, the one you've seen already, |\ekv@mark| +% and |\ekv@nil|. Contrarily to how for instance \pkg{expl3} does things, we +% don't define them, as we don't need them to have an actual meaning. This has +% the advantage that if they somehow get expanded -- which should never happen +% if things work out -- they'll throw an error directly. +% +% \begin{macro}[internal] +% { +% \ekv@ifempty,\ekv@ifempty@,\ekv@ifempty@true,\ekv@ifempty@false, +% \ekv@ifempty@true@F,\ekv@ifempty@true@F@gobble, +% \ekv@ifempty@true@F@gobbletwo +% } +% We can test for a lot of things building on an if-empty test, so lets define a +% really fast one. Since some tests might have reversed logic (true if something +% is not empty) we also set up macros for the reversed branches. +% \begin{macrocode} +\long\def\ekv@ifempty#1% + {% + \ekv@ifempty@\ekv@ifempty@A#1\ekv@ifempty@B\ekv@ifempty@true + \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo + } +\long\def\ekv@ifempty@#1\ekv@ifempty@A\ekv@ifempty@B{} +\long\def\ekv@ifempty@true\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo#1#2{#1} +\long\def\ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo#1#2{#2} +\long\def\ekv@ifempty@true@F\ekv@ifempty@A\ekv@ifempty@B\@firstofone#1{} +\long\def\ekv@ifempty@true@F@gobble\ekv@ifempty@A\ekv@ifempty@B\@firstofone#1#2% + {} +\long\def\ekv@ifempty@true@F@gobbletwo + \ekv@ifempty@A\ekv@ifempty@B\@firstofone#1#2#3% + {} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekv@ifblank,\ekv@ifblank@,\ekv@ifblank@false,\ekv@ifblank@gobbletrue} +% The obvious test that can be based on an if-empty is if-blank, meaning a test +% checking whether the argument is empty or consists only of spaces. But instead +% of building atop |\ekv@ifempty| our version will utilise the fact that the +% argument in none of our usages can contain |\ekv@stop| (due to the way we +% check for end-of-list), and hence we can use an even faster check. This here +% works due to the fact that \TeX\ will read at least one token (or a group) for +% an undelimited parameter which might be the end marker of the delimited next +% parameter. So if |#1| to |\ekv@ifblank| is indeed blank, |#1| of +% |\ekv@ifblank@| will be the placed |\ekv@stop| and |#2| will be anything until +% the next |\ekv@stop|. The wrapper |\ekv@ifblank| will not be used by \expkv\ +% for speed reasons but \expkvo\ uses it. Also, not only a |TF| variant is +% provided but also a rather strange one that assumes that only the |F| branch +% is provided and that this branch consists only of a single token. +% \begin{macrocode} +\long\def\ekv@ifblank#1% + {\ekv@ifblank@#1\ekv@stop\ekv@ifblank@false\ekv@stop\@firstoftwo} +\long\def\ekv@ifblank@\ekv@mark#1#2\ekv@stop{} +\long\def\ekv@ifblank@false\ekv@stop\@firstoftwo#1#2{#2} +\def\ekv@ifblank@gobbletrue\ekv@stop#1{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@ifdefined} +% We'll need to check whether something is defined quite frequently, so why not +% define a macro that does this. The following test is expandable and pretty +% fast. The version with |\lastnamedcs| is the fastest version to test for an +% undefined macro I know of (that considers both undefined macros and those with +% the meaning |\relax|). +% \begin{macrocode} +\ekv@if@lastnamedcs + {% + \long\def\ekv@ifdefined#1{\ifcsname#1\endcsname\ekv@ifdef@\fi\@secondoftwo} + \def\ekv@ifdef@\fi\@secondoftwo + {% + \fi + \expandafter\ifx\lastnamedcs\relax + \ekv@fi@secondoftwo + \fi + \@firstoftwo + } + } + {% + \long\def\ekv@ifdefined#1% + {% + \ifcsname#1\endcsname\ekv@ifdef@\fi\ekv@ifdef@false#1\endcsname\relax + \ekv@fi@secondoftwo + \fi + \@firstoftwo + } + \def\ekv@ifdef@\fi\ekv@ifdef@false{\fi\expandafter\ifx\csname} + \long\def\ekv@ifdef@false + #1\endcsname\relax\ekv@fi@secondoftwo\fi\@firstoftwo#2#3% + {#3} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@strip,\ekv@strip@a,\ekv@strip@b,\ekv@strip@c} +% We borrow some ideas of \pkg{expl3}'s \pkg{l3tl} to strip spaces +% from keys and values. This |\ekv@strip| also strips one level of outer braces +% \emph{after} stripping spaces, so an input of | {abc} | becomes |abc| after +% stripping. It should be used with |#1| prefixed by |\ekv@mark|. Also this +% implementation at most strips \emph{one} space from both sides (which should +% be fine most of the time, since \TeX\ reads consecutive spaces as a single one +% during tokenisation). +% \begin{macrocode} +\def\ekv@strip#1% + {% + \long\def\ekv@strip##1% + {% + \ekv@strip@a + ##1\ekv@nil + \ekv@mark#1% + #1\ekv@nil + }% + \long\def\ekv@strip@a##1\ekv@mark#1{\ekv@strip@b##1\ekv@mark}% + } +\ekv@strip{ } +\long\def\ekv@strip@b#1 \ekv@nil{\ekv@strip@c#1\ekv@nil} +\long\def\ekv@strip@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil#3{#3{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekv@strip@key,\ekv@strip@key@a,\ekv@strip@key@b,\ekv@strip@key@c} +% Same idea and code as |\ekv@strip|, but the second argument to +% |\ekv@strip@key| must be a single token and the result of the stripping will +% be |\detokenize|d. +% \begin{macrocode} +\def\ekv@strip@key#1% + {% + \long\def\ekv@strip@key##1{\ekv@strip@key@a##1\ekv@nil\ekv@mark#1#1\ekv@nil} + \long\def\ekv@strip@key@a##1\ekv@mark#1{\ekv@strip@key@b##1\ekv@mark} + } +\ekv@strip@key{ } +\long\def\ekv@strip@key@b#1 \ekv@nil{\ekv@strip@key@c#1\ekv@nil} +\long\def\ekv@strip@key@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil#3% + {\expandafter#3\detokenize{#1}\ekv@mark{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@exparg,\ekv@exparg@, +% \ekv@expandedarg,\ekv@fexparg,\ekv@expandafter +% \ekv@expargtwice,\ekv@expargtwice@,\ekv@zero +% } +% To reduce some code doublets while gaining some speed (and also as convenience +% for other packages in the family), it is often useful to expand the first +% token in a definition once. Let's define a wrapper for this. And while we're +% at it, also define a helper for |f|-~and |e|-expansion as well. +% +% Also, to end a |\romannumeral| expansion, we want to use |\z@|, which is +% contained in both plain \TeX\ and \LaTeX, but we use a private name for it to +% make it easier to spot and hence easier to manage. +% \begin{macrocode} +\let\ekv@zero\z@ +\long\def\ekv@exparg#1#2% + {\ekv@expanded{\ekv@unexpanded{#1}\expandafter}\expandafter{#2}} +\long\def\ekv@expandafter#1{\ekv@expanded{\ekv@unexpanded{#1}\expandafter}} +\long\def\ekv@exparg@#1#2{#2{#1}}% +\long\def\ekv@expandedarg#1#2{\ekv@expanded{\ekv@unexpanded{#1}{#2}}} +\long\def\ekv@fexparg#1#2% + {% + \ekv@expanded{\ekv@unexpanded{#1}\expandafter}\expandafter + {\romannumeral`\^^@#2}% + } +\long\def\ekv@expargtwice#1#2{\expandafter\ekv@expargtwice@\expandafter{#2}{#1}} +\def\ekv@expargtwice@{\expandafter\ekv@exparg@\expandafter} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcsvloop} +% \begin{macro}[internal]{\ekv@csv@loop@active,\ekv@csv@loop@active@end} +% An |\ekvcsvloop| will just loop over a csv list in a simple manner. First we +% split at active commas (gives better performance this way), next we have to +% check whether we're at the end of the list (checking for |\ekv@stop|). If +% not we go on splitting at commas of category other. +% \begin{macrocode} +\begingroup +\def\ekvcsvloop#1{% +\endgroup +\long\def\ekvcsvloop##1##2% + {\ekv@csv@loop@active{##1}\ekv@mark##2#1\ekv@stop#1} +% \end{macrocode} +% This does the same as |\ekv@csv@loop| but for active commas. +% \begin{macrocode} +\long\def\ekv@csv@loop@active##1##2#1% + {% + \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@active@end\ekv@stop + \ekv@csv@loop{##1}##2,\ekv@stop,% + }% +\long\def\ekv@csv@loop@active@end + \ekv@stop + \ekv@csv@loop##1\ekv@mark\ekv@stop,\ekv@stop,% + {}% +} +% \end{macrocode} +% Do the definitions with the weird catcode. +% \begin{macrocode} +\catcode`\,=13 +\ekvcsvloop, +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@csv@loop,\ekv@csv@loop@do,\ekv@csv@loop@end} +% We use temporary macros and an |\expandafter| chain to preexpand +% |\ekv@strip| here. After splitting at other commas we check again for end +% the end of the sublist, check for blank elements which should be ignored, +% and else strip spaces and execute the user code (protecting it from further +% expanding with |\unexpanded|). +% \begin{macrocode} +\def\ekv@csv@loop#1% + {% + \long\def\ekv@csv@loop##1##2,% + {% + \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@end\ekv@stop + \ekv@ifblank@##2\ekv@stop\ekv@ifblank@gobbletrue + \ekv@stop\ekv@csv@loop@blank + #1\ekv@csv@loop@do{##1}% + }% + } +\expandafter\ekv@csv@loop\expandafter{\ekv@strip{#2}} +\long\def\ekv@csv@loop@do#1#2{\ekv@unexpanded{#2{#1}}\ekv@csv@loop{#2}\ekv@mark} +\def\ekv@csv@loop@end#1% + {% + \long\def\ekv@csv@loop@end + \ekv@stop + \ekv@ifblank@\ekv@mark\ekv@stop\ekv@stop\ekv@ifblank@gobbletrue + \ekv@stop\ekv@csv@loop@blank + #1\ekv@csv@loop@do##1% + {\ekv@csv@loop@active{##1}\ekv@mark}% + } +\expandafter\ekv@csv@loop@end\expandafter{\ekv@strip{\ekv@mark\ekv@stop}} +\long\expandafter\def\expandafter\ekv@csv@loop@blank + \ekv@strip{\ekv@mark#1}\ekv@csv@loop@do#2% + {\ekv@csv@loop{#2}\ekv@mark} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekv@name,\ekv@name@set,\ekv@name@key} +% The keys will all follow the same naming scheme, so we define it here. +% \begin{macrocode} +\def\ekv@name@set#1{ekv#1(} +\long\def\ekv@name@key#1{#1)} +\edef\ekv@name + {% + \ekv@unexpanded\expandafter{\ekv@name@set{#1}}% + \ekv@unexpanded\expandafter{\ekv@name@key{\detokenize{#2}}}% + } +\long\ekv@exparg{\def\ekv@name#1#2}{\ekv@name} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@undefined@set} +% We can misuse the macro name we use to expandably store the set-name in a +% single token -- since this increases performance drastically, especially for +% long set-names -- to throw a more meaningful error message in case a set isn't +% defined. The name of |\ekv@undefined@set| is a little bit misleading, as it is +% called in either case inside of |\csname|, but the result will be a control +% sequence with meaning |\relax| if the set is undefined, hence will break the +% |\csname| building the key-macro which will throw the error message. +% \begin{macrocode} +\def\ekv@undefined@set#1{! expkv Error: Set `#1' undefined.} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@checkvalid} +% We place some restrictions on the allowed names, though, namely sets and +% keys are not allowed to be empty -- blanks are fine (meaning \mbox{set- +% or} key-names consisting of spaces). The |\def\ekv@tmp| gobbles any \TeX\ +% prefixes which would otherwise throw errors. This will, however, break the +% package if an |\outer| has been gobbled this way. I consider that good, +% because keys shouldn't be defined |\outer| anyways. +% \begin{macrocode} +\edef\ekv@checkvalid + {% + \ekv@unexpanded\expandafter{\ekv@ifempty{#1}}% + \ekv@unexpanded + {{% + \def\ekv@tmp{}% + \ekv@errm{empty set name not allowed}% + }}% + {% + \ekv@unexpanded\expandafter{\ekv@ifempty{#2}}% + \ekv@unexpanded + {% + {% + \def\ekv@tmp{}% + \ekv@errm{empty key name not allowed}% + }% + \@secondoftwo + }% + }% + \ekv@unexpanded{\@gobble}% + } +\ekv@exparg{\protected\def\ekv@checkvalid#1#2}{\ekv@checkvalid}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvifdefined,\ekvifdefinedNoVal} +% And provide user-level macros to test whether a key is defined. +% \begin{macrocode} +\ekv@expargtwice{\long\def\ekvifdefined#1#2}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}}} +\ekv@expargtwice{\long\def\ekvifdefinedNoVal#1#2}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}N}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal, +% \ekvdefunknown,\ekvdefunknownNoVal,\ekvletunknown,\ekvletunknownNoVal +% } +% Set up the key defining macros |\ekvdef| etc. We use temporary macros to set +% these up with a few expansions already done. +% \begin{macrocode} +\def\ekvdef#1#2#3#4#5#6% + {% + \protected\long\def\ekvdef##1##2##3% + {#1{\expandafter\def\csname#2\endcsname####1{##3}#4}}% + \protected\long\def\ekvdefNoVal##1##2##3% + {#1{\expandafter\def\csname#2N\endcsname{##3}#4}}% + \protected\long\def\ekvlet##1##2##3% + {#1{\expandafter\let\csname#2\endcsname##3#4}}% + \protected\long\def\ekvletNoVal##1##2##3% + {#1{\expandafter\let\csname#2N\endcsname##3#4}}% + \ekv@expargtwice{\protected\long\def\ekv@defunknown##1##2##3##4}% + {% + \romannumeral + \ekv@exparg + {\ekv@zero\ekv@checkvalid{##3}.}% + {% + \expandafter\expandafter\expandafter + \def\expandafter\csname\ekv@name{##3}{}u##1\endcsname##2{##4}% + #6% + }% + }% + \ekv@expargtwice{\protected\long\def\ekv@letunknown##1##2##3}% + {% + \romannumeral + \ekv@exparg + {\ekv@zero\ekv@checkvalid{##2}.}% + {% + \expandafter\expandafter\expandafter + \let\expandafter\csname\ekv@name{##2}{}u##1\endcsname##3% + #5% + }% + }% + \protected\long\def\ekvletkv##1##2##3##4% + {% + #1% + {% + \expandafter\let\csname#2\expandafter\endcsname + \csname#3\endcsname + #4% + }% + }% + \protected\long\def\ekvletkvNoVal##1##2##3##4% + {% + #1% + {% + \expandafter\let\csname#2N\expandafter\endcsname + \csname#3N\endcsname + #4% + }% + }% + } +\edef\ekvdefNoVal + {% + {\ekv@unexpanded\expandafter{\ekv@checkvalid{#1}{#2}}}% + {\ekv@unexpanded\expandafter{\ekv@name{#1}{#2}}}% + {\ekv@unexpanded\expandafter{\ekv@name{#3}{#4}}}% + {% + \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}% + \ekv@unexpanded\expandafter{\ekv@undefined@set{#1}\endcsname{#1}}% + }% + {% + \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}% + \ekv@unexpanded\expandafter{\ekv@undefined@set{#2}\endcsname{#2}}% + }% + {% + \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}% + \ekv@unexpanded\expandafter{\ekv@undefined@set{#3}\endcsname{#3}}% + }% + } +\expandafter\ekvdef\ekvdefNoVal +\ekv@exparg{\protected\long\def\ekvdefunknown#1#2}% + {\ekv@defunknown{}{##1##2##3}{#1}{#2}} +\ekv@exparg{\protected\long\def\ekvdefunknownNoVal#1#2}% + {\ekv@defunknown{N}{##1##2}{#1}{#2}} +\ekv@exparg{\protected\long\def\ekvletunknown#1#2}% + {\ekv@letunknown{}{#1}{#2}} +\ekv@exparg{\protected\long\def\ekvletunknownNoVal#1#2}% + {\ekv@letunknown{N}{#1}{#2}} +\let\ekv@defunknown\ekv@undefined +\let\ekv@letunknown\ekv@undefined +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvredirectunknown,\ekvredirectunknownNoVal} +% \begin{macro}[internal] +% { +% \ekv@defredirectunknown,\ekv@redirectunknown@aux, +% \ekv@redirectunknownNoVal@aux +% } +% The redirection macros prepare the unknown function by looping over the +% provided list of sets and leaving a |\ekv@redirect@kv| or |\ekv@redirect@k| +% for each set. Only the first of these internals will receive the \key\ and +% \val\ as arguments. +% \begin{macrocode} +\protected\def\ekv@defredirectunknown#1#2#3#4#5#6% + {% + \begingroup + \edef\ekv@tmp + {% + \ekvcsvloop#1{#6}% + \ekv@unexpanded{#2}% + {\ekvcsvloop{}{#5,#6}}% + }% + \ekv@expargtwice + {\endgroup\long#3{#5}}% + {\expandafter#4\ekv@tmp\ekv@stop}% + } +\ekv@exparg{\protected\def\ekvredirectunknown#1#2}% + {% + \ekv@defredirectunknown + \ekv@redirect@kv + \ekv@err@redirect@kv@notfound + \ekvdefunknown + \ekv@redirectunknown@aux + {#1}{#2}% + } +\ekv@exparg{\protected\def\ekvredirectunknownNoVal#1#2}% + {% + \ekv@defredirectunknown + \ekv@redirect@k + \ekv@err@redirect@k@notfound + \ekvdefunknownNoVal + \ekv@redirectunknownNoVal@aux + {#1}{#2}% + } +\def\ekv@redirectunknown@aux#1{#1{##1}{##2}} +\def\ekv@redirectunknownNoVal@aux#1{#1{##1}} +\let\ekv@defredirectunknown\ekv@undefined +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@redirect@k,\ekv@redirect@k@a,\ekv@redirect@k@a@, +% \ekv@redirect@k@b,\ekv@redirect@k@c,\ekv@redirect@k@d, +% \ekv@redirect@kv,\ekv@redirect@kv@a,\ekv@redirect@kv@a@, +% \ekv@redirect@kv@b,\ekv@redirect@kv@c,\ekv@redirect@kv@d +% } +% The redirect code works by some simple loop over all the sets, which we +% already preprocessed in |\ekv@defredirectunknown|. For some optimisation we +% blow this up a bit code wise, essentially, all this does is |\ekvifdefined| +% or |\ekvifdefinedNoVal| in each set, if there is a match gobble the +% remainder of the specified sets and execute the key macro, else go on with +% the next set (to which the \key\ and \val\ are forwarded). +% +% First we set up some code which is different depending on |\lastnamedcs| +% being available or not. All this is stored in a temporary macro to have +% pre-expanded |\ekv@name| constellations ready. +% \begin{macrocode} +\def\ekv@redirect@k#1#2#3#4% + {% + \ekv@if@lastnamedcs + {% + \def\ekv@redirect@k##1##2##3% + {% + \ifcsname#1\endcsname\ekv@redirect@k@a\fi + ##3{##1}% + }% + \def\ekv@redirect@k@a\fi{\fi\expandafter\ekv@redirect@k@b\lastnamedcs}% + \long\def\ekv@redirect@kv##1##2##3##4% + {% + \ifcsname#2\endcsname\ekv@redirect@kv@a\fi\@gobble{##1}% + ##4{##1}{##2}% + }% + \def\ekv@redirect@kv@a\fi\@gobble + {\fi\expandafter\ekv@redirect@kv@b\lastnamedcs}% + } + {% + \def\ekv@redirect@k##1##2##3% + {% + \ifcsname#1\endcsname\ekv@redirect@k@a\fi\ekv@redirect@k@a@ + #1\endcsname + ##3{##1}% + }% + \def\ekv@redirect@k@a@#3\endcsname{}% + \def\ekv@redirect@k@a\fi\ekv@redirect@k@a@ + {\fi\expandafter\ekv@redirect@k@b\csname}% + \long\def\ekv@redirect@kv##1##2##3##4% + {% + \ifcsname#2\endcsname\ekv@redirect@kv@a\fi\ekv@redirect@kv@a@ + #2\endcsname{##1}% + ##4{##1}{##2}% + }% + \long\def\ekv@redirect@kv@a@#4\endcsname##3{}% + \def\ekv@redirect@kv@a\fi\ekv@redirect@kv@a@ + {\fi\expandafter\ekv@redirect@kv@b\csname}% + }% + } +% \end{macrocode} +% The key name given to this loop will already be |\detokenize|d by |\ekvset|, +% so we can safely remove the |\detokenize| here for some performance gain. +% \begin{macrocode} +\def\ekv@redirect@kv#1\detokenize#2#3\ekv@stop{\ekv@unexpanded{#1#2#3}} +\edef\ekv@redirect@kv + {% + {\expandafter\ekv@redirect@kv\ekv@name{#2}{#1}N\ekv@stop}% + {\expandafter\ekv@redirect@kv\ekv@name{#3}{#2}\ekv@stop}% + {\expandafter\ekv@redirect@kv\ekv@name{#1}{#2}N\ekv@stop}% + {\expandafter\ekv@redirect@kv\ekv@name{#1}{#2}\ekv@stop}% + } +% \end{macrocode} +% Everything is ready to make the real definitions. +% \begin{macrocode} +\expandafter\ekv@redirect@k\ekv@redirect@kv +% \end{macrocode} +% The remaining macros here are independent on |\lastnamedcs|, starting from +% the |@b| we know that there is a hash table entry, and get the macro as a +% parameter. We still have to test whether the macro is |\relax|, depending on +% the result of that test we have to either remove the remainder of the +% current test, or the remainder of the set list and invoke the macro. +% \begin{macrocode} +\def\ekv@redirect@k@b#1% + {\ifx\relax#1\ekv@redirect@k@c\fi\ekv@redirect@k@d#1} +\def\ekv@redirect@k@c\fi\ekv@redirect@k@d#1{\fi} +\def\ekv@redirect@k@d#1#2\ekv@stop{#1} +\def\ekv@redirect@kv@b#1% + {\ifx\relax#1\ekv@redirect@kv@c\fi\ekv@redirect@kv@d#1} +\long\def\ekv@redirect@kv@c\fi\ekv@redirect@kv@d#1#2{\fi} +\long\def\ekv@redirect@kv@d#1#2#3\ekv@stop{#1{#2}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@defsetmacro} +% In order to enhance the speed the set name given to |\ekvset| will be turned +% into a control sequence pretty early, so we have to define that control +% sequence. +% \begin{macrocode} +\edef\ekv@defsetmacro + {% + \ekv@unexpanded{\ifx#1\relax\edef#1##1}% + {% + \ekv@unexpanded\expandafter{\ekv@name@set{#2}}% + \ekv@unexpanded\expandafter{\ekv@name@key{##1}}% + }% + \ekv@unexpanded{\fi}% + } +\ekv@exparg{\protected\def\ekv@defsetmacro#1#2}{\ekv@defsetmacro} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvifdefinedset} +% \begin{macrocode} +\ekv@expargtwice{\def\ekvifdefinedset#1}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@undefined@set{#1}}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@ifexp} +% We want to be able to include expansion control. This is started by a +% specific structure, namely if the stuff to the left of the equals sign +% contains a colon followed by a space followed by at least one non-space +% token. There is an additional rule namely the contents in front of the +% \verb*|: | marker mustn't contain spaces outside of braces, but +% unfortunately we can test this only after we did the real split and not as +% part of a fast test. The temporary meaning we set up here will get +% \tkn{:}{13}\vissp{ } and \tkn{:}{11}\vissp{ } as |#1| and |#2|. +% +% The real definition will take three arguments (two curried), namely the +% un-|\detokenize|d name as |#1|, what should be done if the \expnotation\ +% should start as |#2|, and what should be done if no \expnotation\ is found +% as |#3|. +% \begin{macrocode} +\def\ekv@ifexp#1#2% + {% +% \end{macrocode} +% In the following explanations I use \tkn{:}{12}, \tkn{:}{13}, and +% \tkn{:}{11}, but I always mean that token followed by a space. +% \begin{macro}[internal]{\ekv@ifexp@split@o@or@other} +% \tkn{:}{12} splitter also used to test for \tkn{:}{13} and \tkn{:}{11} +% \begin{macrocode} + \long\def\ekv@ifexp@split@o@or@other + ##1: ##2\ekv@stop##3% + ##4#1##5\ekv@stop##6% + ##7#2##8\ekv@stop##9% + {##9##6##3##1\ekv@stop##2}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal]{\ekv@ifexp@split@a@or@l} +% \tkn{:}{13} splitter also used to test for \tkn{:}{11} +% \begin{macrocode} + \long\def\ekv@ifexp@split@a@or@l + ##1#1##2\ekv@stop##3% + ##4#2##5\ekv@stop##6% + {##6##3##1\ekv@stop##2}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% {\ekv@ifexp@split@a,\ekv@ifexp@split@l} +% \tkn{:}{13} splitter +% \begin{macrocode} + \long\def\ekv@ifexp@split@a##1#1##2\ekv@stop##3% + {##3##1\ekv@stop##2}% +% \end{macrocode} +% \tkn{:}{11} splitter +% \begin{macrocode} + \long\def\ekv@ifexp@split@l##1#2##2\ekv@stop##3% + {##3##1\ekv@stop##2}% +% \end{macrocode} +% \end{macro} +% To get good performance in the most common case (standard category codes +% apply) we try to split on \tkn{:}{12}, due to the way this split works there +% are a few possible outcomes: +% \begin{enumerate} +% \item |\ekv@ifexp@none| is the first token if no colon was +% found at all +% \item |\ekv@ifexp@o| is the first token if only \tkn{:}{12} +% were part of \key +% \item |\ekv@ifexp@a| is put in front of either of the two +% aforementioned cases if there was at least one \tkn{:}{13} (in that case +% we don't yet know which was the first colon) +% \item |\ekv@ifexp@o| is put in front of any of the +% aforementioned cases if there was at least one \tkn{:}{11} (in which +% case we also don't know yet which was the first colon) +% \end{enumerate} +% \begin{macrocode} + \long\def\ekv@ifexp##1% + {% + \ekv@ifexp@split@o@or@other + ##1\ekv@nil\ekv@stop\ekv@ifexp@o + : \ekv@stop\ekv@ifexp@none + ##1\ekv@stop\ekv@ifexp@a#1\ekv@stop{}% + ##1\ekv@stop\ekv@ifexp@l#2\ekv@stop{}% + }% +% \end{macrocode} +% \begin{macro}[internal] +% {\ekv@ifexp@o,\ekv@ifexp@none} +% If any of the next two are called we already know the outcome and can +% directly expand to it. +% \begin{macrocode} + \long\def\ekv@ifexp@o##1\ekv@stop + {\ekv@ifexp@done{##1}\ekv@mark}% + \long\def\ekv@ifexp@none + ##1\ekv@nil\ekv@stop\ekv@ifexp@o\ekv@stop + ##2##3% + {##3}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% {\ekv@ifexp@a,\ekv@ifexp@a@i,\ekv@ifexp@a@ii} +% If this is called we know that there was no \tkn{:}{13}, if the first token +% now is |\ekv@ifexp@none| there was no \tkn{:}{12} as well +% \begin{macrocode} + \def\ekv@ifexp@a##1% + {% + \ekv@ifexp@a@i + ##1\ekv@ifexp@a@ii + \ekv@ifexp@none\ekv@ifexp@a@or@o + }% + \def\ekv@ifexp@a@i##1\ekv@ifexp@none{}% +% \end{macrocode} +% Easy route, no \tkn{:}{12}, simply grab and pack +% \begin{macrocode} + \long\def\ekv@ifexp@a@ii + \ekv@ifexp@none\ekv@ifexp@a@or@o + ##1#1##2\ekv@stop\ekv@ifexp@o\ekv@stop + {\ekv@ifexp@done{##1}\ekv@mark##2}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% { +% \ekv@ifexp@a@or@o, +% \ekv@ifexp@a@not@o,\ekv@ifexp@a@not@o@, +% \ekv@ifexp@o@not@a +% } +% other route, there is a \tkn{:}{12}, re-split at \tkn{:}{13} +% \begin{macrocode} + \long\def\ekv@ifexp@a@or@o##1\ekv@stop + {% + \ekv@ifexp@split@a + ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@o + #1\ekv@stop\ekv@ifexp@o@not@a + }% +% \end{macrocode} +% \tkn{:}{13} came earlier than \tkn{:}{12} +% \begin{macrocode} + \long\def\ekv@ifexp@a@not@o##1\ekv@stop + {\ekv@ifexp@a@not@o@{##1}\ekv@mark} + \long\def\ekv@ifexp@a@not@o@ + ##1##2\ekv@nil#1\ekv@stop\ekv@ifexp@o@not@a + {\ekv@ifexp@done{##1}##2: }% +% \end{macrocode} +% \tkn{:}{12} came earlier than \tkn{:}{13} +% \begin{macrocode} + \long\def\ekv@ifexp@o@not@a + ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@o\ekv@stop + {\ekv@ifexp@done{##1}\ekv@mark}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal]{\ekv@ifexp@l,\ekv@ifexp@l@i,\ekv@ifexp@l@ii} +% Now this is the most complicated case, if |\ekv@ifexp@l| is +% called there are a few possibilities: +% \begin{enumerate} +% \item |#1| is |\ekv@ifexp@none|: easiest route, only +% \tkn{:}{11} was there +% \item |#1| is |\ekv@ifexp@o|: complicated route, there was a +% \tkn{:}{11} and a \tkn{:}{12} +% \item |#1| is |\ekv@ifexp@a| and after that comes +% |\ekv@ifexp@none|: more complicated, there was a +% \tkn{:}{11} and a \tkn{:}{13} +% \item |#1| is |\ekv@ifexp@a| and after that comes +% |\ekv@ifexp@o|: cursed route, there were \tkn{:}{11}, +% \tkn{:}{12}, and \tkn{:}{13} +% \end{enumerate} +% \begin{macrocode} + \def\ekv@ifexp@l##1% + {% + \ekv@ifexp@l@i + ##1\ekv@ifexp@l@ii + \ekv@ifexp@none{}% + ##1\ekv@ifexp@l@or@o + \ekv@ifexp@o + \ekv@ifexp@l@or@a + }% + \def\ekv@ifexp@l@i + ##1\ekv@ifexp@none##2% + ##3\ekv@ifexp@o + {##2}% +% \end{macrocode} +% easiest route, just \tkn{:}{11} +% \begin{macrocode} + \long\def\ekv@ifexp@l@ii + \ekv@ifexp@l@or@a##1#2##2\ekv@stop + \ekv@ifexp@o\ekv@stop#2\ekv@stop##3% + {\ekv@ifexp@done{##1}\ekv@mark##2}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% { +% \ekv@ifexp@l@or@o, +% \ekv@ifexp@l@not@o,\ekv@ifexp@l@not@o@, +% \ekv@ifexp@o@not@l +% } +% slightly harder, \tkn{:}{11} or \tkn{:}{12}, re-split at \tkn{:}{11} +% \begin{macrocode} + \long\def\ekv@ifexp@l@or@o + \ekv@ifexp@o\ekv@ifexp@l@or@a + ##1\ekv@stop + {% + \ekv@ifexp@split@l + ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@o + #2\ekv@stop\ekv@ifexp@o@not@l + }% +% \end{macrocode} +% \tkn{:}{11} came earlier than \tkn{:}{12} +% \begin{macrocode} + \long\def\ekv@ifexp@l@not@o##1\ekv@stop + {\ekv@ifexp@l@not@o@{##1}\ekv@mark} + \long\def\ekv@ifexp@l@not@o@ + ##1##2\ekv@nil#2\ekv@stop\ekv@ifexp@o@not@l + ##3#2\ekv@stop##4% + {\ekv@ifexp@done{##1}##2: ##3}% +% \end{macrocode} +% \tkn{:}{12} came earlier than \tkn{:}{11} +% \begin{macrocode} + \long\def\ekv@ifexp@o@not@l + ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@o\ekv@stop + ##2#2\ekv@stop##3% + {\ekv@ifexp@done{##1}\ekv@mark##2}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% { +% \ekv@ifexp@l@or@a, +% \ekv@ifexp@l@or@a@not@o, +% \ekv@ifexp@l@not@a,\ekv@ifexp@l@not@a@, +% \ekv@ifexp@a@not@l +% } +% \tkn{:}{13} was in the mix with \tkn{:}{11}, we need to see whether there +% was a \tkn{:}{12} as well +% \begin{macrocode} + \def\ekv@ifexp@l@or@a##1% + {% +% \end{macrocode} +% don't get confused by the name, this gobbles until |@none| +% \begin{macrocode} + \ekv@ifexp@a@i + ##1\ekv@ifexp@l@or@a@not@o + \ekv@ifexp@none + \ekv@ifexp@l@or@a@or@o + }% +% \end{macrocode} +% phew, no \tkn{:}{12} in the mix, we split at \tkn{:}{13} and see whether an +% earlier \tkn{:}{11} is found +% \begin{macrocode} + \long\def\ekv@ifexp@l@or@a@not@o + \ekv@ifexp@none\ekv@ifexp@l@or@a@or@o + ##1#1% + {% + \ekv@ifexp@split@l + ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@a + #2\ekv@stop\ekv@ifexp@a@not@l + }% + \long\def\ekv@ifexp@l@not@a##1\ekv@stop + {\ekv@ifexp@l@not@a@{##1}\ekv@mark}% + \long\def\ekv@ifexp@l@not@a@ + ##1##2\ekv@nil#2\ekv@stop\ekv@ifexp@a@not@l + ##3\ekv@stop\ekv@ifexp@o\ekv@stop#2\ekv@stop##4% + {\ekv@ifexp@done{##1}##2#1##3}% + \long\def\ekv@ifexp@a@not@l + ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@a\ekv@stop + ##2\ekv@stop\ekv@ifexp@o\ekv@stop#2\ekv@stop##3% + {\ekv@ifexp@done{##1}\ekv@mark##2}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% { +% \ekv@ifexp@l@or@a@or@o,\ekv@ifexp@l@or@a@or@o@i,\ekv@ifexp@l@or@a@or@o@ii, +% \ekv@ifexp@l@or@a@or@o@iii,\ekv@ifexp@l@or@a@or@o@iv, +% \ekv@ifexp@o@not@l@or@a, +% \ekv@ifexp@a@not@l@or@o,\ekv@ifexp@a@not@l@or@o@ +% } +% ooh no, the ugliest path, we got ourself a \tkn{:}{11}, a \tkn{:}{12}, and a +% \tkn{:}{13}, first grab everything left after splitting the first +% \tkn{:}{12} and try a \tkn{:}{13} split +% \begin{macrocode} + \long\def\ekv@ifexp@l@or@a@or@o##1\ekv@stop + {% + \ekv@ifexp@split@a@or@l + ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@l@or@o + #1\ekv@stop\ekv@ifexp@o@not@l@or@a + ##1\ekv@stop\ekv@ifexp@l@or@a@or@o@i#2\ekv@stop{}% + }% +% \end{macrocode} +% \tkn{:}{12} splitting was correct +% \begin{macrocode} + \long\def\ekv@ifexp@o@not@l@or@a + ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@l@or@o\ekv@stop + ##2#2\ekv@stop##3% + {\ekv@ifexp@done{##1}\ekv@mark##2}% +% \end{macrocode} +% \tkn{:}{13} split is correct, \tkn{:}{12} has to be reverted +% \begin{macrocode} + \long\def\ekv@ifexp@a@not@l@or@o##1\ekv@stop + {\ekv@ifexp@a@not@l@or@o@{##1}\ekv@mark} + \long\def\ekv@ifexp@a@not@l@or@o@ + ##1##2\ekv@nil##3#2\ekv@stop##4% + {\ekv@ifexp@done{##1}##2: ##3}% +% \end{macrocode} +% there is still a \tkn{:}{11} left, we need to take care of \tkn{:}{13} as +% well, but \tkn{:}{12} was wrong +% \begin{macrocode} + \def\ekv@ifexp@l@or@a@or@o@i##1% + {% + \ekv@ifexp@l@or@a@or@o@ii + ##1\ekv@ifexp@l@or@a@or@o@iii + \ekv@ifexp@o@not@l@or@a + \ekv@ifexp@why + }% + \def\ekv@ifexp@l@or@a@or@o@ii + ##1\ekv@ifexp@o@not@l@or@a + {}% +% \end{macrocode} +% at least it's not absolutely the worst, \tkn{:}{11} is correct, revert the +% \tkn{:}{12} split +% \begin{macrocode} + \long\def\ekv@ifexp@l@or@a@or@o@iii + \ekv@ifexp@o@not@l@or@a\ekv@ifexp@why + ##1#2% + {\ekv@ifexp@l@or@a@or@o@iv{##1}\ekv@mark}% + \long\def\ekv@ifexp@l@or@a@or@o@iv + ##1##2\ekv@nil\ekv@stop\ekv@ifexp@a@not@l@or@o\ekv@stop + #2\ekv@stop##3##4#2\ekv@stop##5% + {\ekv@ifexp@done{##1}##2: ##4}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% { +% \ekv@ifexp@why, +% \ekv@ifexp@why@l,\ekv@ifexp@why@l@i,\ekv@ifexp@why@l@ii, +% \ekv@ifexp@why@a +% } +% why have you forsaken me? We know \tkn{:}{12} was wrong, but now we have +% something that might be correctly split at \tkn{:}{13}, but might as well be +% not +% \begin{macrocode} + \long\def\ekv@ifexp@why##1\ekv@stop + {% + \ekv@ifexp@split@l + ##1\ekv@nil\ekv@stop\ekv@ifexp@why@l + #2\ekv@stop\ekv@ifexp@why@a + }% +% \end{macrocode} +% \tkn{:}{11} is correct, rebuild stuff undoing the wrong \tkn{:}{13} split +% \begin{macrocode} + \long\def\ekv@ifexp@why@l##1\ekv@stop + {\ekv@ifexp@why@l@i{##1}\ekv@mark} + \long\def\ekv@ifexp@why@l@i + ##1##2\ekv@nil#2\ekv@stop\ekv@ifexp@why@a + {\ekv@ifexp@why@l@ii{##1}##2#1} +% \end{macrocode} +% and undoing the wrong \tkn{:}{12} split +% \begin{macrocode} + \long\def\ekv@ifexp@why@l@ii + ##1##2\ekv@nil#2\ekv@stop##3##4#2\ekv@stop##5% + {\ekv@ifexp@done{##1}##2: ##4}% +% \end{macrocode} +% \tkn{:}{13} is correct, we know there is a \tkn{:}{11} hiding somewhere, so +% we don't need to protect against brace strip using a two-step grab here, +% just undoing the wrong \tkn{:}{12} split is enough +% \begin{macrocode} + \long\def\ekv@ifexp@why@a + ##1\ekv@nil\ekv@stop\ekv@ifexp@why@l\ekv@stop + ##2\ekv@nil#2\ekv@stop##3##4#2\ekv@stop##5% + {\ekv@ifexp@done{##1}\ekv@mark##2: ##4}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% { +% \ekv@ifexp@done,\ekv@ifexp@done@, +% \ekv@ifexp@done@test, +% \ekv@ifexp@panic +% } +% once the colon split is done we need to check that there are no spaces in +% the now split of part, if there are \emph{panic} (we did all of this for +% nothing), else we're finally completely done with the splitting business. +% \begin{macrocode} + \ekv@exparg{\long\def\ekv@ifexp@done##1##2\ekv@nil}% + {% + \@firstofone + {\ekv@ifexp@done@test##1\ekv@nil\ekv@ifexp@panic} \ekv@nil{}% + ##2\ekv@stop\ekv@ifexp@done@{##1}{##2}\ekv@stop\@secondoftwo + } + \long\def\ekv@ifexp@done@test##1 ##2\ekv@nil##3##4##5##6\ekv@stop{##3} + \ekv@exparg{\long\def\ekv@ifexp@done@##1##2\ekv@stop\@secondoftwo##3}% + {\ekv@strip{##2}{##3{##1}}}% +% \end{macrocode} +% Panic in this case means give up on expansion parsing, instead the normal +% action that would've been done if the expansion mark isn't found is +% executed. +% \begin{macrocode} + \long\def\ekv@ifexp@panic##1\@secondoftwo##2##3{##3} + } +% \end{macrocode} +% \end{macro} +% We set up the strange category codes for colons and do the real definition +% after the temporary one. +% \begin{macrocode} +\begingroup + \catcode`\~=13 + \lccode`\~=`\: + \catcode`\z=11 + \lccode`\z=`\: +\lowercase{\endgroup +\ekv@ifexp{~ }{z }} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvset} +% Set up |\ekvset|, which should not be affected by active commas and equal +% signs. The equal signs are a bit harder to cope with and we'll do that later, +% but the active commas can be handled by just doing two comma-splitting loops +% one at actives one at others. That's why we define |\ekvset| here with a +% temporary meaning just to set up the things with two different category codes. +% |#1| will be a \texttt{,\textsubscript{13}} and |#2| will be a +% \texttt{=\textsubscript{13}}. +% \begin{macrocode} +\begingroup +\def\ekvset#1#2{% +\endgroup +\ekv@exparg{\long\def\ekvset##1##2}% + {% + \expandafter\expandafter\expandafter + \ekv@set\expandafter\csname\ekv@undefined@set{##1}\endcsname + \ekv@mark##2#1\ekv@stop#1{}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@set} +% |\ekv@set| will split the \kv\ list at active commas. Then it has to check +% whether there were unprotected other commas and resplit there. +% \begin{macrocode} +\long\def\ekv@set##1##2#1% + {% +% \end{macrocode} +% Test whether we're at the end, if so invoke |\ekv@endset|, +% \begin{macrocode} + \ekv@gobble@from@mark@to@stop##2\ekv@endset\ekv@stop +% \end{macrocode} +% else go on with other commas. +% \begin{macrocode} + \ekv@set@other##1##2,\ekv@stop,% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@endset} +% |\ekv@endset| is a hungry little macro. It will eat everything that remains +% of |\ekv@set| and unbrace the sneaked stuff. +% \begin{macrocode} +\long\def\ekv@endset\ekv@stop\ekv@set@other##1\ekv@mark\ekv@stop,\ekv@stop,##2% + {##2} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@eq@other@or@active,\ekv@eq@active} +% Splitting at equal signs will be done in a way that checks whether there is +% an equal sign and splits at the same time. This gets quite messy and the +% code might look complicated, but this is pretty fast (faster than first +% checking for an equal sign and splitting if one is found). The splitting +% code will be adapted for |\ekvset| and |\ekvparse| to get the most speed, +% but some of these macros don't require such adaptions. +% |\ekv@eq@other@or@active| and |\ekv@eq@active| will split the argument at +% the first equal sign. |\ekv@eq@active| will insert the macro which comes +% after the first following |\ekv@mark|, while |\ekv@eq@other@or@active| will +% also check for an active equals sign or none. +% Usage should look like this: +% \begin{syntaxexample*} +% |\ekv@eq@other@or@active| +% \indent|#1\ekv@nil\ekv@mark|\meta{code for \tkn{=}{12}} +% \indent\tkn{=}{12}|\ekv@mark|\meta{code for no equals sign} +% \indent|#1\ekv@mark|\meta{code for \tkn{=}{13} or mixed} +% \indent\tkn{=}{13}|\ekv@mark{}| +% |\ekv@eq@active| +% \indent|#1\ekv@nil\ekv@mark|\meta{code for \tkn{=}{13}} +% \indent\tkn{=}{13}|\ekv@mark|\meta{code for no \tkn{=}{13}} +% \end{syntaxexample*} +% To prevent accidental brace stripping |#1| should be handed in with a +% leading |\ekv@mark|, also that's what the |\ekv@nil| after the first |#1| is +% used for. In |\ekv@eq@other@or@active| four cases are distinguished: +% \begin{enumerate} +% \item +% Only equals signs of category 12 are found, then \meta{code for +% \tkn{=}{12}} will be inserted. +% \item +% No equals sign is found, then \meta{code for no equals sign} will be +% inserted. +% \item +% Only equals signs of category 13 are found, then \meta{code for +% \tkn{=}{13} or mixed} will be inserted, directly followed by +% \meta{code for no equals sign}. +% \item +% Equals signs of categories 12 and 13 are found, then \meta{code for +% \tkn{=}{13} or mixed} directly followed by \meta{code for \tkn{=}{12}} +% will be inserted. +% \end{enumerate} +% This allows for fast branching based on \TeX's argument grabbing rules and +% we don't have to split after the branching if the equal sign was there. +% \begin{macrocode} +\long\def\ekv@eq@other@or@active + ##1=##2\ekv@mark##3% + ##4#2##5\ekv@mark##6% + {##6##3##1\ekv@stop\ekv@mark##2} +\long\def\ekv@eq@other##1=##2\ekv@mark##3{##3##1\ekv@stop\ekv@mark##2} +\long\def\ekv@eq@active##1#2##2\ekv@mark##3{##3##1\ekv@stop\ekv@mark##2} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@set@other,\ekv@set@next@other} +% The macro |\ekv@set@other| is guaranteed to get only single \kv\ pairs. +% \begin{macrocode} +\long\def\ekv@set@other##1##2,% + {% +% \end{macrocode} +% First we test whether we're done. +% \begin{macrocode} + \ekv@gobble@from@mark@to@stop##2\ekv@endset@other\ekv@stop +% \end{macrocode} +% If not we split at the equal sign of category other. +% \begin{macrocode} + \ekv@eq@other@or@active + ##2\ekv@nil\ekv@mark\ekv@set@eq@other + =\ekv@mark\ekv@set@noeq + ##2\ekv@mark\ekv@set@eq@active + #2\ekv@mark{}% +% \end{macrocode} +% And put the set name after the splitting. +% \begin{macrocode} + ##1% + \ekv@mark + } +% \end{macrocode} +% The second macro is used as a shortcut to gobble two arguments and do the +% same as a following |\ekv@set@other|. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@set@next@other##1##2\ekv@set@other##3##4,}% + {\ekv@set@other{##3}{##4},} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@endset@other} +% Breaking the current inner loop for \tkn{,}{12} is done by gobbling all the +% remaining tokens and grabbing the next split at an active comma +% (incorporating the next |\ekv@set| call). +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@endset@other + \ekv@stop\ekv@eq@other@or@active + \ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@set@eq@other + =\ekv@mark\ekv@set@noeq + \ekv@mark\ekv@stop\ekv@mark\ekv@set@eq@active#2\ekv@mark##1% + ##2##3#1}% + {\ekv@set{##2}{##3}#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@set@eq@other} +% If this gets called we know that there is no \tkn{=}{13}, hence are done +% with the splitting business. This doesn't even need much clean up, we can +% just strip the \key-name and forward it to |\ekv@set@pair|. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@set@eq@other##1\ekv@stop}% + {\ekv@strip@key{##1}\ekv@set@pair} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@set@if@noeq} +% In the case that there was at least one \tkn{=}{13} we need a way to know +% whether there also was a \tkn{=}{12}. This here can be used to check this by +% only looking for |\ekv@set@noeq|. +% \begin{macrocode} +\def\ekv@set@if@noeq##1\ekv@set@noeq{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@set@eq@active,\ekv@set@eq@active@} +% The first token after this will either be |\ekv@set@noeq| or +% |\ekv@set@eq@other|. We test this and branch accordingly. +% \begin{macrocode} +\def\ekv@set@eq@active##1% + {\ekv@set@if@noeq##1\ekv@set@eq@active@\ekv@set@noeq\ekv@set@eq@mixed} +% \end{macrocode} +% Since we know that there was no \tkn{=}{12} when the next macro is called we +% can simply do the correct split without any checks and forward to +% |\ekv@set@pair|. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@set@eq@active@ + \ekv@set@noeq\ekv@set@eq@mixed + ##1#2##2\ekv@mark + \ekv@set@eq@other\ekv@stop\ekv@mark#2\ekv@mark##3}% + {\ekv@strip@key{##1}\ekv@set@pair\ekv@mark##2} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekv@set@eq@mixed,\ekv@set@eq@mixed@o,\ekv@set@mixed@a} +% In the mixed case we grab until the first split of \tkn{=}{12} already done, +% and resplit at \tkn{=}{13} using the conditonal splitter described earlier. +% \begin{macrocode} +\long\def\ekv@set@eq@mixed##1\ekv@stop + {% + \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@mixed@a + #2\ekv@mark\ekv@set@eq@mixed@o + } +% \end{macrocode} +% If there actually was no \tkn{=}{13} before the first \tkn{=}{12} everything +% was fine. We pick up our first split and remove the gibberish left by the +% conditional splitter. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@set@eq@mixed@o + ##1\ekv@nil\ekv@mark\ekv@set@eq@mixed@a\ekv@stop\ekv@mark + ##2\ekv@nil#2\ekv@mark##3}% + {\ekv@strip@key{##1}\ekv@set@pair##2\ekv@nil} +% \end{macrocode} +% |\ekv@set@eq@mixed@a| will trigger if before the first \tkn{=}{12} there was +% already a \tkn{=}{13}. So we use the new split and revert the erroneous +% split at \tkn{=}{12}. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@set@eq@mixed@a + ##1\ekv@stop + ##2\ekv@nil#2\ekv@mark\ekv@set@eq@mixed@o\ekv@mark + ##3#2\ekv@mark##4}% + {\ekv@strip@key{##1}\ekv@set@pair##2=##3} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@set@noeq,\ekv@set@was@blank} +% If there was no equals sign the current element might in fact be blank. +% Blank elements are ignored, hence we need to check for that, else we can +% call |\ekv@set@key|. +% \begin{macrocode} +\ekv@expandedarg + {% + \long\def\ekv@set@noeq + ##1\ekv@nil\ekv@mark\ekv@set@eq@other\ekv@stop\ekv@mark + }% + {% + \ekv@unexpanded + {% + \ekv@ifblank@##1\ekv@stop\ekv@ifblank@gobbletrue + \ekv@stop\ekv@set@was@blank + }% + \ekv@unexpanded\expandafter{\ekv@strip@key{##1}\ekv@set@key}% + } +% \end{macrocode} +% If in fact the element was blank we remove the |\ekv@set@key| call and +% instead do the next iteration of |\ekv@set@other|. +% \begin{macrocode} +\ekv@exparg{\long\expandafter\def\expandafter\ekv@set@was@blank + \ekv@strip@key{\ekv@mark##1}\ekv@set@key + ##2##3,}% + {\ekv@set@other{##2}{##3},} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak} +% Provide macros that can completely stop the parsing of |\ekvset|, who knows +% what it'll be useful for. +% \begin{macrocode} +\long\def\ekvbreak##1##2\ekv@stop#1##3{##1} +\long\def\ekvbreakPreSneak ##1##2\ekv@stop#1##3{##1##3} +\long\def\ekvbreakPostSneak##1##2\ekv@stop#1##3{##3##1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvsneak,\ekvsneakPre} +% One last thing we want to do for |\ekvset| is to provide macros that just +% smuggle stuff after |\ekvset|'s effects. +% \begin{macrocode} +\long\def\ekvsneak##1##2\ekv@stop#1##3{##2\ekv@stop#1{##3##1}} +\long\def\ekvsneakPre##1##2\ekv@stop#1##3{##2\ekv@stop#1{##1##3}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvparse} +% Additionally to the |\ekvset| macro we also want to provide an |\ekvparse| +% macro, that has the same scope as |\keyval_parse:NNn| from \pkg{expl3}. +% This is pretty analogue to the |\ekvset| implementation, we just put an +% |\unexpanded| here and there instead of other macros to stop the |\expanded| +% on our output. The |\unexpanded\expanded{{...}}| ensures that the material +% is in an alignment safe group at all time, and that it doesn't expand any +% further in an |\edef| or |\expanded| context. +% \begin{macrocode} +\long\def\ekvparse##1##2##3% + {% + \ekv@unexpanded\ekv@expanded + {{\ekv@parse{##1}{##2}\ekv@mark##3#1\ekv@stop#1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse} +% \begin{macrocode} +\long\def\ekv@parse##1##2##3#1% + {% + \ekv@gobble@from@mark@to@stop##3\ekv@endparse\ekv@stop + \ekv@parse@other{##1}{##2}##3,\ekv@stop,% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@endparse} +% \begin{macrocode} +\long\def\ekv@endparse\ekv@stop\ekv@parse@other##1\ekv@mark\ekv@stop,\ekv@stop,% + {} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@other} +% \begin{macrocode} +\long\def\ekv@parse@other##1##2##3,% + {% + \ekv@gobble@from@mark@to@stop##3\ekv@endparse@other\ekv@stop + \ekv@eq@other@or@active + ##3\ekv@nil\ekv@mark\ekv@parse@eq@other + =\ekv@mark\ekv@parse@noeq + ##3\ekv@mark\ekv@parse@eq@active#2\ekv@mark{}% + {##1}{##2}% + \ekv@mark + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@endparse@other} +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@endparse@other + \ekv@stop\ekv@eq@other@or@active + \ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@parse@eq@other + =\ekv@mark\ekv@parse@noeq + \ekv@mark\ekv@stop\ekv@mark\ekv@parse@eq@active#2\ekv@mark##1% + ##2##3##4#1}% + {\ekv@parse{##2}{##3}{##4}#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@eq@other} +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@parse@eq@other##1\ekv@stop}% + {\ekv@strip{##1}\ekv@parse@pair}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@if@noeq} +% \begin{macrocode} +\def\ekv@parse@if@noeq##1\ekv@parse@noeq{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@eq@active,\ekv@parse@eq@active@} +% \begin{macrocode} +\def\ekv@parse@eq@active##1% + {% + \ekv@parse@if@noeq##1\ekv@parse@eq@active@\ekv@parse@noeq + \ekv@parse@eq@mixed + } +\ekv@exparg{\long\def\ekv@parse@eq@active@ + \ekv@parse@noeq\ekv@parse@eq@mixed + ##1#2##2\ekv@mark + \ekv@parse@eq@other\ekv@stop\ekv@mark#2\ekv@mark##3}% + {\ekv@strip{##1}\ekv@parse@pair\ekv@mark##2} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekv@parse@eq@mixed,\ekv@parse@eq@mixed@o,\ekv@parse@eq@mixed@a} +% \begin{macrocode} +\long\def\ekv@parse@eq@mixed##1\ekv@stop + {% + \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@mixed@a + #2\ekv@mark\ekv@parse@eq@mixed@o + } +\ekv@exparg{\long\def\ekv@parse@eq@mixed@a + ##1\ekv@stop + ##2\ekv@nil#2\ekv@mark\ekv@parse@eq@mixed@o\ekv@mark + ##3#2\ekv@mark##4}% + {\ekv@strip{##1}\ekv@parse@pair##2=##3} +\ekv@exparg{\long\def\ekv@parse@eq@mixed@o + ##1\ekv@nil\ekv@mark\ekv@parse@eq@mixed@a\ekv@stop\ekv@mark + ##2\ekv@nil#2\ekv@mark##3}% + {\ekv@strip{##1}\ekv@parse@pair##2\ekv@nil} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@noeq,\ekv@parse@was@blank} +% \begin{macrocode} +\ekv@expandedarg + {% + \long\def\ekv@parse@noeq + ##1\ekv@nil\ekv@mark\ekv@parse@eq@other\ekv@stop\ekv@mark + }% + {% + \ekv@unexpanded + {% + \ekv@ifblank@##1\ekv@stop\ekv@ifblank@gobbletrue + \ekv@stop\ekv@parse@was@blank + }% + \ekv@unexpanded\expandafter{\ekv@strip{##1}\ekv@parse@key}% + } +\ekv@exparg{\long\expandafter\def\expandafter\ekv@parse@was@blank + \ekv@strip{\ekv@mark##1}\ekv@parse@key + ##2##3##4,}% + {\ekv@parse@other{##2}{##3}{##4},} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@pair,\ekv@parse@pair@} +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@parse@pair##1##2\ekv@nil}% + {\ekv@strip{##2}\ekv@parse@pair@{##1}} +\ekv@exparg{\long\def\ekv@parse@pair@##1##2##3##4}% + {% + \ekv@ifexp{##2}% + {\ekv@expansion@parse@pair{##1}{##4}}% + {\ekv@unexpanded{##4{##2}{##1}}}% + \ekv@parse@other{##3}{##4}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@parse@key} +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@parse@key##1##2}% + {% + \ekv@ifexp{##1}% + {\ekv@expansion@parse@key{##2}}% + {\ekv@unexpanded{##2{##1}}}% + \ekv@parse@other{##2}% + } +% \end{macrocode} +% \end{macro} +% +% Finally really setting things up with |\ekvset|'s temporary meaning: +% \begin{macrocode} +} +\catcode`\,=13 +\catcode`\==13 +\ekvset,= +% \end{macrocode} +% +% \begin{macro}{\ekvsetSneaked} +% This macro can be defined just by expanding |\ekvsneak| once after expanding +% |\ekvset|. To expand everything as much as possible early on we use a +% temporary definition. +% \begin{macrocode} +\edef\ekvsetSneaked + {% + \ekv@unexpanded{\ekvsneak{#2}}% + \ekv@unexpanded\expandafter{\ekvset{#1}{#3}}% + } +\ekv@expargtwice{\long\def\ekvsetSneaked#1#2#3}{\ekvsetSneaked} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvchangeset} +% Provide a macro that is able to switch out the current \set\ in |\ekvset|. +% This operation allows something similar to \pkg{pgfkeys}'s +% \texttt{\meta{key}/.cd} mechanism. However this operation can be more +% expensive than |/.cd| as we can't just redefine some token to reflect this, +% but have to switch out the set expandably, so this works similar to the +% |\ekvsneak| macros reading and reinserting things, but it only has to read and +% reinsert the remainder of the current key's replacement code. +% \begin{macrocode} +\ekv@exparg{\def\ekvchangeset#1}% + {% + \expandafter\expandafter\expandafter + \ekv@changeset\expandafter\csname\ekv@undefined@set{#1}\endcsname\ekv@empty + } +% \end{macrocode} +% \begin{macro}[internal]{\ekv@changeset} +% This macro does the real change-out of |\ekvchangeset|. |#2| will have a +% leading |\ekv@empty| so that braces aren't stripped accidentally, but that +% will not hurt and just expand to nothing in one step. +% \begin{macrocode} +\long\def\ekv@changeset#1#2\ekv@set@other#3{#2\ekv@set@other#1} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvmorekv} +% |\ekvmorekv| injects additional keys for consideration without needing a +% nested |\ekvset|. The issue we're facing here is that we can only inject +% after the next |\ekv@set@other| to not destroy the order of things, but in +% theory the argument to |\ekvmorekv| could contain an active comma. Now we +% have two options: +% \begin{enumerate} +% \item reread all keys until the end of the current |\ekv@set@other| loop +% to change the commas there to an active one and insert |\ekv@set| +% instead +% \item loop over the argument of |\ekvmorekv| to remove all top-level +% active commas and replace them with ones of category 12. +% \end{enumerate} +% The following implements the second approach. We use a temporary definition +% of |\ekvmorekv| to get us |#1| for active commas. For the comma replacement +% we set up a small loop. The |\ekv@empty| at the end of |\ekvmorekv|'s +% definition protects against accidental brace loss in user code. +% \begin{macrocode} +\def\ekvmorekv#1% + {% + \long\def\ekvmorekv##1% + {% + \expandafter\ekv@morekv\ekv@expanded + {{\ekv@morekv@loop\ekv@empty##1\ekv@stop\ekv@morekv@done#1}}% + \ekv@empty + }% +% \end{macrocode} +% \begin{macro}[internal]{\ekv@parse@more,\ekv@parse@more@} +% For the expansion control we also need a way to support reinsertion in +% |\ekvparse|. Since there no user code is executed this is an internal macro. +% It works in the same way but the argument order exchange is different. Also, +% since this is only used internally, we know it is safe to omit the trailing +% |\ekv@empty|. +% \begin{macrocode} + \long\def\ekv@parse@more##1\@gobble##2% + {% + \expandafter\ekv@parse@more@\ekv@expanded + {{\ekv@morekv@loop\ekv@empty##1\ekv@stop\ekv@morekv@done#1}}% + }% + \long\def\ekv@parse@more@##1\ekv@parse@other##2##3\ekv@mark + {\ekv@parse@other{##2}{##3}\ekv@mark##1,}% +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal]{\ekv@morekv} +% This is just a simple change of argument order, injecting the new comma +% separated list after the current |\ekv@set@other| call. +% \begin{macrocode} + \long\def\ekv@morekv##1##2\ekv@set@other##3\ekv@mark + {##2\ekv@set@other##3\ekv@mark##1,} +% \end{macrocode} +% \end{macro} +% \begin{macro}[internal] +% {\ekv@morekv@loop,\ekv@morekv@done,\ekv@morekv@wrap} +% And here is the heavy lifting of the comma replacement. Each list element +% until the next active comma is read and left as an argument for +% |\ekv@morekv@wrap|, that will remove the leading |\ekv@empty|. The trick for +% a fast end of the loop is that |\ekv@morekv@wrap| will read until the next +% |\ekv@stop|, of which an additional one is put before |\ekv@morekv@done| so +% that |wrap| will wrap the real end of the list and then |done| removes the +% excess code. +% \begin{macrocode} + \long\def\ekv@morekv@loop##1#1% + {\ekv@morekv@wrap##1\ekv@stop,\ekv@morekv@loop\ekv@empty}% + \long\def\ekv@morekv@wrap##1\ekv@stop{\ekv@unexpanded\expandafter{##1}}% + \def\ekv@morekv@done\ekv@stop,\ekv@morekv@loop\ekv@empty{}% + } +\begingroup\catcode`\,=13 \@firstofone{\endgroup\ekvmorekv,} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \begin{macro}[internal] +% { +% \ekv@def@expansion@rule,\ekv@def@expansion@rule@ea, +% \ekv@expansion@rule@,\ekv@expansion@csname +% } +% All expansion rules share the same basic structure, they do their expansion +% step and then call |\ekv@expansion@rule@| (this could be done faster by +% letting each rule build the macro for the next rule in |\csname|, but that +% would require a more complex argument grabbing structure of the rules, and +% we'd need to use |\expanded{\unexpanded{\csname ...\endcsname}\expandafter}| +% or similar to not do the expansion steps of the \val\ inside of |\csname|). +% As a result of this the \val\ needs to be grabbed twice per expansion step. +% \begin{macrocode} +\protected\def\ekv@def@expansion@rule#1#2#3#4% + {% + \long\expandafter\def\csname ekv@expansion@rule@#1\endcsname##1#2% + {#3\ekv@expansion@rule@#4}% + } +\protected\def\ekv@def@expansion@rule@ea#1#2% + {\ekv@def@expansion@rule{#1}{#2}\expandafter} +% \end{macrocode} +% Rules follow this naming scheme. Note that for an undefined rule an error is +% thrown and the result of the |\csname| is |\ekv@expansion@rule@|. +% \begin{macrocode} +\long\def\ekv@expansion@csname#1\ekv@stop + {% + ekv@expansion@rule@% + \ifcsname ekv@expansion@rule@#1\endcsname + #1% + \else + \ekv@err@undefined@expansion{#1}% + \fi + } +% \end{macrocode} +% This auxiliary macro simply builds the next rule, it is used to start an +% expansion chain, as well as for each defined or undefined rule. +% \begin{macrocode} +\long\def\ekv@expansion@rule@#1#2% + {\csname\expandafter\ekv@expansion@csname\string#2\ekv@stop\endcsname{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@expansion@rule@\ekv@mark} +% The expansion chain is ended by an internal rule that behaves very different +% from the others. It'll test whether the |r|-rule was used (in which case the +% third argument will not be empty, and also execute that reinsertion) or grab +% the code for the next step after the expansion. +% \begin{macrocode} +\ekv@exparg + {% + \long\expandafter + \def\csname ekv@expansion@rule@\string\ekv@mark\endcsname + #1\ekv@stop#2#3#4#5% + }% + {% + \romannumeral\ekv@exparg{\expandafter\ekv@zero\ekv@ifempty{#3}{#5{#2}{#1}}}% + {\ekv@ifempty{#2}{#4{#1}}{\ekv@err@val@no@reinsert{#2}}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@expansion@rule@o,\ekv@expansion@rule@e,\ekv@expansion@rule@c, +% \ekv@expansion@rule@f,\ekv@expansion@rule@V,\ekv@expansion@rule@v, +% \ekv@expansion@rule@s,\ekv@expansion@rule@b,\ekv@expansion@rule@\r, +% \ekv@expansion@rule@g,\ekv@expansion@rule@p,\ekv@expansion@rule@P, +% \ekv@expansion@rule@r,\ekv@expansion@rule@R +% } +% All the expansion rules are executed inside a |\csname| construct, so they +% have to issue an |\endcsname| after the intended expansion took place. I +% guess the code is pretty straight forward here. +% \begin{macrocode} +\ekv@def@expansion@rule@ea{o}{}{\expandafter{#1}} +\ekv@def@expansion@rule@ea{e}{}{\ekv@expanded{{#1}}} +\ekv@def@expansion@rule@ea{c}{}{\csname#1\endcsname} +\ekv@def@expansion@rule@ea{f}{}{\expandafter{\romannumeral`\^^@#1}} +\ekv@exparg{\ekv@def@expansion@rule{s}{}}{\ekv@strip{\ekv@mark#1}}{} +\ekv@def@expansion@rule{b}{}{}{{{#1}}} +\ekv@def@expansion@rule{\string\r}{#2\ekv@stop#3#4}{}{{#1}#2\ekv@stop{#3}{#4r}} +\ekv@def@expansion@rule@ea{g}{}{\expandafter{\@gobble#1}} +\ekv@def@expansion@rule{p}{#2}{}{{#2#1}} +\ekv@def@expansion@rule{P}{#2}{}{{#1#2}} +% \end{macrocode} +% The |V| and |v| rules are a bit more complicated. They use some auxiliaries +% but otherwise should be more or less obvious as well. The |v| one uses some +% expansion to get a more meaningful error message in case the resulting +% control sequence would be undefined (without actually using |\csname|, to +% not accidentally define anything as |\relax|). +% \begin{macrocode} +\long\def\ekv@expansion@rule@V#1% + {% + \ifx\relax#1\ekv@err@erroneous@variable{#1}\ekv@expansion@@clean@V\fi + \ekv@expansion@@V{#1}% + } +\ekv@expandedarg{\long\def\ekv@expansion@rule@v#1}% + {% + \ekv@unexpanded{\ekv@ifdefined{#1}% + {\expandafter\ekv@expansion@@V\csname#1\endcsname}}% + {% + \ekv@unexpanded{\expandafter\ekv@err@erroneous@variable\ekv@expanded}% + {{\expandafter\@gobble\string\\#1}}% + \ekv@unexpanded{\ekv@expansion@rule@}{}% + }% + } +\ekv@exparg{\long\def\ekv@expansion@rule@r#1#2\ekv@stop#3#4}% + {\ekv@expansion@rule@v{#1}#2\ekv@stop{#3}{#4r}} +\ekv@exparg{\long\def\ekv@expansion@rule@R#1#2\ekv@stop#3#4}% + {\ekv@expansion@rule@V{#1}#2\ekv@stop{#3}{#4r}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@expansion@@V,\ekv@expansion@@clean@V} +% The actual |V| evaluation is pretty much the same as in \pkg{expl3}, but we +% need to put braces around the expansion result, hence branch slightly +% differently. In general, a macro will temporarily have the meaning of +% |\relax| if hit by |\noexpand| and hence the \cs[no-index]{ifx} test will +% result in false (as the meaning isn't the same now), so we can tell +% whether it would expand directly. Else we assume this is a register type and +% use |\the|. +% \begin{macrocode} +\long\def\ekv@expansion@@V#1% + {% + \expandafter\ifx\noexpand#1#1% + \ekv@fi@firstoftwo + \fi + \@secondoftwo + {\expandafter\ekv@expansion@rule@\expandafter{\the#1}}% + {\expandafter\ekv@expansion@rule@\expandafter{#1}}% + } +\long\def\ekv@expansion@@clean@V\fi\ekv@expansion@@V#1% + {\fi\ekv@expansion@rule@{}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekv@expansion@rule@\key,\ekv@expansion@rule@\ekv@key@after} +% The |\key|-rule works by calling a nested expansion chain with swapped \key\ +% and \val. Instead of |\ekv@mark| marking the end of the expansion chain +% we use a different final step called |\ekv@key@after|, that simply checks +% whether an |r|-rule was executed (\emph{error}) and ends the current +% expansion step in the outer chain. +% \begin{macrocode} +\ekv@def@expansion@rule{\string\key}{#2#3\ekv@stop#4}% + {}{{#4}#2\ekv@key@after\ekv@stop{#1}{}{#3}} +\ekv@exparg{\ekv@def@expansion@rule{\string\ekv@key@after}{\ekv@stop#2#3#4}}% + {\ekv@ifempty{#3}{}\ekv@err@key@no@reinsert} + {{#2}#4\ekv@stop{#1}} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}[internal] +% { +% \ekv@set@pair,\ekv@set@pair@, +% \ekv@set@pair@relax,\ekv@set@pair@undefined, +% \ekv@set@pair@u,\ekv@set@pair@u@relax,\ekv@set@pair@no@u +% } +% |\ekv@set@pair| gets invoked with the space and brace stripped key-name as +% its first, the value as the second (delimited by |\ekv@nil|), and the set +% name as the third argument. It provides tests for the key-macros and +% everything to be able to throw meaningful error messages if it isn't +% defined. We have two routes here, one if |\lastnamedcs| is defined and one +% if it isn't. The big difference is that if it is we can omit a |\csname| and +% instead just expand |\lastnamedcs| once to get the control sequence. +% If the macro is defined the value will be space and brace stripped and the +% key-macro called. Else branch into the error handling provided by +% |\ekv@set@pair@undefined|. +% \begin{macrocode} +\ekv@if@lastnamedcs + {% + \long\def\ekv@set@pair#1\ekv@mark#2#3\ekv@nil#4% + {% + \ifcsname#4{#1}\endcsname\expandafter\ekv@set@pair@\lastnamedcs\fi + \ekv@set@pair@undefined{#3}{#1}{#2}% + \ekv@set@other#4% + } + \ekv@exparg{\long\def\ekv@set@pair@undefined#1#2#3\ekv@set@other#4}% + {% + \romannumeral + \ekv@exparg + {\expandafter\ekv@zero\ekv@ifexp{#3}}% + {\ekv@strip{#1}\ekv@expansion@set@pair}% + {% + \ifcsname#4{}u\endcsname\expandafter\ekv@set@pair@u\lastnamedcs\fi + \ekv@set@pair@no@u{#1}{#2}{#3}% + }% + \ekv@set@other#4% + } + } + {% + \long\def\ekv@set@pair#1\ekv@mark#2#3\ekv@nil#4% + {% + \ifcsname#4{#1}\endcsname + \expandafter\ekv@set@pair@\csname#4{#1}\endcsname + \fi + \ekv@set@pair@undefined{#3}{#1}{#2}% + \ekv@set@other#4% + } + \ekv@exparg{\long\def\ekv@set@pair@undefined#1#2#3\ekv@set@other#4}% + {% + \romannumeral + \ekv@exparg + {\expandafter\ekv@zero\ekv@ifexp{#3}}% + {\ekv@strip{#1}\ekv@expansion@set@pair}% + {% + \ifcsname#4{}u\endcsname + \expandafter\ekv@set@pair@u\csname#4{}u\endcsname + \fi + \ekv@set@pair@no@u{#1}{#2}{#3}% + }% + \ekv@set@other#4% + } + } +\ekv@expandedarg{\long\def\ekv@set@pair@#1\fi\ekv@set@pair@undefined#2}% + {% + \ekv@unexpanded{\fi\ifx#1\relax\ekv@set@pair@relax\fi}% + \ekv@unexpanded\expandafter{\ekv@strip{#2}#1\ekv@set@next@other}% + } +\ekv@expandafter{\long\def\ekv@set@pair@relax\fi}% + \ekv@strip{#1}#2\ekv@set@next@other + {\fi\ekv@set@pair@undefined{#1}} +\ekv@expandedarg{\long\def\ekv@set@pair@u#1\fi\ekv@set@pair@no@u#2}% + {% + \ekv@unexpanded + {% + \fi + \ifx\relax#1% + \ekv@set@pair@u@relax + \fi + }% + \ekv@unexpanded\expandafter{\ekv@strip{#2}#1}% + } +\ekv@expandafter{\long\def\ekv@set@pair@u@relax\fi}\ekv@strip{\ekv@mark#1}#2% + {\fi\ekv@set@pair@no@u.} +\long\def\ekv@set@pair@no@u#1#2#3\ekv@set@other#4% + {% + \ekv@ifdefined{#4{#2}N}% + \ekv@err@noarg + \ekv@err@unknown + #4{#2}{#3}% + \ekv@set@other#4% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@set@key,\ekv@set@key@, +% \ekv@set@key@relax,\ekv@set@key@undefined, +% \ekv@set@key@u,\ekv@set@key@u@relax,\ekv@set@key@no@u +% } +% Analogous to |\ekv@set@pair|, |\ekv@set@key| builds the \Nkey-macro and +% provides an error-branch. It'll test whether the key-macro is defined and if +% so call it, else there might be some \expnotation, or errors are thrown. +% \begin{macrocode} +\ekv@if@lastnamedcs + {% + \long\def\ekv@set@key#1\ekv@mark#2#3% + {% + \ifcsname#3{#1}N\endcsname\expandafter\ekv@set@key@\lastnamedcs\fi + \ekv@set@key@undefined{#1}{#2}% + \ekv@set@other#3% + } + \ekv@exparg{\long\def\ekv@set@key@undefined#1#2\ekv@set@other#3}% + {% + \ekv@ifexp{#2}% + \ekv@expansion@set@key + {% + \ifcsname#3{}uN\endcsname\expandafter\ekv@set@key@u\lastnamedcs\fi + \ekv@set@key@no@u{#1}{#2}% + }% + \ekv@set@other#3% + } + } + {% + \long\def\ekv@set@key#1\ekv@mark#2#3% + {% + \ifcsname#3{#1}N\endcsname + \expandafter\ekv@set@key@\csname#3{#1}N\endcsname + \fi + \ekv@set@key@undefined{#1}{#2}% + \ekv@set@other#3% + } + \ekv@exparg{\long\def\ekv@set@key@undefined#1#2\ekv@set@other#3}% + {% + \ekv@ifexp{#2}% + \ekv@expansion@set@key + {% + \ifcsname#3{}uN\endcsname + \expandafter\ekv@set@key@u\csname#3{}uN\endcsname + \fi + \ekv@set@key@no@u{#1}{#2}% + }% + \ekv@set@other#3% + } + } +\long\def\ekv@set@key@#1\fi\ekv@set@key@undefined + {\fi\ifx#1\relax\ekv@set@key@relax\fi#1\ekv@set@next@other} +\long\def\ekv@set@key@relax\fi#1\ekv@set@next@other{\fi\ekv@set@key@undefined} +\long\def\ekv@set@key@u#1\fi\ekv@set@key@no@u + {\fi\ifx\relax#1\ekv@set@key@u@relax\fi#1} +\def\ekv@set@key@u@relax\fi#1{\fi\ekv@set@key@no@u} +\long\def\ekv@set@key@no@u#1#2\ekv@set@other#3% + {% + \ekv@ifdefined{#3{#1}}% + \ekv@err@reqval + \ekv@err@unknown + #3{#1}{#2}% + \ekv@set@other#3% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@expansion@set@key,\ekv@expansion@set@key@, +% \ekv@expansion@set@pair,\ekv@expansion@set@pair@ +% } +% These macros just pick up the pieces of the \expansion\ prefix as separate +% arguments. The \val\ for the \expansion-rules is left empty. Once expansion +% is done we loop back to |\ekv@set@key|, that'll pick up the processed \key\ +% and set it if it's now defined. The same trick is used for a \kv\ pair. +% \begin{macrocode} +\long\def\ekv@expansion@set@key#1#2#3% + {% + \ekv@expansion@rule@{#2}#1\ekv@mark\ekv@stop{}{}\ekvmorekv + \ekv@expansion@set@key@ + } +\long\def\ekv@expansion@set@key@#1#2\ekv@set@other#3% + {\expandafter\ekv@set@key\detokenize{#2}\ekv@mark{#2}#3} +\long\def\ekv@expansion@set@pair#1#2#3#4% + {% + \ekv@expansion@rule@{#1}#2\ekv@mark\ekv@stop{#3}{}\ekvmorekv + \ekv@expansion@set@pair@ + } +\long\def\ekv@expansion@set@pair@#1#2\ekv@set@other#3% + {\expandafter\ekv@set@pair\detokenize{#1}\ekv@mark{#1}\ekv@mark{#2}\ekv@nil#3} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@expansion@parse@key,\ekv@expansion@parse@key@, +% \ekv@expansion@parse@pair,\ekv@expansion@parse@pair@ +% } +% In |\ekvparse| we don't need to resort to looping back into |\ekv@parse@key| +% or the like, we can simply leave the result via |\ekv@unexpanded|. +% \begin{macrocode} +\long\def\ekv@expansion@parse@key#1#2#3#4% + {% + \ekv@expansion@rule@{#3}#2\ekv@mark\ekv@stop{}{}\ekv@parse@more + \ekv@expansion@parse@key@\@gobble{#1}% + } +\long\def\ekv@expansion@parse@key@#1#2\@gobble#3{\ekv@unexpanded{#3{#2}}} +\long\def\ekv@expansion@parse@pair#1#2#3#4#5% + {% + \ekv@expansion@rule@{#1}#3\ekv@mark\ekv@stop{#4}{}\ekv@parse@more + \ekv@expansion@parse@pair@\@gobble{#2}% + } +\long\def\ekv@expansion@parse@pair@#1#2\@gobble#3{\ekv@unexpanded{#3{#1}{#2}}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvsetdef} +% Provide a macro to define a shorthand to use |\ekvset| on a specified \set. +% To gain the maximum speed |\ekvset| is expanded twice by +% |\ekv@exparg| so that during runtime the macro storing the set name +% is already built and one |\expandafter| doesn't have to be used. +% \begin{macrocode} +\ekv@expargtwice{\protected\def\ekvsetdef#1#2}% + {% + \romannumeral + \ekv@exparg{\ekv@zero\ekv@exparg{\def#1##1}}% + {\ekvset{#2}{##1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvsetSneakeddef,\ekvsetdefSneaked} +% And do the same for |\ekvsetSneaked| in the two possible ways, with a fixed +% sneaked argument and with a flexible one. +% \begin{macrocode} +\ekv@expargtwice{\protected\def\ekvsetSneakeddef#1#2}% + {% + \romannumeral + \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1##2}}% + {\ekvsetSneaked{#2}{##1}{##2}}% + } +\ekv@expargtwice{\protected\def\ekvsetdefSneaked#1#2#3}% + {% + \romannumeral + \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1}}% + {\ekvsetSneaked{#2}{#3}{##1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@alignsafe,\ekv@endalignsafe} +% These macros protect the usage of ampersands inside of alignment contexts. +% \begin{macrocode} +\begingroup +\catcode`\^^@=2 +\@firstofone{\endgroup + \def\ekv@alignsafe{\romannumeral\iffalse{\fi`^^@ } +} +\def\ekv@endalignsafe{\ifnum`{=\ekv@zero}\fi} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoptarg,\ekvoptargTF} +% Provide macros to expandably collect an optional argument in brackets. The +% macros here are pretty simple in nature compared to \pkg{xparse}'s +% possibilities (they don't care for nested bracket levels). +% +% We start with a temporary definition to pre-expand |\ekv@alignsafe| (will be +% |#1|) and |\ekv@endalignsafe| (will be |#2|). As |\ekv@alignsafe| starts with +% a |\romannumeral| we use that to also control the number of steps needed +% instead of adding another |\romannumeral|. For this we have to remove the +% space token from the end of |\ekv@alignsafe|'s definition. +% \begin{macrocode} +\begingroup +\def\ekvoptarg#1#2{% +\endgroup +% \end{macrocode} +% The real definition starts an expansion context and afterwards grabs the +% arguments. |#1| will be the next step, |#2| the default value, and |#3| might +% be an opening bracket, or the mandatory argument. We check for the opening +% bracket, if it is found grab the optional argument, else leave |#1{#2}| in the +% input stream after ending the expansion context. +% \begin{macrocode} +\def\ekvoptarg{#1\ekv@optarg@a} +\long\def\ekv@optarg@a##1##2##3% + {% + \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optarg@b\ekv@mark[\ekv@mark + #2% + \@firstofone{ ##1}{##2}{##3}% + }% +% \end{macrocode} +% The other variant of this will do roughly the same. Here, |#1| will be the +% next step if an optional argument is found, |#2| the next step else, and |#3| +% might be the opening bracket or mandatory argument. +% \begin{macrocode} +\def\ekvoptargTF{#1\ekv@optargTF@a} +\long\def\ekv@optargTF@a##1##2##3% + {% + \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optargTF@b{##1}\ekv@mark[\ekv@mark + #2% + \@firstofone{ ##2}{##3}% + } +% \end{macrocode} +% The two macros to grab the optional argument have to remove the remainder of +% the test and the wrong next step as well as grabbing the argument. +% \begin{macrocode} +\long\def\ekv@optarg@b\ekv@mark[\ekv@mark\ifnum`##1\fi\@firstofone##2##3##4##5]% + {#2##2{##5}} +\long\def\ekv@optargTF@b + ##1\ekv@mark[\ekv@mark\ifnum`##2\fi\@firstofone##3##4##5]% + {#2 ##1{##5}} +} +% \end{macrocode} +% Do the definitions and add the test macro. We use |\ekv@strip| to remove the +% trailing space from the definition of |\ekv@alignsafe|. +% \begin{macrocode} +\ekv@exparg + {% + \expandafter\ekv@strip\expandafter + {\expandafter\ekv@mark\ekv@alignsafe}% + \ekvoptarg + }% + \ekv@endalignsafe +\long\def\ekv@optarg@if#1\ekv@mark[\ekv@mark{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@ifstar} +% Internal helper to get an expandable |ifstar| implementation. This is pretty +% straight forward. +% \begin{macrocode} +\long\def\ekv@ifstar#1#2#3% + {% + \if*\string#3% + \ekv@fi@firstoftwo + \fi + \@secondoftwo + {#1}% + {#2{#3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcompile} +% The idea of key-compilation is to preparse some list into a form that sets +% that list very fast (but is frozen), in our case this means we do everything +% except expanding the key's code. There are two levels of expansion possible, +% either expand until the key-macro or expand the key-macro once as well (the +% latter might break if the key-macros take additional arguments). The +% following supports both by using a starred form. +% \begin{macrocode} +\protected\long\def\ekvcompile + {% + \ekv@alignsafe + \ekv@ifstar{\ekv@compile{}}{\ekv@compile{\expandafter\expandafter}}% + } +% \end{macrocode} +% \begin{macro}[internal] +% {\ekv@compile,\ekv@compile@,\ekv@compile@key,\ekv@compile@pair} +% |\ekvparse| starts with an |\unexpanded| that we don't need. Hence we use a +% temporary definition to remove that. Else this is pretty straight forward. +% There is a minor indirection (the parsing step of |\ekv@compile|) which is +% necessary to grab any argument specification. +% \begin{macrocode} +\def\ekv@compile#1#2#3#{\ekv@compile@{#1}{#2#3}} +\def\ekv@compile@#1#2#3{#2#3} +\ekv@exparg{\protected\long\def\ekv@compile@#1#2#3#4}% + {% + \expandafter\ekv@expanded\expandafter + {% + \romannumeral\ekv@expargtwice{\ekv@zero\def\ekv@unexpanded{#2}}% + {% + \expandafter\ekv@compile@ + \ekvparse + {\ekv@compile@key{#1}{#3}}% + {\ekv@compile@pair{#1}{#3}}% + {#4}% + }% + }% + \ekv@endalignsafe + } +% \end{macrocode} +% The compilation step just checks whether the keys are defined and leaves +% their code (maybe expanded once). +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@compile@key#1#2#3}% + {% + \ekvifdefinedNoVal{#2}{#3}% + {\ekv@unexpanded#1\expandafter{\csname\ekv@name{#2}{#3}N\endcsname}}% + {% + \ekv@ifdefined{\ekv@name{#2}{}uN} + {% + \ekv@unexpanded#1\expandafter + {% + \csname\ekv@name{#2}{}uN\expandafter\endcsname + \expandafter{\detokenize{#3}}{#3}% + }% + }% + {% + \ekvifdefined{#2}{#3}% + {\ekv@err{missing value for `#3' in set `#2'}}% + {\ekv@err{unknown key `#3' in set `#2'}}% + }% + }% + } +\ekv@exparg{\long\def\ekv@compile@pair#1#2#3#4}% + {% + \ekvifdefined{#2}{#3}% + {\ekv@unexpanded#1\expandafter{\csname\ekv@name{#2}{#3}\endcsname{#4}}}% + {% + \ekv@ifdefined{\ekv@name{#2}{}u}% + {% + \ekv@unexpanded#1\expandafter + {% + \csname\ekv@name{#2}{}u\ekv@expanded{\endcsname + {\ekv@unexpanded{#4}}{\detokenize{#3}}}{#3}% + }% + }% + {% + \ekvifdefinedNoVal{#2}{#3}% + {\ekv@err{unwanted value for `#3' in set `#2'}}% + {\ekv@err{unknown key `#3' in set `#2'}}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekverr} +% \begin{macro}[internal]{\ekv@err@collect,\ekv@err@cleanup} +% Since |\ekvset| is fully expandable as long as the code of the keys is (which +% is unlikely) we want to somehow throw expandable errors, in our case via +% a runaway argument (to my knowledge the first version of this method was +% implemented by Jean-François Burnol, many thanks to him). The first step is to +% ensure that the second argument (which might contain user input) doesn't +% contain tokens we use as delimiters (in this case |\par|), this will be done +% by the front facing macro |\ekverr|. But first we set some other things up. +% +% We use a temporary definition for |\ekverr| to get multiple consecutive +% spaces. Then we set up the macro that will collect the error and the macro +% that will throw the error. The latter will have an unreasonable long name. +% This way we can convey more information. Though the information in the macro +% name is static and has to be somewhat general to fit every occurence. The +% important bit is that the long named macro has a delimited argument and is +% short which will throw the error at the |\par| at the end of +% |\ekv@err@collect|. This macro has the drawback that it will only print nicely +% if the |\newlinechar| is |^^J|. +% \begin{macrocode} +\def\ekv@err@cleanup\par{} +\def\ekv@err@collect#1% + {% + \def\ekv@err@collect##1\par##2% + {% + \expandafter + \ekv@err@cleanup + #1! ##2 Error: ##1\par + }% + \def#1##1\thanks@jfbu{}% + } +\def\ekverr{ } +\expandafter\ekv@err@collect\csname <an-expandable-macro>^^J% + completed due to above exception. \ekverr If the error^^J% + summary is \ekverr not comprehensible \ekverr see the package^^J% + documentation.^^J% + I will try to recover now. \ekverr If you're in inter-^^J% + active mode hit <return> \ekverr at the ? prompt and I^^J% + continue hoping recovery\endcsname +% \end{macrocode} +% \begin{macrocode} +\long\def\ekverr#1#2{\expandafter\ekv@err@collect\detokenize{#2}\par{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@err,\ekv@errm} +% We define shorthands to throw errors in \expkv. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@err#1}{\ekverr{expkv}{#1}} +\protected\long\def\ekv@errm#1{\errmessage{expkv Error: #1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekv@err@common,\ekv@err@common@, +% \ekv@err@unknown,\ekv@err@noarg,\ekv@err@reqval, +% \ekv@err@undefined@expansion +% } +% Now we can use |\ekv@err| to set up some error messages so that we can later +% use those instead of the full strings. +% \begin{macrocode} +\long\def\ekv@err@common #1#2{\expandafter\ekv@err@common@\string#2{#1}} +\ekv@exparg{\long\def\ekv@err@common@#1`#2' #3.#4#5}% + {\ekv@err{#4 `#5' in set `#2'}} +\ekv@exparg{\long\def\ekv@err@unknown#1#2}{\ekv@err@common{unknown key}{#1}} +\ekv@exparg{\long\def\ekv@err@noarg#1#2} + {\ekv@err@common{unwanted value for}{#1}} +\ekv@exparg{\long\def\ekv@err@reqval#1#2}% + {\ekv@err@common{missing value for}{#1}} +\ekv@exparg{\long\def\ekv@err@redirect@kv@notfound#1#2#3\ekv@stop}% + {\ekv@err{no key `#2' in sets #3}} +\ekv@exparg{\def\ekv@err@redirect@k@notfound#1#2\ekv@stop}% + {\ekv@err{no NoVal key `#1' in sets #2}} +\ekv@exparg{\def\ekv@err@undefined@expansion#1\fi}% + {\expandafter\fi\ekv@err{Undefined expansion rule `#1'}}% +\ekv@exparg{\def\ekv@err@erroneous@variable#1}% + {\ekv@err{Erroneous variable `#1' used}} +\ekv@exparg{\def\ekv@err@key@no@reinsert}% + {\ekv@err{Reinsertion in \key expansion forbidden}} +\ekv@exparg{\def\ekv@err@val@no@reinsert#1}% + {\ekv@err{Reinsertion with key `#1' forbidden}} +% \end{macrocode} +% \end{macro} +% +% Now everything that's left is to reset the category code of |@|. +% \begin{macrocode} +\catcode`\@=\ekv@tmp +% \end{macrocode} +% +% \gobbledocstriptag +%</tex> +%^^A=<< diff --git a/macros/generic/expkv-bundle/impatient.tex b/macros/generic/expkv-bundle/impatient.tex new file mode 100644 index 0000000000..b3d62b07fa --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/introduction.tex b/macros/generic/expkv-bundle/introduction.tex new file mode 100644 index 0000000000..de52d9e584 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/pkg-cs.tex b/macros/generic/expkv-bundle/pkg-cs.tex new file mode 100644 index 0000000000..850b9af620 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/pkg-def.tex b/macros/generic/expkv-bundle/pkg-def.tex new file mode 100644 index 0000000000..fade98e8b8 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/pkg-main.tex b/macros/generic/expkv-bundle/pkg-main.tex new file mode 100644 index 0000000000..0109eb70da --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/pkg-opt.tex b/macros/generic/expkv-bundle/pkg-opt.tex new file mode 100644 index 0000000000..452fbd1c28 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/pkg-pop.tex b/macros/generic/expkv-bundle/pkg-pop.tex new file mode 100644 index 0000000000..e34d0c5d1f --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-enverb.tex b/macros/generic/expkv-bundle/preamble-enverb.tex new file mode 100644 index 0000000000..22f39cd31c --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-examples.tex b/macros/generic/expkv-bundle/preamble-examples.tex new file mode 100644 index 0000000000..5a146d9eb6 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-l3doctweaks.tex b/macros/generic/expkv-bundle/preamble-l3doctweaks.tex new file mode 100644 index 0000000000..47d2c744a5 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-logos.tex b/macros/generic/expkv-bundle/preamble-logos.tex new file mode 100644 index 0000000000..f5c963134c --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-lst.tex b/macros/generic/expkv-bundle/preamble-lst.tex new file mode 100644 index 0000000000..a94920bfff --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-noidx.tex b/macros/generic/expkv-bundle/preamble-noidx.tex new file mode 100644 index 0000000000..410db83483 --- /dev/null +++ b/macros/generic/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/macros/generic/expkv-bundle/preamble-prefixes.tex b/macros/generic/expkv-bundle/preamble-prefixes.tex new file mode 100644 index 0000000000..a961fc3f24 --- /dev/null +++ b/macros/generic/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 + } diff --git a/macros/generic/expkv-cs/README.md b/macros/generic/expkv-cs/README.md deleted file mode 100644 index 481d94ba34..0000000000 --- a/macros/generic/expkv-cs/README.md +++ /dev/null @@ -1,32 +0,0 @@ -------------------------------------------------------------------------------- -# expkv-cs -- define expandable key=val macros using expkv - -Version 2022-02-13 v1.2 - -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt - -Hosted at https://github.com/Skillmon/tex_expkv-cs - -------------------------------------------------------------------------------- - -Copyright (C) 2020-2022 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. - -------------------------------------------------------------------------------- - -This provides means to define expandable key=val taking macros utilizing -[`expkv`](https://github.com/Skillmon/tex_expkv). It is generic code and only -requires `expkv` for its parsing. There is a LaTeX package `expkv-cs.sty` -included to play nice on LaTeX's package loading system, but that package is not -needed and does not provide more functionality than the generic code in -`expkv-cs.tex`. diff --git a/macros/generic/expkv-cs/expkv-cs.dtx b/macros/generic/expkv-cs/expkv-cs.dtx deleted file mode 100644 index 561dec7af9..0000000000 --- a/macros/generic/expkv-cs/expkv-cs.dtx +++ /dev/null @@ -1,3431 +0,0 @@ -% \iffalse meta-comment -% -% File: expkv-cs.dtx Copyright (C) 2020-2022 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 -% -% ------------------------------------------------------------------------------ -% -%<*driver>^^A>>= -\def\nameofplainTeX{plain} -\ifx\fmtname\nameofplainTeX\else - \expandafter\begingroup -\fi -\input l3docstrip.tex -\askforoverwritefalse -\preamble - --------------------------------------------------------------- -expkv-cs -- define expandable key=val macros using expkv -E-mail: jspratte@yahoo.de -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt --------------------------------------------------------------- - -Copyright (C) 2020-2022 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. - -This work consists of the file expkv-cs.dtx -and the derived files expkv-cs.pdf - expkv-cs.sty - expkv-cs.tex - t-expkv-cs.tex - -\endpreamble -% stop docstrip adding \endinput -\postamble -\endpostamble -\generate{\file{expkv-cs.sty}{\from{expkv-cs.dtx}{pkg}}} -\generate{\file{expkv-cs.tex}{\from{expkv-cs.dtx}{tex}}} -\generate{\file{t-expkv-cs.tex}{\from{expkv-cs.dtx}{ctx}}} -\ifx\fmtname\nameofplainTeX - \expandafter\endbatchfile -\else - \expandafter\endgroup -\fi -% -\IfFileExists{expkv-cs.tex}{\input{expkv-cs.tex}}{} -\ProvidesFile{expkv-cs.dtx} - [\csname ekvcDate\endcsname\ define expandable key=val macros using expkv] -\PassOptionsToPackage{full}{textcomp} -\documentclass{l3doc} -\RequirePackage[oldstylenums,nott]{kpfonts} -\input{glyphtounicode} -\pdfgentounicode=1 -\RequirePackage{tcolorbox} -\newtcolorbox{exresult}[2][] - {colback=expkvgrey!10!white,colframe=expkvgrey,fontupper=\small,width={#2},#1} -\newtcbox\exres[1][] - { - colback=expkvgrey!10!white - ,colframe=expkvgrey - ,size=small - ,nobeforeafter - ,tcbox raise base - ,fontupper=\small - ,#1 - } -\RequirePackage{listings} -\RequirePackage{booktabs} -\RequirePackage{array} -\RequirePackage{collcell} -\RequirePackage{siunitx} -\RequirePackage{xcolor} -\RequirePackage{caption} -\RequirePackage{microtype} -\RequirePackage{accsupp} -\lstdefinelanguage{expkvc}[primitive]{TeX} - { - ,moretexcs=[4]^^A e-TeX - { - expanded, - numexpr, - protected, - } - ,moretexcs=[5]^^A plain/LaTeX - { - approx, - begin, - empty, - item, - LaTeX, - makeatletter,makeatother,multicolumn, - newcommand,newdimen, - RequirePackage, - rule, - TeX, - textit,texttt, - usepackage, - usemodule, - } - ,moretexcs=[6]^^A used packages - { - ^^A xfp - fpeval, - ^^A keyval - setkeys, - ^^A yax - defactiveparameter,storevalue,setparameterlist - } - ,moretexcs=[2]^^A expkv-cs macros - { - ekvcSplit,ekvcSplitAndForward,ekvcSplitAndUse, - ekvcHash,ekvcHashAndForward,ekvcHashAndUse, - ekvcValue,ekvcValueFast,ekvcValueSplit,ekvcValueSplitFast, - ekvcSecondaryKeys, - ekvcChange, - ekvcFlagNew, - ekvcFlagHeight, - ekvcFlagRaise, - ekvcFlagSetTrue,ekvcFlagSetFalse, - ekvcFlagIf,ekvcFlagIfRaised, - ekvcFlagReset, - ekvcFlagGetHeight,ekvcFlagGetHeights, - ekvcPass - } - ,moretexcs=[3]^^A expkv-pkg macros - { - ^^A expkv - ekvdef,ekvdefNoVal,^^A - ekvlet,ekvletNoVal,ekvletkv,ekvletkvNoVal,^^A - ekvdefunknown,ekvdefunknownNoVal,^^A - ekvredirectunknown,ekvredirectunknownNoVal,^^A - ekvset,ekvsetSneaked,^^A - ekvsetdef,ekvsetSneakeddef,ekvsetdefSneaked,^^A - ekvparse,^^A - ekvVersion,ekvDate,^^A - ekvifdefined,ekvifdefinedNoVal,ekvifdefinedset,^^A - ekvbreak,ekvbreakPreSneak,ekvbreakPostSneak,^^A - ekvsneak,ekvsneakPre,^^A - ekvchangeset,^^A - ekvoptarg,ekvoptargTF,^^A - ekverr, - ^^A expkv-def - ekvdefinekeys - } - } -\colorlet{codeparam}{cyan!65!black} -\def\mylstwd{.55em} -\lstset - { - ,language=expkvc - ,flexiblecolumns=false - ,basewidth=\mylstwd - ,gobble=2 - ,basicstyle=\fontfamily{jkp}\itshape - ,texcsstyle=*[1]{\bfseries\color{expkvgrey}} ^^A primitives - ,texcsstyle=*[2]{\bfseries\color{expkvred}} ^^A expkv - ,texcsstyle=*[3]{\color{expkvred}} ^^A expkv-pkg - ,texcsstyle=*[4]{\bfseries\color{expkvgrey}} ^^A e-TeX - ,texcsstyle=*[5]{\bfseries\color{expkvgrey}} ^^A plain/LaTeX macros - ,texcsstyle=*[6]{} ^^A macros of other packages - ,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} - ^^A,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}}} -\RequirePackage{randtext} -\let\metaORIG\meta -\protected\def\meta #1{\texttt{\metaORIG{#1}}} -\renewcommand*\thefootnote{\fnsymbol{footnote}} -\definecolor{expkvred}{HTML}{9F393D} -\colorlet{expkvgrey}{black!75} -\makeatletter -\newcommand*\example{\par\smallskip\noindent\textit{Example:} \ignorespaces} -\newcommand*\expkv - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv}^^A - \href{https://github.com/Skillmon/tex_expkv} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv}^^A - } -\newcommand*\expkvpkg[1] - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv-#1}^^A - \href{https://github.com/Skillmon/tex_expkv-#1} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - {\color{expkvgrey}^^A - \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{#1}^^A - }^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv-#1}^^A - } -\newcommand*\expkvd{\expkvpkg{def}} -\newcommand*\expkvc{\expkvpkg{cs}} -\newcommand\kv{\meta{key}=\meta{value}} -\newcommand\key{\meta{key}} -\newcommand\val{\meta{value}} -\newcommand\set{\meta{set}} -\newcommand\enflong{\textcolor{black}{long}} -\newcommand\alllong{\textcolor{gray}{long}} -\newcommand\notlong{\textcolor{red!80!black}{long}} -\newcommand\prefixes[1] - {^^A - \hfill - \ifcase\numexpr#1\relax\or - \enflong\or - \alllong\or - \notlong\fi - } -\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} -\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} -\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long,\z@} -\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space,\advance} -\DoNotIndex{\@,\unexpanded,\string,\expanded,\dimexpr,\global,\glueexpr,\hbox} -\DoNotIndex{\numexpr,\RequirePackage,\setbox,\the,\unless,\xdef,\gdef,\newcount} -\DoNotIndex{\number,\detokenize,\meaning,\lccode,\lowercase} -\DoNotIndex{\^,\\,\@firstofone,\@firstoftwo,\@gobble,\@secondoftwo} -\DoNotIndex{\ifcsname} -\DoNotIndex{\ifx} -\DoNotIndex{\ifodd} -\DoNotIndex{\ifnum} -\DoNotIndex{\ifdefined} -\DoNotIndex{\iffalse} -\DoNotIndex{\iftrue} -\DoNotIndex{\else} -\DoNotIndex{\fi} -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} -\makeatother -\begin{document} - \title - {^^A - \texorpdfstring - {^^A - \huge\expkvc - \\[\medskipamount] - \Large define expandable \kv\ macros using \expkv - }{expkv-cs - define expandable key=val macros using expkv}^^A - } - \date{\ekvcDate\space v\ekvcVersion} - \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}} - \DocInput{expkv-cs.dtx} -\end{document} -%</driver>^^A=<< -% \fi -% -% \maketitle -% \renewcommand*\thefootnote{\arabic{footnote}} -% -% \begin{abstract} -% \noindent\parfillskip=0pt -% \expkvc\ provides two small interfaces to define expandable \kv\ macros using -% \expkv. It therefore lowers the entrance boundary to expandable \kv\ macros. -% The stylised name is \expkvc\ but the files use \file{expkv-cs}, this is due -% to CTAN-rules which don't allow \string| in package names since that is the -% pipe symbol in *nix shells. -% \end{abstract} -% -% \tableofcontents -% -% \begin{documentation}^^A>>= -% -% \section{Documentation} -% -% The \expkv\ package enables the new possibility of creating \kv\ macros which -% are fully expandable. The creation of such macros is however cumbersome for -% the average user. \expkvc\ tries to step in here. It provides interfaces to -% define \kv\ macros without worrying too much about the implementation. In case -% you're wondering now, the |cs| in \expkvc\ stands for control sequence, -% because |def| was already taken by \expkvd\ and ``control sequence'' is the -% term D.\,E.\,Knuth used in his \TeX book for named commands hence macros -% (though he also used the term ``macro''). So \expkvc\ defines control -% sequences for and with \expkv. -% -% There are two different approaches supported by this package. The first is -% splitting the keys up into individual arguments, the second is providing all -% the keys as a single argument to the underlying macro and getting an -% individual \val\ by using a hash. Well, actually there is no real hash, just -% some markers which are parsed, but this shouldn't be apparent to the user, the -% behaviour matches that of a hash-table. -% -% In addition to these two methods of defining a macro with primary keys a way -% to define secondary keys, which can reference the primary ones, is provided. -% These secondary keys don't correspond to an argument or an entry in the hash -% table directly but might come in handy for the average use case. Each macro -% has its own set of primary and secondary keys. -% -% A word of advice you should consider: If your macro doesn't have to be -% expandable (and often it doesn't) consider not using \expkvc. The interface -% has some overhead (though it still is fast -- check \autoref{sec:speed}) and -% the approach has its limits in versatility. If you don't need to be -% expandable, you should consider either defining your keys manually using -% \expkv\ or using \expkvd\ for convenience. Or you resort to another \kv\ -% interface. Nevertheless setting up macros with \expkvc, especially with -% |\ekvcSplit|, is very convenient in my opinion, so if you just want to define -% a single macro with just a few keys this might be the way to go. -% -% \expkvc\ is usable as generic code, as a \LaTeX\ package, and as a \ConTeXt\ -% module. It'll automatically load \expkv\ in the same mode as well. To use it, -% just use one of -% \begin{lstlisting} -% \input expkv-cs % plainTeX -% \usepackage{expkv-cs} % LaTeX -% \usemodule[expkv-cs] % ConTeXt -% \end{lstlisting} -% -% -% \subsection{Define Macros and Primary Keys}\label{sec:define} -% -% All macros defined with \expkvc\ have to be previously undefined or have the -% |\meaning| of |\relax|. This is necessary as there is no way to automatically -% undefine keys once they are set up (neither \expkv\ nor \expkvc\ keep track of -% defined keys) -- so to make sure there are no conflicts only new definitions -% are allowed (that's not the case for individual keys, only for frontend -% macros). -% -% -% \subsubsection{Primary Keys}\label{sec:primaries} -% -% In the following descriptions there will be one argument named \meta{primary -% keys}. This argument should be a \kv\ list where each \key\ will be one -% primary key and \val\ the associated initial value. By default all keys are -% defined short, but you can define long keys by prefixing \key\ with |long| -% (\emph{e.g.}, \texttt{long name=Jonathan P. Spratte}). You only need |long| if -% the key should be able to take an explicit |\par| token. Note however that -% |long| keys are a microscopic grain faster (due to some internals of \expkvc). -% Only if at least one of the keys was |long| the \meta{cs} in the following -% defining macros will be |\long|. For obvious reasons there is no possibility -% to define a macro or key as |\protected|. -% -% To allow keys not defined |long| which key names should start with |long|, you -% can also use the prefix |short| (|short| and |long| are mutually exclusive, -% which ever comes first defines the behaviour, the latter is considered part of -% the key name). Note that this is the only reason for |short|'s existance, -% essentially it does nothing. -% -% The consequence culminates in the following: -% \begin{lstlisting} -% \ekvcSplit\foo -% { -% long short = abc\par -% ,short long = def -% } -% {#1#2} -% \end{lstlisting} -% will define a macro |\foo| that knows two primary keys, the first one is -% called |short|, and accepts explicit |\par| tokens inside its values, the -% second one is called |long| and will not accept |\par| tokens (leading to a -% low level \TeX\ error). A description of |\ekvcSplit| follows shortly. -% -% There is one exception to these syntax rules of \meta{primary keys}: One can -% include a key named |...| without a value. If this is found the \meta{cs} will -% be defined |\long|. Any unknown keys found at use time will end up in a list -% at this spot. See some examples in \autoref{sec: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 -% \texttt{EKVC\string|} as that should be the private name space. -% -% -% \subsubsection{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:primaries}). 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 |\ekvcSplit| you can define macros using at most -% nine primary keys. -% \end{function} -% \example The following defines a macro |\foo| that takes the keys |a| and |b| -% and outputs their values in a textual form:\\ -% \begin{minipage}{.75\linewidth} -% \begin{lstlisting} -% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} -% \foo{} -% \foo{b=e} -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} -% \foo{} -% \foo{b=e} -% \end{exresult} -% -% \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{lstlisting} -% \ekvcSplitAndForward\foo\foo@aux{keyA = A, keyB = B} -% \ekvcSplitAndForward\foo{\foo@aux{more args}}{keyA = A, keyB = B} -% \end{lstlisting} -% \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 |\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}\{\key=\val, \ldots\}\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} -% -% 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 |\ekvcValue|, not the -% parsing, and for a key access using |\ekvcValueFast| it was still about $1.2$ -% times slower). So if your macro uses less than ten primary keys, you should -% consider using 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 -% \texttt{\cs[no-index]{ekvcValue}\{\key\}\{\#1\}}. -% \end{function} -% \example This defines an equivalent macro to the |\foo| defined with -% |\ekvcSplit| earlier:\\ -% \begin{minipage}{.75\linewidth} -% \begin{lstlisting} -% \ekvcHash\foo{a=a,b=b}{a is \ekvcValue{a}{#1}.\par -% b is \ekvcValue{b}{#1}.\par} -% \foo{} -% \foo{b=e} -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \ekvcHash\foo{a=a,b=b}{a is \ekvcValue{a}{#1}.\par -% b is \ekvcValue{b}{#1}.\par} -% \foo{} -% \foo{b=e} -% \end{exresult} -% -% \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 -% \texttt{\cs[no-index]{ekvcValue}\{\key\}\{\#1\}} (or whichever argument the -% hashed \kv\ list will be). -% \end{function} -% \example This defines a macro |\foo| processing two keys, and passing the -% result to |\foobar|: -% \begin{lstlisting} -% \ekvcHashAndForward\foo\foobar{a=a,b=b} -% \newcommand*\foobar[1]{a is \ekvcValue{a}{#1}.\par -% b is \ekvcValue{b}{#1}.\par} -% \end{lstlisting} -% -% \begin{function}{\ekvcHashAndUse} -% \begin{syntax} -% \cs{ekvcHashAndUse}\meta{cs}\marg{primary keys} -% \end{syntax} -% This will roughly do the same as |\ekvcHashAndForward|, but instead of -% specifying what will be used after hashing the keys at install time, -% \meta{cs} will use what follows the \kv\ list. So its syntax will be -% \end{function} -% \begin{quote} -% \ttfamily -% \meta{cs}\{\key=\val, \ldots\}\marg{after} -% \end{quote} -% -% \begin{function}{\ekvcValue} -% \begin{syntax} -% \cs{ekvcValue}\{\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 |\ekvcHash|, |\ekvcHashAndForward|, or |\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}\{\key\}\marg{key list} -% \end{syntax} -% This behaves just like |\ekvcValue|, but \emph{without any} safety 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 \key\ -% name). Use it if you know what you're doing. This macro needs exactly three -% steps of expansion in the no-errors case. -% \end{function} -% -% \begin{function}{\ekvcValueSplit} -% \begin{syntax} -% \cs{ekvcValueSplit}\{\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. Hence the macro \cs{ekvcValueSplit} will extract one -% specific \key's value 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 -% |\ekvcValue| and runs the same tests. -% \end{function} -% \example 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{minipage}{.75\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \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{exresult} -% -% \begin{function}{\ekvcValueSplitFast} -% \begin{syntax} -% \cs{ekvcValueSplitFast}\{\key\}\marg{key list}\marg{next} -% \end{syntax} -% This behaves just like |\ekvcValueSplit|, but it won't run the same tests, -% hence it is faster but more error prone, just like the relation between -% |\ekvcValue| and |\ekvcValueFast|. -% \end{function} -% -% -% \subsection{Secondary Keys}\label{sec:secondaries} -% -% To remove some of the limitations with the approach that each primary key -% matches an 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 a split or a hash variant. If a secondary key references another key -% it doesn't matter whether that other key is primary or secondary unless -% otherwise specified. -% -% Secondary keys can have a prefix (like |long|) which are called |p|-type -% prefix and must have a type (like |meta|) which are called |t|-type prefix. -% Some types might require some |p|-prefixes, while others might forbid those. -% -% Please keep in mind that key names shouldn't start with \texttt{EKVC\string|}. -% -% \begin{function}{\ekvcSecondaryKeys} -% \begin{syntax} -% \cs{ekvcSecondaryKeys}\meta{cs}\{\kv, \ldots\} -% \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\small -% \meta{prefix} \meta{name} -% \end{quote} -% Where \meta{prefix} is a space separated list of optional |p|-type prefixes -% followed by one |t|-type prefix. The syntax of \val\ is dependent on the -% used |t|-prefix. -% \end{function} -% -% -% \subsubsection{\texttt{p}-type Prefixes} -% -% There is only one |p|-prefix available, which is |long|. -% -% \begin{function}{long} -% The following key will be defined |\long|. -% \end{function} -% -% -% \subsubsection{\texttt{t}-type Prefixes} -% -% If you're familiar with \expkvd\ you'll notice that the |t|-type prefixes -% provided here are much fewer. The expansion only concept doesn't allow for -% great variety in the auto-defined keys. -% -% The syntax examples of the |t|-prefixes will show which |p|-prefix will be -% automatically used by printing those black (\texttt{\enflong}), which -% will be available in grey (\texttt{\alllong}), and which will be disallowed in -% red (\texttt{\notlong}). This will be put flush right next to the syntax line. -% -% \begin{function}{meta} -% \begin{syntax} -% meta \key\ = \{\kv, \ldots\} \prefixes2 -% \end{syntax} -% With a |meta| key you can set other keys. Whenever \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. The keys in the \kv\ list can -% be primary and secondary ones. -% \end{function} -% -% \begin{function}{nmeta} -% \begin{syntax} -% nmeta \key\ = \{\kv, \ldots\} \prefixes3 -% \end{syntax} -% An |nmeta| key is like a |meta| key, but it doesn't take a value, so the -% \kv\ list is static. -% \end{function} -% -% \begin{function}{alias} -% \begin{syntax} -% alias \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 value taking and the -% |NoVal| version (that's \expkv\ slang for a key not accepting a value) will -% be copied if they are defined when |alias| is used. Of course, \meta{key_2} -% has to be defined, be it as a primary or secondary one. -% \end{function} -% -% \begin{function}{default} -% \begin{syntax} -% default \key\ = \marg{default} \prefixes3 -% \end{syntax} -% If \key\ is a defined value taking key, you can define a |NoVal| version -% with this that 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 in |\ekvcSplit| and friends (see |\ekvcChange| in -% \autoref{sec:change} for this). \key\ can be a primary or secondary key. -% \end{function} -% -% \begin{function}{enum} -% \begin{syntax} -% enum \key\ = \marg{key_2}\{\val, \ldots\} \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 treated as a string, 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} -% \example First a small example that might give you an idea of what the -% description above could mean:\\ -% \begin{minipage}[c]{.75\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \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{exresult} -% \example 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 -% |\ifcase|:\\ -% \begin{minipage}[c]{.75\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \ekvcSplit\foo{k-internal=-1} -% {^^A -% \ifcase#1 -% is\or -% This\or -% easy^^A -% \else -% .^^A -% \fi -% } -% \ekvcSecondaryKeys\foo -% {enum k = {k-internal}{a,b,c}} -% \foo{k=b} \foo{k=a} \foo{k=c}\foo{} -% \end{exresult} -% -% \begin{function}{aggregate} -% \begin{syntax} -% aggregate \key\ = \marg{primary}\marg{definition} \prefixes3 -% \end{syntax} -% While all other key types replace the current value of the associated -% \meta{primary} key, with |aggregate| you can create keys that append or -% prepend (or whatever you like) the new value to the current one. The -% value must be exactly two \TeX\ arguments, where \meta{primary} should -% be the name of a \meta{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 (so -% this doesn't allow general processing of current and new values). The -% resulting \key\ will inherit being either |short| or |long| from the -% \meta{primary} key. -% \end{function} -% \example 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{minipage}[c]{.75\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \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{exresult} -% -% But also more strange stuff could end there, like macros or using the same -% value multiple times: -% \begin{lstlisting} -% \ekvcSecondaryKeys\foo -% {aggregate k = {k-internal}{\old{#1}\new{#2\old{#1}}}} -% \end{lstlisting} -% -% -% \begin{function}{flag-bool} -% \begin{syntax} -% flag-bool \key\ = \meta{cs} \prefixes3 -% \end{syntax} -% This is a secondary key that doesn't directly involve any of the primary or -% 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. -% Please also read \autoref{sec:flags}. -% \end{function} -% -% \begin{function}{flag-true,flag-false} -% \begin{syntax} -% flag-true \key\ = \meta{cs} \prefixes3 -% \end{syntax} -% This is a secondary key that doesn't directly involve any of the primary or -% secondary keys. -% This defines \key\ to take no value and set the flag called \meta{cs} to -% |true| or |false|, respectively. -% If \meta{cs} isn't defined yet it will be initialised as a flag. -% Please also read \autoref{sec:flags}. -% \end{function} -% -% \begin{function}{flag-raise} -% \begin{syntax} -% flag-raise \key\ = \meta{cs} \prefixes3 -% \end{syntax} -% This is a secondary key that doesn't directly involve any of the primary or -% secondary keys. -% This defines \key\ to take no value and raise the flag called \meta{cs}. -% If \meta{cs} isn't defined yet it will be initialised as a flag. -% Please also read \autoref{sec:flags}. -% \end{function} -% -% -% \subsection{Changing the Initial Values}\label{sec:change} -% -% \begin{function}{\ekvcChange} -% \begin{syntax} -% \cs{ekvcChange}\meta{cs}\{\kv,\ldots\} -% \end{syntax} -% This processes the \kv\ list for the macro \meta{cs} to set new defaults for -% it (meaning the values used if you don't provide anything at use time, not -% those specified with the |default| secondary key). \meta{cs} should be -% defined with \expkvc\ (so with any of the methods in \autoref{sec:define}). -% 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 defaults were). If there -% was an unknown key forwarding added to \meta{cs} (see \autoref{sec:unknown}) -% any unknown key will be stored inside the list of unknown keys as well. -% |\ekvcChange| is not expandable! -% \end{function} -% -% Consider the following example:\\ -% \begin{minipage}[c]{.75\linewidth} -% \begin{lstlisting} -% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} -% \begingroup -% \ekvcChange\foo{b=B} -% \foo{} % new defaults -% \ekvcSecondaryKeys\foo{meta c={a={#1},b={#1}}} -% \ekvcChange\foo{c=c} -% \foo{} % newer defaults -% \endgroup -% \foo{} % initial defaults -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \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{exresult} -% -% As a result with this the typical setup macro could be implemented: -% \begin{lstlisting} -% \ekvcHashAndUse\foo{key=a,key=b} -% \newcommand*\foosetup{\ekvcChange\foo} -% \end{lstlisting} -% Of course the usage is limited to a single macro |\foo|, 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. -% -% -% \subsection{Handling Unknown Keys}\label{sec: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 name will be the result of one |\detokenize|, the value -% will be forwarded as \verb*| {|\meta{value}\verb*|} |, so with spaces around -% one set of braces (this way, most other \kv\ implementations should parse the -% correct input). -% -% The exact behaviour differs slightly between the two variants (as all 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. -% -% \example The following will forward any unknown key to |\includegraphics| to -% control the appearance while processing its own keys:\\ -% \begin{minipage}{.6\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.4\linewidth} -% \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{exresult} -% -% Inside the hash variants the unknown keys list will be put inside the hash -% named |...| (we have to use some name, so why not this). 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. -% -% \example The following is more or less equivalent to the example above, 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{minipage}{.76\linewidth} -% \begin{lstlisting} -% \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 -% \expanded{\noexpand\includegraphics -% [{\ekvcValue{...}{#1}}]} -% {#2}\par -% } -% \foo[width=\linewidth, b=c] -% {example-image-duck-portrait} -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.24\linewidth} -% \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 -% \expanded{\noexpand\includegraphics -% [{\ekvcValue{...}{#1}}]} -% {#2}\par -% } -% \foo[width=\linewidth, b=c] -% {example-image-duck-portrait} -% \end{exresult} -% -% -% \subsection{Flags}\label{sec: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 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 |t|-types involving flags -% bear in mind that they can affect other macros using the same flags at the -% current group level! -% -% \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 |\ifnum| for example and it will terminate the number scanning -% on its own). -% \end{function} -% -% All other macros dealing with flags take as a parameter a macro defined as a -% \meta{flag} with |\ekvcFlagNew|. -% -% \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} -% \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. 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 -% \texttt -% {^^A -% \cs[no-index]{ekvcFlagGetHeights}\hskip0pt^^A -% \{\cs[no-index]{myflagA}\cs[no-index]{myflagB}\}\hskip0pt^^A -% \{\cs[no-index]{stuff}\}^^A -% } -% will expand to -% \texttt{\cs[no-index]{stuff}\{\marg{height-A}\marg{height-B}\}}. -% \end{function} -% -% -% \subsection{Further Examples}\label{sec:examples} -% -% How could a documentation be a good documentation without enough basic -% examples? Say we want to define a small macro expanding to some character -% description (who knows why this has to be expandable?). A character -% description will not have too many items to it, so we use |\ekvcSplit| (the -% comments with the parameter numbers are of course not necessary and just ease -% reading the example). -% \begin{lstlisting} -% \ekvcSplit\character -% { -% name=John Doe, % #1 -% age=any, % #2 -% nationality=the Universe, % #3 -% hobby=to exist, % #4 -% type=Mister, % #5 -% pronoun=He, % #6 -% possessive=his, % #7 -% } -% {#1 is a #5 from #3. #6 is of #2 age and #7 hobby is #4.\par} -% \end{lstlisting} -% Also we want to give some short cuts so that it's easier to describe several -% persons. -% \begin{lstlisting} -% \ekvcSecondaryKeys\character -% { -% alias pro = pronoun, -% alias pos = possessive, -% nmeta me = -% { -% name=Jonathan, -% age=a young, -% nationality=Germany, -% hobby=\TeX\ coding, -% }, -% meta lady = -% {type=Lady, pronoun=She, possessive=her, name=Jane Doe, #1}, -% nmeta paulo = -% { -% name=Paulo, -% type=duck, -% age=a young, -% nationality=Brazil, -% hobby=to quack, -% } -% } -% \end{lstlisting} -% Now we can describe people using -% \begin{lstlisting} -% \character{} -% \character{me} -% \character{paulo} -% \character -% {lady={name=Evelyn,nationality=Ireland,age=the best,hobby=reading}} -% \character -% { -% name=Our sun, type=star, nationality=our solar system, pro=It, -% age=an old, pos=its, hobby=shining -% } -% \end{lstlisting} -% As one might see, the |lady| key could actually have been an |nmeta| key as -% well, as all that is done with the argument is using it as a \kv\ list. -% -% The result of only the first two usages would be: -% \begin{exresult}{\linewidth} -% \ekvcSplit\character -% { -% name=John Doe, -% age=any, -% nationality=the Universe, -% hobby=to exist, -% type=Mister, -% pronoun=He, -% possessive=his, -% } -% {#1 is a #5 from #3. #6 is of #2 age and #7 hobby is #4.\par} -% \ekvcSecondaryKeys\character -% { -% alias pro = pronoun, -% alias pos = possessive, -% nmeta me = -% { -% name=Jonathan, -% age=a young, -% nationality=Germany, -% hobby=\TeX\ coding, -% }, -% meta lady = -% {type=Lady, pronoun=She, possessive=her, name=Jane Doe, #1}, -% nmeta paulo = -% { -% name=Paulo, -% type=duck, -% age=a young, -% nationality=Brazil, -% hobby=to quack, -% } -% } -% \character{} -% \character{me} -% \end{exresult} -% -% Using \pkg{xparse} 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 (which should -% perhaps use |\ekvcSplitAndForward| instead of |\ekvcHashAndForward| since it -% only uses four keys and one other argument -- and isn't expandable since it -% uses a \env{tabular} environment, so it would've been better to use a more -% feature rich \kv\ interface most likely, \emph{e.g.}, the one provided by -% \expkvd): -% \begin{lstlisting} -% \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 -% \end{lstlisting} -% And then we would be able to do some nonsense -% \begin{lstlisting} -% \nonsense{} -% \nonsense[keyA=hihi]{haha} -% \nonsense[keyA=hihi, keyB=A]{hehe} -% \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} -% \end{lstlisting} -% resulting in -% \begin{exresult}{\linewidth} -% \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{} -% \nonsense[keyA=hihi]{haha} -% \nonsense[keyA=hihi, keyB=A]{hehe} -% \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} -% \end{exresult} -% -% -% \subsection{Freedom for Keys!} -% -% If this was the \TeX book this subsection would have 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 types listed in \autoref{sec:secondaries} 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 -% |\string|\meta{cs} is used. This can be exploited to define additional keys -% with arbitrary code. Consider the following \emph{bad} example: -% \begin{lstlisting} -% \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2\par} -% \protected\ekvdef{\string\foo}{c}{\def\fooC{#1}} -% \end{lstlisting} -% This would define a key named |c| that will store its value 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 -% create a key that pre-parses the input and after that passes the parsed value -% on. This parsing would have to be completely expandable though. 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 \meta{value} on to \meta{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} -% -% With this we could for example split the value of a key at a hyphen and pass -% the parts to different keys:\\ -% \begin{minipage}[c]{.75\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \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{exresult} -% -% Additionally, there is a more general version of the |aggregate| secondary key -% type (described in \autoref{sec:secondaries}), namely the |process| key type: -% -% \begin{function}{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 |\ekvcPass| to forward the -% values afterwards. Unlike |aggregate| you can specify whether the \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} -% \example We could define a key that only accepts values greater than the -% current value with this:\\ -% \begin{minipage}[c]{.75\linewidth} -% \begin{lstlisting} -% \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{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \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{exresult} -% -% -% \subsection{Speed Considerations}\label{sec:speed} -% -% As already mentioned in the introduction there are some speed considerations -% implied if you choose to define macros via \expkvc. However the overhead isn't -% the factor which should hinder you to use \expkvc\ if you found a reasonable -% use case. The key-parsing is still faster than with most other \kv\ packages -% (see the ``Comparisons'' subsection in the \expkv\ documentation). -% -% The speed considerations in this subsection use the first example of -% \autoref{sec:examples} as the benchmark. So we have seven keys and a short -% sentence which should be typeset. For comparisons I use the following -% equivalent \expkvd\ definitions. Each result is the average between changing -% no keys from their initial values and altering four. Furthermore I'll compare -% three variants of \expkvc\ with the \expkvd\ definitions, namely the split -% example from above, a hash variant using |\ekvcValue| and a hash variant using -% |\ekvcValueFast|. -% \begin{lstlisting} -% \usepackage{expkv-def} -% \ekvdefinekeys{keys} -% {% -% ,store name = \KEYSname -% ,initial name = John Doe -% ,store age = \KEYSage -% ,initial age = any -% ,store nationality = \KEYSnationality -% ,initial nationality = the Universe -% ,store hobby = \KEYShobby -% ,initial hobby = to exist -% ,store type = \KEYStype -% ,initial type = Mister -% ,store pronoun = \KEYSpronoun -% ,initial pronoun = He -% ,store possessive = \KEYSpossessive -% ,initial possessive = his -% } -% \newcommand*\KEYS[1] -% {% -% \begingroup -% \ekvset{keys}{#1}% -% \KEYSname\ is a \KEYStype\ from \KEYSnationality. -% \KEYSpronoun\ is of \KEYSage\ age and -% \KEYSpossessive\ hobby is \KEYShobby.% -% \endgroup -% } -% \end{lstlisting} -% -% The first comparison removes the typesetting part from all the definitions, so -% that only the key parsing is compared. In this comparison the |\ekvcValue| -% and |\ekvcValueFast| variants will not differ, as they are exactly the same -% until the key usage. We find that the split approach is $1.4$ times slower -% than the \expkvd\ setup and the hash variants end up in the middle at $1.17$ -% times slower. -% -% Next we put the typesetting part back in. Every call of the macros will -% typeset the sentences into a box register in horizontal mode. With the -% typesetting part (which includes the accessing of values) the fastest remains -% the \expkvd\ definitions, but split is close at $1.16$ times slower, followed -% by the hash variant with fast accesses at $1.36$ times slower, and the safe -% hash access variant ranks in the slowest $1.8$ times slower than \expkvd. -% -% Just in case you're wondering now, a simple macro taking seven arguments is -% $30$ to $40$ times faster than any of those in the argument grabbing and \kv\ -% parsing part and only $1.5$ to $2.8$ times faster if the typesetting part is -% factored in. So the real choke isn't the parsing. -% -% So to summarise this, if you have a use case for expandable \kv\ -% parsing macros you should go on and define them using \expkvc. If you just -% want to define a simple macro with a few keys |\ekvcSplit| might be the -% easiest interface there is. If you have a reasonable use case for \kv\ parsing -% macros but defining them expandable isn't necessary for your use you should -% take advantage of the greater flexibility of non-expandable \kv\ setups (but -% if you're after maximum speed there aren't that many \kv\ parsers beating -% \expkvc). And if you are after maximum performance maybe ditching the \kv\ -% interface altogether is a good idea, but depending on the number of arguments -% your interface might get convoluted. -% -% -% \subsection{Useless Macros} -% -% Perhaps these macros aren't completely useless, but I figured from a user's -% point of view I wouldn't know what I should do with these. -% -% \begin{function}{\ekvcDate,\ekvcVersion} -% These two macros store the version and the date of the package/generic code. -% \end{function} -% -% -% \subsection{Bugs} -% -% Of course I don't think there are any bugs (who would knowingly distribute -% buggy software as long as he isn't a multi-million dollar corporation?). But -% if you find some please let me know. For this one might find my email address -% on the first page or file an issue on Github: -% \url{https://github.com/Skillmon/tex_expkv-cs} -% -% -% \subsection{License} -% -% Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\ -% Jonathan P. Spratte -% -% \medskip\noindent -% 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: -% -% \url{http://www.latex-project.org/lppl.txt} -% -% \noindent -% This work is ``maintained'' (as per LPPL maintenance status) by -% -% \mbox{Jonathan P. Spratte}. -% -% \end{documentation}^^A=<< -% -% \begin{implementation}^^A>>= -% -% \clearpage -% -% \section{Implementation}^^A>>= -%^^A the LaTeX package >>= -% \subsection{The \LaTeX\ Package} -% Just like for \expkv\ we provide a small \LaTeX\ package that sets up things -% such that we behave nicely on \LaTeX\ packages and files system. It'll -% |\input| the generic code which implements the functionality. -% \gobbledocstriptag -%<*pkg> -% \begin{macrocode} -\RequirePackage{expkv} -\def\ekvc@tmp - {% - \ProvidesFile{expkv-cs.tex}% - [% - \ekvcDate\space v\ekvcVersion\space - define expandable key=val macros using expkv% - ]% - } -\input{expkv-cs.tex} -\ProvidesPackage{expkv-cs}% - [% - \ekvcDate\space v\ekvcVersion\space - define expandable key=val macros using expkv% - ] -% \end{macrocode} -% \gobbledocstriptag -%</pkg> -%^^A=<< -%^^A the ConTeXt module >>= -% \subsection{The \ConTeXt\ module} -% \gobbledocstriptag -%<*ctx> -% \begin{macrocode} -\writestatus{loading}{ConTeXt User Module / expkv-cs} -\usemodule[expkv] -\unprotect -\input expkv-cs.tex -\writestatus{loading} - {ConTeXt User Module / expkv-cs / Version \ekvcVersion\space loaded} -\protect\endinput -% \end{macrocode} -% \gobbledocstriptag -%</ctx> -%^^A=<< -%^^A main file >>= -% \subsection{The Generic Code} -% The rest of this implementation will be the generic code. -% \gobbledocstriptag -%<*tex> -% -% Load \expkv\ if the package didn't already do so -- since \expkv\ has -% safeguards against being loaded twice this does no harm and the overhead -% isn't that big. Also we reuse some of the internals of \expkv\ to save us from -% retyping them. -% \begin{macrocode} -\input expkv -% \end{macrocode} -% -% We make sure that \file{expkv-cs.tex} is only input once: -% \begin{macrocode} -\expandafter\ifx\csname ekvcVersion\endcsname\relax -\else - \expandafter\endinput -\fi -% \end{macrocode} -% -% \begin{macro}{\ekvcVersion,\ekvcDate} -% We're on our first input, so lets store the version and date in a macro. -% \begin{macrocode} -\def\ekvcVersion{1.2} -\def\ekvcDate{2022-02-13} -% \end{macrocode} -% \end{macro} -% -% If the \LaTeX\ format is loaded we want to be a good file and report back who -% we are, for this the package will have defined |\ekvc@tmp| to use -% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. -% \begin{macrocode} -\csname ekvc@tmp\endcsname -% \end{macrocode} -% -% Store the category code of |@| to later be able to reset it and change it to -% 11 for now. -% \begin{macrocode} -\expandafter\chardef\csname ekvc@tmp\endcsname=\catcode`\@ -\catcode`\@=11 -% \end{macrocode} -% |\ekvc@tmp| will be reused later, but we don't need it to ever store -% information long-term after \expkvc\ was initialized. -% -% \begin{macro}[internal]{\ekvc@tripledots} -% This macro just serves as a marker for a comparison to allow the syntax -% for the unknown key handlers. -% \begin{macrocode} -\def\ekvc@tripledots{...} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@keycount} -% We'll need to keep count how many keys must be defined for each macro in the -% |split| variants. -% \begin{macrocode} -\newcount\ekvc@keycount -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@long,\ekvc@any@long} -% Some macros will have to be defined long. These two will be let to |\long| -% when this should be the case. -% \begin{macrocode} -\let\ekvc@long\ekv@empty -\let\ekvc@any@long\ekv@empty -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@ifdefined} -% We want to test whether a macro is already defined. This test checks for a -% defined macro that isn't |\relax|. -% \begin{macrocode} -\long\def\ekvc@ifdefined#1% - {% - \ifdefined#1% - \ifx\relax#1% - \ekv@fi@gobble - \fi - \@firstofone - \ekv@fi@firstoftwo - \fi - \@secondoftwo - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@ekvset@pre@expander, -% \ekvc@ekvset@pre@expander@a, -% \ekvc@ekvset@pre@expander@b -% } -% This macro expands |\ekvset| twice so that the first two steps of expansion -% don't have to be made every time the \expkvc\ macros are used. We have to do -% a little magic trick to get the macro parameter |#1| for the macro -% definition this is used in, even though we're calling |\unexpanded|. We do -% that by splitting the expanded |\ekvset| at some marks and place |##1| in -% between. At this spot we also add |\ekv@alignsafe| and |\ekv@endalignsafe| -% to ensure that macros created with \expkvc\ are alignment safe. -% \begin{macrocode} -\def\ekvc@ekvset@pre@expander#1% - {% - \expandafter\ekvc@ekvset@pre@expander@a\ekvset{#1}\ekvc@stop\ekvc@stop - } -\def\ekvc@ekvset@pre@expander@a - {% - \expandafter\ekvc@ekvset@pre@expander@b - } -\def\ekvc@ekvset@pre@expander@b#1\ekvc@stop#2\ekvc@stop - {% - \ekv@unexpanded\expandafter{\ekv@alignsafe}% - \ekv@unexpanded{#1}##1\ekv@unexpanded{#2}% - \ekv@unexpanded\expandafter{\ekv@endalignsafe}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcSplitAndUse} -% The first user macro we want to set up can be reused for -% \cs[no-index]{ekvcSplitAndForward} and \cs[no-index]{ekvcSplit}. We'll split -% this one up so that the test whether the macro is already defined doesn't -% run twice. -% \begin{macrocode} -\protected\long\def\ekvcSplitAndUse#1#2% - {% - \let\ekvc@helpers@needed\@firstoftwo - \ekvc@ifdefined#1% - {\ekvc@err@already@defined#1}% - {\ekvcSplitAndUse@#1{}{#2}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvcSplitAndUse@} -% The actual macro setting up things. We need to set some variables, forward -% the key list to |\ekvc@SetupSplitKeys|, and afterwards define the front -% facing macro to call |\ekvset| and put the initials and the argument sorting -% macro behind it. The internals |\ekvc@any@long|, |\ekvc@initials| and -% |\ekvc@keycount| will be set correctly by |\ekvc@SetupSplitKeys|. -% \begin{macrocode} -\protected\long\def\ekvcSplitAndUse@#1#2#3% - {% - \edef\ekvc@set{\string#1}% - \ekvc@SetupSplitKeys{#3}% - \ekvc@helpers@needed - {% - \ekvc@any@long\edef#1##1% - {% - \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% - \ekv@unexpanded\expandafter - {\csname ekvc@split@\the\ekvc@keycount\endcsname}% - \ekv@unexpanded\expandafter{\ekvc@initials{}#2}% - }% - }% - {% - \ekvc@any@long\edef#1##1% - {% - \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% - \ekv@unexpanded{#2}% - \ekv@unexpanded\expandafter{\ekvc@initials}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% -% \begin{macro}{\ekvcSplitAndForward} -% This just reuses |\ekvcSplitAndUse@| with a non-empty second argument, -% resulting in that argument to be called after the splitting. -% \begin{macrocode} -\protected\long\def\ekvcSplitAndForward#1#2#3% - {% - \let\ekvc@helpers@needed\@firstoftwo - \ekvc@ifdefined#1% - {\ekvc@err@already@defined#1}% - {\ekvcSplitAndUse@#1{{#2}}{#3}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcSplit} -% The first half is just |\ekvcSplitAndForward| then we define the macro to -% which the parsed key list is forwarded. There we need to allow for up to -% nine arguments. -% \begin{macrocode} -\protected\long\def\ekvcSplit#1#2#3% - {% - \let\ekvc@helpers@needed\@secondoftwo - \ekvc@ifdefined#1% - {\ekvc@err@already@defined#1}% - {% - \expandafter - \ekvcSplitAndUse@\expandafter#1\csname ekvc@\string#1\endcsname{#2}% - \ifnum\ekvc@keycount<1 - \ekvc@any@long\expandafter\def\csname ekvc@\string#1\endcsname{#3}% - \else - \ifnum\ekvc@keycount>9 - \ekvc@err@toomany{#1}% - \let#1\ekvc@undefined - \else - \ekvcSplit@build@argspec - \ekvc@any@long\expandafter - \def\csname ekvc@\string#1\expandafter\endcsname\ekvc@tmp{#3}% - \fi - \fi - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvcSplit@build@argspec,\ekvcSplit@build@argspec@} -% \begin{macrocode} -\protected\def\ekvcSplit@build@argspec - {% - \begingroup - \edef\ekvc@tmp - {\endgroup\def\ekv@unexpanded{\ekvc@tmp}{\ekvcSplit@build@argspec@{1}}}% - \ekvc@tmp - } -\def\ekvcSplit@build@argspec@#1% - {% - \ifnum#1>\ekvc@keycount - \ekv@fi@gobble - \fi - \@firstofone - {% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@#1\endcsname####}#1% - \expandafter\ekvcSplit@build@argspec@\expandafter{\the\numexpr#1+1}% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@SetupSplitKeys, \ekvc@SetupSplitKeys@a, \ekvc@SetupSplitKeys@b, -% \ekvc@SetupSplitKeys@c, \ekvc@SetupSplitKeys@d, \ekvc@SetupSplitKeys@e, -% \ekvc@SetupSplitKeys@check@unknown,\ekvc@SetupSplitKeys@unknown -% } -% These macros parse the list of keys and set up the key macros. First we need -% to initialise some macros and start |\ekvparse|. -% \begin{macrocode} -\protected\long\def\ekvc@SetupSplitKeys - {% - \ekvc@keycount=\ekv@zero - \let\ekvc@any@long\ekv@empty - \let\ekvc@initials\ekv@empty - \ekvparse\ekvc@SetupSplitKeys@check@unknown\ekvc@SetupSplitKeys@a - } -% \end{macrocode} -% Then we need to step the key counter for each key. Also we have to check -% whether this key has a |long| prefix so we initialise |\ekvc@long|. -% \begin{macrocode} -\protected\long\def\ekvc@SetupSplitKeys@a#1% - {\expandafter\ekvc@SetupSplitKeys@b\detokenize{#1}\ekvc@stop} -\protected\def\ekvc@SetupSplitKeys@b#1\ekvc@stop - {% - \advance\ekvc@keycount1 - \let\ekvc@long\ekv@empty - \ekvc@ifspace{#1}% - {\ekvc@SetupSplitKeys@c#1\ekvc@stop}% - {\ekvc@SetupSplitKeys@d{#1}}% - } -% \end{macrocode} -% If there was a space, there might be a prefix. If so call the prefix macro, -% else call the next step |\ekvc@SetupSplitKeys@d| which will define the key -% macro and add the key's value to the initials list. -% \begin{macrocode} -\protected\long\def\ekvc@SetupSplitKeys@c#1 #2\ekvc@stop - {% - \ekv@ifdefined{ekvc@split@p@#1}% - {\csname ekvc@split@p@#1\endcsname{#2}}% - {\ekvc@SetupSplitKeys@d{#1 #2}}% - } -% \end{macrocode} -% The inner definition is grouped, because we don't want to actually define -% the marks we build with |\csname|. We have to append the value to the -% |\ekvc@initials| list here with the correct split mark. The key macro will -% read everything up to those split marks and change the value following it to -% the value given to the key. Additionally we'll need a sorting macro for each -% key count in use so we set it up with |\ekvc@setup@splitmacro|. -% \begin{macrocode} -\protected\def\ekvc@SetupSplitKeys@d - {% - \begingroup\expandafter\endgroup - \expandafter\ekvc@SetupSplitKeys@e - \csname ekvc@splitmark@\the\ekvc@keycount\endcsname - } -\protected\long\def\ekvc@SetupSplitKeys@e#1#2#3% - {% - \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% -% \end{macrocode} -% The short variant needs a bit of special treatment. The key macro will be -% short to throw the correct error, but since there might be long macros -% somewhere the reordering of arguments needs to be long, so for short keys we -% use a two step approach, first grabbing only the short argument, then -% reordering. -% \begin{macrocode} - \unless\ifx\ekvc@long\long - \expandafter\let\csname ekvc@\ekvc@set(#2)\endcsname\ekvc@tmp - \edef\ekvc@tmp##1% - {% - \ekv@unexpanded\expandafter{\csname ekvc@\ekvc@set(#2)\endcsname}% - {##1}% - }% - \fi - \ekvlet\ekvc@set{#2}\ekvc@tmp - \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{#3}}}% - \ekvc@helpers@needed - {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% - {}% - } -% \end{macrocode} -% If no value was provided this could either be an error, or the unknown key -% forwarding. We have to check this (comparing against |...| inside -% |\ekvc@tripledots|) and if this is the unknown key list type, set it up -% accordingly (advancing the key count and setting up the unknown handlers of -% \expkv). Else we simply throw an error and ignore the incident. -% \begin{macrocode} -\protected\long\def\ekvc@SetupSplitKeys@check@unknown#1% - {% - \begingroup - \edef\ekvc@tmp{\detokenize{#1}}% - \expandafter - \endgroup - \ifx\ekvc@tripledots\ekvc@tmp - \advance\ekvc@keycount1 -% \end{macrocode} -% The |\begingroup\expandafter\endgroup| ensures that the split mark isn't -% actually defined (even if it just were with meaning |\relax|). -% \begin{macrocode} - \begingroup\expandafter\endgroup - \expandafter\ekvc@SetupSplitKeys@unknown - \csname ekvc@splitmark@\the\ekvc@keycount\endcsname - \let\ekvc@any@long\long - \else - \ekvc@err@value@required{#1}% - \fi - } -% \end{macrocode} -% -% \begin{macrocode} -\protected\long\def\ekvc@SetupSplitKeys@unknown#1% - {% - \long\expandafter\def\csname\ekv@name\ekvc@set{}u\endcsname##1##2##3#1##4% - {##3#1{##4,##2= {##1} }}% - \long\expandafter\def\csname\ekv@name\ekvc@set{}uN\endcsname##1##2#1##3% - {##2#1{##3,##1}}% - \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{}}}% - \ekvc@helpers@needed - {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% - {}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@split@p@long} -% The |long| prefix lets the internals |\ekvc@long| and |\ekvc@any@long| to -% |\long| so that the key macro will be long. -% \begin{macrocode} -\protected\def\ekvc@split@p@long - {% - \let\ekvc@long\long - \let\ekvc@any@long\long - \ekvc@SetupSplitKeys@d - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@split@p@short} -% The |short| prefix does essentially nothing, it is only provided to allow -% key names starting with |long| that aren't |\long|. -% \begin{macrocode} -\def\ekvc@split@p@short{\ekvc@SetupSplitKeys@d} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@defarggobbler} -% This is needed to define a macro with 1-9 parameters programmatically. -% \LaTeX's \cs[no-index]{newcommand} does something similar for example. -% \begin{macrocode} -\protected\def\ekvc@defarggobbler#1{\def\ekvc@tmp##1#1##2##{##1#1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@setup@splitmacro, -% \ekvc@split@1, \ekvc@split@2, \ekvc@split@3, -% \ekvc@split@4, \ekvc@split@5, \ekvc@split@6, -% \ekvc@split@7 -% } -% Since the first few split macros are different from the others we manually -% set those up now. All the others will be defined as needed (always -% globally). The split macros just read up until the correct split mark, move -% that argument into a list and reinsert the rest, calling the next split -% macro afterwards. -% \begin{macrocode} -\begingroup -\edef\ekvc@tmp - {% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@1\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}% - ##1##2##3% - {##3{##1}##2}% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@2\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% - ##3##4% - {##4{##1}{##2}##3}% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@3\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% - ##4##5% - {##5{##1}{##2}{##3}##4}% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@4\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% - ##5##6% - {##6{##1}{##2}{##3}{##4}##5}% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@5\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% - ##6##7% - {##7{##1}{##2}{##3}{##4}{##5}##6}% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@6\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@6\endcsname}##6% - ##7##8% - {##8{##1}{##2}{##3}{##4}{##5}{##6}##7}% - \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@7\endcsname}% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@6\endcsname}##6% - \ekv@unexpanded\expandafter{\csname ekvc@splitmark@7\endcsname}##7% - ##8##9% - {##9{##1}{##2}{##3}{##4}{##5}{##6}{##7}##8}% - } -\ekvc@tmp -\endgroup -\protected\def\ekvc@setup@splitmacro#1% - {% - \ekv@ifdefined{ekvc@split@#1}{}% - {% - \begingroup - \edef\ekvc@tmp - {% - \long\gdef - \ekv@unexpanded\expandafter{\csname ekvc@split@#1\endcsname}% - ####1% - \ekv@unexpanded\expandafter - {\csname ekvc@splitmark@#1\endcsname}% - ####2####3% - {% - \ekv@unexpanded\expandafter - {\csname ekvc@split@\the\numexpr#1-1\relax\endcsname}% - ####1{{####2}####3}% - }% - }% - \ekvc@tmp - \endgroup - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcHashAndUse} -% |\ekvcHashAndUse| works just like |\ekvcSplitAndUse|. -% \begin{macrocode} -\protected\long\def\ekvcHashAndUse#1#2% - {% - \let\ekvc@helpers@needed\@firstoftwo - \ekvc@ifdefined#1% - {\ekvc@err@already@defined#1}% - {\ekvcHashAndUse@#1{}{#2}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvcHashAndUse@} -% This is more or less the same as |\ekvcSplitAndUse@|. Instead of an empty -% group we place a marker after the initials, we don't use the sorting macros -% of |split|, but instead pack all the values in one argument. -% \begin{macrocode} -\protected\long\def\ekvcHashAndUse@#1#2#3% - {% - \edef\ekvc@set{\string#1}% - \ekvc@SetupHashKeys{#3}% - \ekvc@helpers@needed - {% - \ekvc@any@long\edef#1##1% - {% - \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% - \ekv@unexpanded{\ekvc@hash@pack@argument}% - \ekv@unexpanded\expandafter{\ekvc@initials\ekvc@stop#2}% - }% - }% - {% - \ekvc@any@long\edef#1##1% - {% - \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% - \ekv@unexpanded{#2}% - \ekv@unexpanded\expandafter{\ekvc@initials\ekvc@stop}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcHashAndForward} -% |\ekvcHashAndForward| works just like |\ekvcSplitAndForward|. -% \begin{macrocode} -\protected\long\def\ekvcHashAndForward#1#2#3% - {% - \let\ekvc@helpers@needed\@firstoftwo - \ekvc@ifdefined#1% - {\ekvc@err@already@defined#1}% - {\ekvcHashAndUse@#1{{#2}}{#3}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcHash} -% |\ekvcHash| does the same as |\ekvcSplit|, but has the advantage of not -% needing to count arguments, so the definition of the internal macro is a bit -% more straight forward. -% \begin{macrocode} -\protected\long\def\ekvcHash#1#2#3% - {% - \let\ekvc@helpers@needed\@secondoftwo - \ekvc@ifdefined#1% - {\ekvc@err@already@defined#1}% - {% - \expandafter - \ekvcHashAndUse@\expandafter#1\csname ekvc@\string#1\endcsname{#2}% - \ekvc@any@long\expandafter\def\csname ekvc@\string#1\endcsname - ##1\ekvc@stop - {#3}% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@hash@pack@argument} -% All this macro does is pack the values into one argument and forward that to -% the next macro. -% \begin{macrocode} -\long\def\ekvc@hash@pack@argument#1\ekvc@stop#2{#2{#1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@SetupHashKeys,\ekvc@SetupHashKeys@a,\ekvc@SetupHashKeys@b, -% \ekvc@SetupHashKeys@c,\ekvc@SetupHashKeys@d,\ekvc@SetupHashKeys@e, -% \ekvc@SetupHashKeys@check@unknown,\ekvc@SetupHashKeys@unknown -% } -% This should look awfully familiar as well, since it's just the same as for -% the split keys with a few other names here and there. -% \begin{macrocode} -\protected\long\def\ekvc@SetupHashKeys#1% - {% - \let\ekvc@any@long\ekv@empty - \let\ekvc@initials\ekv@empty - \ekvparse\ekvc@SetupHashKeys@check@unknown\ekvc@SetupHashKeys@a{#1}% - } -\protected\long\def\ekvc@SetupHashKeys@a#1% - {\expandafter\ekvc@SetupHashKeys@b\detokenize{#1}\ekvc@stop} -\protected\def\ekvc@SetupHashKeys@b#1\ekvc@stop - {% - \let\ekvc@long\ekv@empty - \ekvc@ifspace{#1}% - {\ekvc@SetupHashKeys@c#1\ekvc@stop}% - {\ekvc@SetupHashKeys@d{#1}}% - } -\protected\def\ekvc@SetupHashKeys@c#1 #2\ekvc@stop - {% - \ekv@ifdefined{ekvc@hash@p@#1}% - {\csname ekvc@hash@p@#1\endcsname{#2}}% - {\ekvc@SetupHashKeys@d{#1 #2}}% - } -% \end{macrocode} -% Again we build the marker, this time instead of a numbered one a named -% hashmark, inside a group to not actually define the macro used as a marker. -% \begin{macrocode} -\protected\long\def\ekvc@SetupHashKeys@d#1% - {% - \begingroup\expandafter\endgroup - \expandafter\ekvc@SetupHashKeys@e\csname ekvc@hashmark@#1\endcsname{#1}% - } -% \end{macrocode} -% Yes, even the defining macro looks awfully familiar. Instead of numbered we -% have named marks. Still the key macros grab everything up to their -% respective mark and reorder the arguments. The same quirk is applied for -% short keys. And instead of the |\ekvc@setup@splitmacro| we use -% |\ekvc@setup@hashmacro|. -% \begin{macrocode} -\protected\long\def\ekvc@SetupHashKeys@e#1#2#3% - {% - \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% - \unless\ifx\ekvc@long\long - \expandafter\let\csname ekvc@\ekvc@set(#2)\endcsname\ekvc@tmp - \edef\ekvc@tmp##1% - {% - \ekv@unexpanded\expandafter{\csname ekvc@\ekvc@set(#2)\endcsname}% - {##1}% - }% - \fi - \ekvlet\ekvc@set{#2}\ekvc@tmp - \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{#3}}}% - \ekvc@setup@hashmacro{#2}% - } -% \end{macrocode} -% \begin{macrocode} -\protected\long\def\ekvc@SetupHashKeys@check@unknown#1% - {% - \begingroup - \edef\ekvc@tmp{\detokenize{#1}}% - \expandafter - \endgroup - \ifx\ekvc@tripledots\ekvc@tmp - \ekvc@SetupHashKeys@unknown - \let\ekvc@any@long\long - \else - \ekvc@err@value@required{#1}% - \fi - } -% \end{macrocode} -% \begin{macrocode} -\def\ekvc@SetupHashKeys@unknown#1% - {% - \protected\def\ekvc@SetupHashKeys@unknown - {% - \expandafter - \let\csname\ekv@name\ekvc@set{}u\endcsname\ekvc@hash@unknown@kv - \expandafter - \let\csname\ekv@name\ekvc@set{}uN\endcsname\ekvc@hash@unknown@k - \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{}}}% - \ekvc@setup@hashmacro{...}% - }% - \long\def\ekvc@hash@unknown@kv##1##2##3#1##4{##3#1{##4,##2= {##1} }}% - \long\def\ekvc@hash@unknown@k##1##2#1##3{##2#1{##3,##1}}% - } -\expandafter\ekvc@SetupHashKeys@unknown - \csname ekvc@hashmark@\ekvc@tripledots\endcsname -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@hash@p@long} -% Nothing astonishing here either. -% \begin{macrocode} -\protected\def\ekvc@hash@p@long - {% - \let\ekvc@long\long - \let\ekvc@any@long\long - \ekvc@SetupHashKeys@d - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@hash@p@short} -% The |short| prefix does essentially nothing, it is only provided to allow -% key names starting with |long| that aren't |\long|. -% \begin{macrocode} -\def\ekvc@hash@p@short{\ekvc@SetupHashKeys@d} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@setup@hashmacro} -% The safe hash macros will be executed inside of an |\unexpanded| expansion -% context, so they have to insert braces for that once they are done. -% Most of the tests which have to be executed will already be done, but we -% have to play safe if the hash doesn't show up in the hash list. Therefore we -% use some |\ekvc@mark|s and |\ekvc@stop| to throw errors if the hash isn't -% found in the right place. The fast variants have an easier life and just -% return the correct value. -% \begin{macrocode} -\protected\def\ekvc@setup@hashmacro#1% - {% - \ekv@ifdefined{ekvc@fasthash@#1}{}% - {% - \begingroup - \edef\ekvc@tmp - {% - \long\gdef - \ekv@unexpanded\expandafter{\csname ekvc@fasthash@#1\endcsname}% - ####1% - \ekv@unexpanded\expandafter - {\csname ekvc@hashmark@#1\endcsname}% - ####2####3\ekv@unexpanded{\ekvc@stop}% - {####2}% - \long\gdef - \ekv@unexpanded\expandafter{\csname ekvc@safehash@#1\endcsname}% - ####1% - {% - \ekv@unexpanded\expandafter - {\csname ekvc@@safehash@#1\endcsname}% - ####1\ekv@unexpanded{\ekvc@mark}{}% - \ekv@unexpanded\expandafter - {% - \csname ekvc@hashmark@#1\endcsname{}% - \ekvc@mark{\ekvc@err@missing@hash{#1}}\ekvc@stop - }% - }% - \long\gdef - \ekv@unexpanded\expandafter - {\csname ekvc@@safehash@#1\endcsname}% - ####1% - \ekv@unexpanded\expandafter - {\csname ekvc@hashmark@#1\endcsname}% - ####2####3\ekv@unexpanded{\ekvc@mark}####4####5% - \ekv@unexpanded{\ekvc@stop}% - {% - ####4{####2}% - }% - \long\gdef\ekv@unexpanded\expandafter - {\csname ekvc@fastsplithash@#1\endcsname}% - ####1% - \ekv@unexpanded\expandafter - {\csname ekvc@hashmark@#1\endcsname}% - ####2####3\ekv@unexpanded{\ekvc@stop}####4% - {% - ####4{####2}% - }% - \long\gdef\ekv@unexpanded\expandafter - {\csname ekvc@safesplithash@#1\endcsname}####1% - {% - \ekv@unexpanded\expandafter - {\csname ekvc@@safesplithash@#1\endcsname}% - ####1\ekv@unexpanded{\ekvc@mark\ekvc@safe@after@hash}% - \ekv@unexpanded\expandafter - {% - \csname ekvc@hashmark@#1\endcsname{}% - \ekvc@mark - {\ekvc@err@missing@hash{#1}\ekvc@safe@after@hash}% - \ekvc@stop - }% - }% - \long\gdef\ekv@unexpanded\expandafter - {\csname ekvc@@safesplithash@#1\endcsname}% - ####1% - \ekv@unexpanded\expandafter - {\csname ekvc@hashmark@#1\endcsname}% - ####2####3\ekv@unexpanded{\ekvc@mark}####4####5% - \ekv@unexpanded{\ekvc@stop}% - {% - ####4{####2}% - }% - }% - \ekvc@tmp - \endgroup - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcValue} -% \begin{macro}[internal]{\ekvcValue@} -% All this does is a few consistency checks on the first argument (not empty, -% hash macro exists) and then call that hash-grabbing macro that will also -% test whether the hash is inside of |#2| or not. -% \begin{macrocode} -\long\def\ekvcValue#1% - {% - \ekv@unexpanded - \expandafter\ekvcValue@\detokenize{#1}\ekvc@stop - } -\def\ekvcValue@#1\ekvc@stop - {% - \ekv@ifdefined{ekvc@safehash@#1}% - {\csname ekvc@safehash@#1\endcsname}% - {\ekvc@err@unknown@hash{#1}\@firstoftwo{{}}}% - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ekvcValueFast} -% To be as fast as possible, this doesn't test for anything, assuming the user -% knows best. -% \begin{macrocode} -\long\def\ekvcValueFast#1#2% - {\csname ekvc@fasthash@\detokenize{#1}\endcsname#2\ekvc@stop} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcValueSplit} -% \begin{macro}[internal]{\ekvcValueSplit@,\ekvcValueSplit@recover} -% This splits off a single value. -% \begin{macrocode} -\long\def\ekvcValueSplit#1% - {\expandafter\ekvcValueSplit@\detokenize{#1}\ekvc@stop} -\def\ekvcValueSplit@#1\ekvc@stop - {% - \ekv@ifdefined{ekvc@safesplithash@#1}% - {\csname ekvc@safesplithash@#1\endcsname}% - {\ekvc@err@unknown@hash{#1}\ekvcValueSplit@recover}% - } -\long\def\ekvcValueSplit@recover#1#2{#2{}} -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@safe@after@hash} -% \begin{macrocode} -\long\def\ekvc@safe@after@hash#1#2% - {% - #2{#1}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcValueSplitFast} -% Again a fast approach which doesn't provide too many safety measurements. -% This needs to build the hash function and expand it before passing the -% results to the next control sequence. The first step only builds the control -% sequence. -% \begin{macrocode} -\long\def\ekvcValueSplitFast#1#2% - {\csname ekvc@fastsplithash@\detokenize{#1}\endcsname#2\ekvc@stop} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@safehash@,\ekvc@fasthash@, -% \ekvc@safesplithash@,\ekvc@fastsplithash@ -% } -% At least in the empty hash case we can provide a meaningful error message -% without affecting performance by just defining the macro that would be build -% in that case. There is of course a downside to this, the error will not be -% thrown by |\ekvcValueFast| in three expansion steps. The safe hash variant -% has to also stop the |\unexpanded| expansion. -% \begin{macrocode} -\long\def\ekvc@safehash@#1{\ekvc@err@empty@hash{}} -\long\def\ekvc@fasthash@#1\ekvc@stop{\ekvc@err@empty@hash} -\long\def\ekvc@safesplithash@#1#2{\ekvc@err@empty@hash#2{}} -\long\def\ekvc@fastsplithash@#1\ekvc@stop#2{\ekvc@err@empty@hash#2{}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcSecondaryKeys} -% \begin{macro}[internal] -% {\ekvcSecondaryKeys@a,\ekvcSecondaryKeys@b,\ekvcSecondaryKeys@c} -% The secondary keys are defined pretty similar to the way the originals are, -% but here we also introduce some key types (those have a |@t@| in their name) -% additionally to the prefixes. -% \begin{macrocode} -\protected\long\def\ekvcSecondaryKeys#1#2% - {% - \edef\ekvc@set{\string#1}% - \ekvparse\ekvc@err@value@required\ekvcSecondaryKeys@a{#2}% - } -\protected\long\def\ekvcSecondaryKeys@a#1% - {\expandafter\ekvcSecondaryKeys@b\detokenize{#1}\ekvc@stop} -\protected\def\ekvcSecondaryKeys@b#1\ekvc@stop - {% - \let\ekvc@long\ekv@empty - \ekvc@ifspace{#1}% - {\ekvcSecondaryKeys@c#1\ekvc@stop}% - {\ekvc@err@missing@type{#1}\@gobble}% - } -\protected\def\ekvcSecondaryKeys@c#1 #2\ekvc@stop - {% - \ekv@ifdefined{ekvc@p@#1}% - {\csname ekvc@p@#1\endcsname}% - {% - \ekv@ifdefined{ekvc@t@#1}% - {\csname ekvc@t@#1\endcsname}% - {\ekvc@err@unknown@keytype{#1}\@firstoftwo\@gobble}% - }% - {#2}% - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ekvcChange} -% This can be used to change the defaults of an \expkvc\ defined macro. It -% checks whether there is a set with the correct name and that the macro is -% defined. If both is true the real work is done by |\ekvc@change|. -% \begin{macrocode} -\protected\long\def\ekvcChange#1% - {% - \ekvifdefinedset{\string#1}% - {% - \ekvc@ifdefined#1% - {\ekvc@change#1}% - {\ekvc@err@no@key@macro#1\@gobble}% - }% - {\ekvc@err@no@key@macro#1\@gobble}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@change ,\ekvc@change@a,\ekvc@change@b, -% \ekvc@change@c,\ekvc@change@d,\ekvc@change@e -% } -% First we need to see whether the macro is currently |\long|. For this we get -% the meaning and will parse it. |#1| is the macro name in which we want to -% change the defaults. -% \begin{macrocode} -\protected\def\ekvc@change#1% - {\expandafter\ekvc@change@a\meaning#1\ekv@stop#1} -% \end{macrocode} -% A temporary definition to get the stringified |macro:|. |##1| will be the -% list of prefixes, we don't care for the exact contents of |##2| and |##3|. -% \begin{macrocode} -\def\ekvc@change@a#1% - {% - \protected\def\ekvc@change@a##1#1##2->##3\ekv@stop - {% - \ekvc@change@iflong{##1}% - {\ekvc@change@b{}}% - {\ekvc@change@b{\long}}% - }% - } -\expandafter\ekvc@change@a\expandafter{\detokenize{macro:}} -% \end{macrocode} -% Next we expand the macro once to get its contents (including the current -% default values with their markers) and place |\ekvc@stop| instead of an -% argument as a marker for the last step. |#1| is either |\long| or empty, -% |#2| is the macro. -% \begin{macrocode} -\protected\def\ekvc@change@b#1#2% - {\expandafter\ekvc@change@c\expandafter{#2\ekvc@stop}{#1}#2} -% \end{macrocode} -% Here we place an unbalanced closing brace after the expansion of the macro. -% Then we just parse the \kv-list with |\ekvset|, that will exchange the -% values behind the markers. Once those are changed we give control to -% |\ekvc@change@d|. The |\ekvset| step might horribly fail if the user defined -% some keys that don't behave nice. |#1| is the expansion of the macro, |#2| -% is either |\long| or empty, |#3| is the macro, and |#4| is the \kv-list -% containing the new defaults. -% \begin{macrocode} -\ekv@exparg{\protected\long\def\ekvc@change@c#1#2#3#4}% - {% - \expandafter\iffalse\expandafter{\expandafter{\expandafter\fi - \ekvset{\string#3}{#4}% - \ekvc@change@d{#2}{#3}% - #1% - }}% - } -% \end{macrocode} -% The final step needs to put an unbalanced opening brace after |\edef|. Also -% we have to protect everything from further expanding with the exception of -% the redefined macro's argument, which is why we placed the |\ekvc@stop| -% earlier. Then we need to also protect the rest of the contents from further -% expanding using |\unexpanded| with another unbalanced opening brace. -% |#1| will be either empty or |\long| and |#2| is the macro. -% \begin{macrocode} -\protected\def\ekvc@change@d#1#2% - {#1\edef#2##1{\expandafter\ekvc@change@e\iffalse}\fi} -\long\def\ekvc@change@e#1\ekvc@stop - {\ekv@unexpanded{#1}##1\ekv@unexpanded\expandafter{\iffalse}\fi} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@change@iflong,\ekvc@change@iflong@} -% Checking whether a string contains the string representation of |\long| can -% be done by gobbling everything up to the first |\long| and checking whether -% the result is completely empty. We need a temporary macro to get the result -% of |\string\long| inside the definitions. -% \begin{macrocode} -\def\ekvc@change@iflong#1% - {% - \protected\def\ekvc@change@iflong##1% - {\expandafter\ekv@ifempty\expandafter{\ekvc@change@iflong@##1#1}}% - \def\ekvc@change@iflong@##1#1{} - } -\expandafter\ekvc@change@iflong\expandafter{\string\long} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcPass} -% This macro can be used to pass a value to a key of some macro (this way -% more complicated key codes are possible that in the end pass processed -% values on to some macro). The implemantation is pretty straight forward. -% \begin{macrocode} -\long\def\ekvcPass#1#2% - {% - \ekvifdefined{\string#1}{#2}% - {\csname\ekv@name{\string#1}{#2}\endcsname}% - {\ekvc@err@unknown@key@or@macro{#1}{#2}\@gobble}% - } -% \end{macrocode} -% \end{macro} -% -% -% \subsubsection{Secondary Key Types} -% -% \begin{macro}[internal]{\ekvc@p@long,\ekvc@after@ptype} -% The prefixes are pretty straight forward again. Just set |\ekvc@long| and -% forward to the |@t@| type. -% \begin{macrocode} -\protected\def\ekvc@p@long#1% - {% - \ekvc@ifspace{#1}% - {% - \let\ekvc@long\long - \ekvc@after@ptype#1\ekvc@stop - }% - {\ekvc@err@missing@type{long #1}\@gobble}% - } -\protected\def\ekvc@after@ptype#1 #2\ekvc@stop - {% - \ekv@ifdefined{ekvc@t@#1}% - {\csname ekvc@t@#1\endcsname{#2}}% - {\ekvc@err@unknown@keytype{#1}\@gobble}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@t@meta, \ekvc@t@nmeta, -% \ekvc@type@meta, \ekvc@type@meta@a, \ekvc@type@meta@b -% } -% The |meta| and |nmeta| key types use a nested |\ekvset| to set other keys in -% the same macro's \set. -% \begin{macrocode} -\protected\def\ekvc@t@meta - {% - \edef\ekvc@tmp{\ekvc@set}% - \expandafter\ekvc@type@meta\expandafter{\ekvc@tmp}\ekvc@long{##1}\ekvlet - } -\protected\def\ekvc@t@nmeta#1% - {% - \ekvc@assert@not@long{nmeta #1}% - \edef\ekvc@tmp{\ekvc@set}% - \expandafter\ekvc@type@meta\expandafter{\ekvc@tmp}{}{}\ekvletNoVal{#1}% - } -\protected\long\def\ekvc@type@meta#1#2#3#4#5#6% - {% - \expandafter\ekvc@type@meta@a\expandafter{\ekvset{#1}{#6}}{#2}{#3}% - #4\ekvc@set{#5}\ekvc@tmp - } -\protected\def\ekvc@type@meta@a - {% - \expandafter\ekvc@type@meta@b\expandafter - } -\protected\long\def\ekvc@type@meta@b#1#2#3% - {% - #2\def\ekvc@tmp#3{#1}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@t@alias} -% |alias| just checks whether there is a key and/or |NoVal| key defined with -% the target name and |\let| the key to those. -% \begin{macrocode} -\protected\def\ekvc@t@alias#1#2% - {% - \ekvc@assert@not@long{alias #1}% - \let\ekvc@tmp\@firstofone - \ekvifdefined\ekvc@set{#2}% - {% - \ekvletkv\ekvc@set{#1}\ekvc@set{#2}% - \let\ekvc@tmp\@gobble - }% - {}% - \ekvifdefinedNoVal\ekvc@set{#2}% - {% - \ekvletkvNoVal\ekvc@set{#1}\ekvc@set{#2}% - \let\ekvc@tmp\@gobble - }% - {}% - \ekvc@tmp{\ekvc@err@unknown@key{#2}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@t@default} -% The |default| key can be used to set a |NoVal| key for an existing key. It -% will just pass the \val\ to the key macro of that other key. -% \begin{macrocode} -\protected\long\def\ekvc@t@default#1#2% - {% - \ekvifdefined\ekvc@set{#1}% - {% - \ekvc@assert@not@long{default #1}% - \edef\ekvc@tmp - {% - \ekv@unexpanded\expandafter - {\csname\ekv@name\ekvc@set{#1}\endcsname{#2}}% - }% - \ekvletNoVal\ekvc@set{#1}\ekvc@tmp - }% - {\ekvc@err@unknown@key{#1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@t@enum} -% \begin{macro}[internal] -% {\ekvc@type@enum,\ekvc@h@enum,\ekvc@h@enum@,\ekvc@enum@name} -% Enums don't need to apply special trickery to make the parts of the names -% retrievable, so unlike in \expkvd\ we don't need catcode juggling. -% The setup of an |enum| requires unpacking the value in two different -% arguments so we need an auxiliary here. -% \begin{macrocode} -\def\ekvc@enum@name#1#2#3{ekvc#1(#2)#3} -\protected\long\def\ekvc@t@enum#1#2% - {\ekvc@assert@twoargs{#2}{enum #1}{\ekvc@type@enum{#1}#2}} -\protected\long\def\ekvc@type@enum#1#2% - {% - \ekvifdefined\ekvc@set{#2}% - {% -% \end{macrocode} -% At run time we need another helper and we need to expand the current -% |\ekvc@set| now. The helper will build a control sequence from each given -% value, those will be set up in the |\ekvcsvloop|. -% \begin{macrocode} - \ekvc@long\edef\ekvc@tmp##1% - {% - \ekv@unexpanded{\expandafter\ekvc@h@enum\detokenize}{##1}% - \ekv@unexpanded{\ekvc@stop}% - {\ekvc@set}{#1}% - }% - \ekvlet\ekvc@set{#1}\ekvc@tmp - \def\ekvc@tmp{0}% - \expandafter\ekvcsvloop\expandafter - {% - \expandafter\ekvc@type@enum@ - \csname\ekv@name\ekvc@set{#2}\endcsname - {#1}% - }% - }% - {% - \ekvc@err@unknown@key{#2}% - \@gobble - }% - } -% \end{macrocode} -% Here |#1| will be the key-macro of the underlying primary or secondary key, -% |#2| is the |enum| key's name, and |#3| will be the choice. The rest is -% pretty obvious. -% \begin{macrocode} -\ekv@exparg{\protected\long\def\ekvc@type@enum@#1#2#3}% - {% - \expandafter\expandafter\expandafter\edef\expandafter - \csname\ekvc@enum@name\ekvc@set{#2}{\detokenize{#3}}\endcsname - {\ekv@unexpanded{#1}{\ekvc@tmp}}% - \edef\ekvc@tmp{\the\numexpr\ekvc@tmp+1\relax}% - } -% \end{macrocode} -% The use-time helper will check if the macro for the passed in choice exists, -% if it doesn't throws an error, else calls that macro which will set the -% correct value. -% \begin{macrocode} -\ekv@if@lastnamedcs - {% - \ekv@exparg{\def\ekvc@h@enum#1\ekvc@stop#2#3}% - {% - \expandafter\ifcsname\ekvc@enum@name{#2}{#3}{#1}\endcsname - \expandafter\ekvc@h@enum@\lastnamedcs - \fi - \ekvc@err@unknown@enum{#2}{#3}{#1}% - } - \def\ekvc@h@enum@#1\fi\ekvc@err@unknown@enum#2#3#4% - {% - \fi - \ifx#1\relax - \ekvc@err@unknown@enum{#2}{#3}{#4}% - \expandafter\@gobble - \fi - #1% - } - } - {% - \def\ekvc@h@enum#1% - {% - \def\ekvc@h@enum##1\ekvc@stop##2##3% - {% - \expandafter\ekvc@h@enum@ - \csname\ifcsname#1\endcsname#1\else relax\fi\endcsname - {##2}{##3}{##1}% - }% - } - \expandafter\ekvc@h@enum\expandafter{\ekvc@enum@name{#2}{#3}{#1}} - \def\ekvc@h@enum@#1#2#3#4% - {% - \ifx#1\relax - \ekvc@err@unknown@enum{#2}{#3}{#4}% - \expandafter\@gobble - \fi - #1% - } - } -% \end{macrocode} -% We don't need |\ekvc@enum@name| anymore, so let's undefine it. -% \begin{macrocode} -\let\ekvc@enum@name\ekvc@undefined -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@t@aggregate} -% Aggregating isn't easy to define. We'll have to extract the correct mark for -% the specified key, branch correctly for short and long keys, and use a small -% hack to have the correct arguments on the user interface (|#1| as the -% current contents, |#2| as the new value). This is split into a few steps -% here. -% -% First, assert that the user input is well-behaved. -% \begin{macrocode} -\protected\def\ekvc@t@aggregate#1% - {% - \ekvc@assert@not@long{aggregate #1}% - \ekvc@type@aggregate - \ekvc@type@aggregate@long\ekvc@type@aggregate@short - {process}% - {#1}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvc@type@aggregate,\ekvc@type@aggregate@a,\ekvc@type@aggregate@b} -% The next step stores the user defined processing in a temporary macro that's -% used to do the parameter number swapping later. It also builds the names of -% the key macro and the helper which would be used for processing a short key. -% \begin{macrocode} -\protected\long\def\ekvc@type@aggregate#1#2#3#4#5% - {% - \ekvc@assert@twoargs{#5}{#3 #4}{\ekvc@type@aggregate@a#1#2{#4}#5}% - } -\protected\long\def\ekvc@type@aggregate@a#1#2#3#4#5% - {% - \ekvifdefined\ekvc@set{#4}% - {% - \def\ekvc@type@aggregate@tmp##1##2{#5}% - \begingroup\expandafter\endgroup - \expandafter\ekvc@type@aggregate@b - \csname\ekv@name\ekvc@set{#4}\expandafter\endcsname - \csname ekvc@\ekvc@set(#4)\endcsname - #1#2% - {#3}% - }% - {\ekvc@err@unknown@key{#4}}% - } -\protected\long\def\ekvc@type@aggregate@b#1#2#3#4% - {% - \ekvc@type@aggregate@check@long#1#2% - {#3#1}% - {#4#2}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@type@aggregate@check@long, -% \ekvc@type@aggregate@check@long@a,\ekvc@type@aggregate@check@long@b -% } -% To check whether the primary key is long we see whether its |\meaning| -% contains the helper which would only be there for short keys. For this we -% have to get the stringified name of the internal (using |\detokenize|), -% and afterwards get the |\meaning| of the macro. A temporary helper does the -% real test by gobbling and forwarding the result to |\ekv@ifempty|. -% \begin{macrocode} -\protected\long\def\ekvc@type@aggregate@check@long#1#2% - {\expandafter\ekvc@type@aggregate@check@long@a\detokenize{#2}\ekv@stop#1} -\protected\long\def\ekvc@type@aggregate@check@long@a#1\ekv@stop#2% - {% - \def\ekvc@type@aggregate@check@long@@##1#1{}% - \expandafter\ekvc@type@aggregate@check@long@b\meaning#2\ekv@stop{#1}% - } -\protected\def\ekvc@type@aggregate@check@long@b#1\ekv@stop#2% - {\expandafter\ekv@ifempty\expandafter{\ekvc@type@aggregate@check@long@@#1#2}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@type@aggregate@long,\ekvc@type@aggregate@long@} -% The long variant just builds the split mark we extract, uses the hack to -% swap argument order, and then does the definition via |\ekvlet| and a -% temporary macro. -% \begin{macrocode} -\protected\long\def\ekvc@type@aggregate@long#1% - {% - \begingroup\expandafter\endgroup\expandafter - \ekvc@type@aggregate@long@ - \csname\ekvc@extract@mark#1\expandafter\endcsname - \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% - } -\protected\long\def\ekvc@type@aggregate@long@#1#2#3% - {% - \long\def\ekvc@type@aggregate@tmp##1##2#1##3{##2#1{#2}} - \ekvlet\ekvc@set{#3}\ekvc@type@aggregate@tmp - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvc@type@aggregate@short,\ekvc@type@aggregate@short@} -% The short variant will have to build the marker and the name of the helper -% function, and swap the user argument order. Hence here are a few more -% |\expandafter|s involved. But afterwards we can do the definition of the key -% and the helper macro directly. -% \begin{macrocode} -\protected\long\def\ekvc@type@aggregate@short#1#2% - {% - \begingroup\expandafter\endgroup\expandafter - \ekvc@type@aggregate@short@ - \csname\ekvc@extract@mark#1\expandafter\endcsname - \csname ekvc@\ekvc@set(#2)\expandafter\endcsname - \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% - {#2}% - } -\protected\long\def\ekvc@type@aggregate@short@#1#2#3#4% - {% - \ekvdef\ekvc@set{#4}{#2{##1}}% - \long\def#2##1##2#1##3{##2#1{#3}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@t@process} -% The |process| type can reuse much of |aggregate|, just the last step of -% definition differ. -% \begin{macrocode} -\protected\def\ekvc@t@process - {% - \ifx\ekvc@long\long - \ekv@fi@firstoftwo - \fi - \@secondoftwo - {% - \ekvc@type@aggregate - \ekvc@type@process@long\ekvc@type@process@long - }% - {% - \ekvc@type@aggregate - \ekvc@type@process@short\ekvc@type@process@short - }% - {process}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@type@process@long,\ekvc@type@process@long@} -% This defines a temporary macro to grab the current value (found after the -% marker |#1|), executes the user code and puts everything back to where it -% belongs. Then |\ekvlet| is used to assign that meaning to the key macro. -% \begin{macrocode} -\protected\long\def\ekvc@type@process@long#1% - {% - \begingroup\expandafter\endgroup\expandafter - \ekvc@type@process@long@ - \csname\ekvc@extract@mark#1\expandafter\endcsname - \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% - } -\protected\long\def\ekvc@type@process@long@#1#2#3% - {% - \long\def\ekvc@type@aggregate@tmp##1##2#1##3{#2##2#1{##3}}% - \ekvlet\ekvc@set{#3}\ekvc@type@aggregate@tmp - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@type@process@short,\ekvc@type@process@short@} -% We define the key macro directly to just grab the argument once and forward -% it to the auxiliary. That one does essentially the same as the long variant. -% \begin{macrocode} -\protected\long\def\ekvc@type@process@short#1#2% - {% - \begingroup\expandafter\endgroup\expandafter - \ekvc@type@process@short@ - \csname\ekvc@extract@mark#1\expandafter\endcsname - \csname ekvc@\ekvc@set(#2)\expandafter\endcsname - \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% - {#2}% - } -\protected\long\def\ekvc@type@process@short@#1#2#3#4% - {% - \ekvdef\ekvc@set{#4}{#2{##1}}% - \long\def#2##1##2#1##3{#3##2#1{##3}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@t@flag-bool} -% \begin{macrocode} -\protected\expandafter\def\csname ekvc@t@flag-bool\endcsname#1#2% - {% - \ekvc@assert@not@long{flag-bool #1}% - \unless\ifdefined#2\ekvcFlagNew#2\fi - \ekvdef\ekvc@set{#1}% - {% - \ekv@ifdefined{ekvc@flag@set@##1}% - {% - \csname ekvc@flag@set@##1\expandafter\endcsname - \ekvcFlagHeight#2\ekv@stop#2% - }% - {\ekvc@err@invalid@bool{##1}}% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvc@t@flag-true,\ekvc@t@flag-false,\ekvc@t@flag-raise,\ekvc@type@flag} -% \begin{macrocode} -\protected\def\ekvc@type@flag#1#2#3#4% - {% - \ekvc@assert@not@long{flag-#1 #3}% - \unless\ifdefined#4\ekvcFlagNew#4\fi - \ekv@exparg{\ekvdefNoVal\ekvc@set{#3}}{#2#4}% - } -\protected\expandafter\def\csname ekvc@t@flag-true\endcsname - {\ekvc@type@flag{true}\ekvcFlagSetTrue} -\protected\expandafter\def\csname ekvc@t@flag-false\endcsname - {\ekvc@type@flag{false}\ekvcFlagSetFalse} -\protected\expandafter\def\csname ekvc@t@flag-raise\endcsname - {\ekvc@type@flag{raise}\ekvcFlagRaise} -% \end{macrocode} -% \end{macro} -% -% -% \subsubsection{Flags} -% -% The basic idea of flags is to store information by the fact that \TeX\ -% expandably assigns the meaning |\relax| to undefined control sequences which -% were built with |\csname|. This mechanism is borrowed from \pkg{expl3}. -% -% \begin{macro}[internal]{\ekvc@flag@name,\ekvc@flag@namescheme} -% Flags follow a simple naming scheme which we define here. |\ekvc@flag@name| -% will store the name of an internal function that is used to build names of -% the second naming scheme defined by |\ekvc@flag@namescheme|. -% \begin{macrocode} -\def\ekvc@flag@name{ekvcf\string} -\def\ekvc@flag@namescheme#1#2{ekvch#2#1} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcFlagHeight} -% For semantic reasons we use |\number| with another name. -% \begin{macrocode} -\let\ekvcFlagHeight\number -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcFlagNew} -% This macro defines a new flag. It stores the function build with the -% |\ekvc@flag@name| naming scheme after the internal function -% |\ekvc@flag@height| that'll determine the current flag height. It'll also -% define the macro named via |\ekvc@flag@name| to build names according to -% |\ekvc@flag@namescheme|. -% \begin{macrocode} -\protected\def\ekvcFlagNew#1% - {% - \edef#1% - {% - \ekv@unexpanded{\ekvc@flag@height}% - \ekv@unexpanded\expandafter{\csname\ekvc@flag@name#1\endcsname}% - }% - \ekv@expargtwice - {\expandafter\def\csname\ekvc@flag@name#1\endcsname##1}% - {\expandafter\ekvc@flag@namescheme\expandafter{\string#1}{##1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@flag@height,\ekvc@flag@height@} -% This macro gets the height of a flag by a simple loop. The first loop -% iteration differs a bit from the following in that it doesn't have to get -% the current iteration count. The space at the end of |\ekvc@flag@height| -% ends the |\number| evaluation. -% \begin{macrocode} -\def\ekvc@flag@height#1% - {% - \ifcsname#10\endcsname - \ekvc@flag@height@1\ekv@stop#1% - \fi - \@firstofone{0} % leave this space - } -\def\ekvc@flag@height@#1\ekv@stop#2\fi\@firstofone#3% - {% - \fi - \ifcsname#2{#1}\endcsname - \expandafter\ekvc@flag@height@\the\numexpr#1+1\relax\ekv@stop#2% - \fi - \@firstofone{#1}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcFlagRaise} -% Raising a flag simply means letting the |\ekvc@flag@namescheme| macro for -% the current height to relax. The result of raising a flag is that its height -% is bigger by $1$. -% \begin{macrocode} -\ekv@exparg{\def\ekvcFlagRaise#1}% - {% - \expandafter\expandafter\expandafter\@gobble\expandafter - \csname\ekvc@flag@namescheme{\string#1}{\ekvcFlagHeight#1}\endcsname - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcFlagSetTrue,\ekvcFlagSetFalse} -% \begin{macro}[internal]{\ekvc@flag@set@true,\ekvc@flag@set@false} -% A flag is considered true if its current height is odd, and as false if it -% is even. Therefore |\ekvcFlagSetTrue| and |\ekvcFlagSetFalse| only need to -% raise the flag if the opposing boolean value is the current one. -% \begin{macrocode} -\def\ekvcFlagSetTrue#1% - {\expandafter\ekvc@flag@set@true\ekvcFlagHeight#1\ekv@stop#1} -\def\ekvcFlagSetFalse#1% - {\expandafter\ekvc@flag@set@false\ekvcFlagHeight#1\ekv@stop#1} -% \end{macrocode} -% We can expand |\ekvc@flag@namescheme| at definition time here, which is why -% we're using a temporary definition to set up |\ekvc@flag@set@true| and -% |\ekvc@flag@set@false|. -% \begin{macrocode} -\def\ekvc@flag@set@true#1% - {% - \def\ekvc@flag@set@true##1\ekv@stop##2% - {% - \ifodd##1 - \ekv@fi@gobble - \fi - \@firstofone{\expandafter\@gobble\csname#1\endcsname}% - }% - \def\ekvc@flag@set@false##1\ekv@stop##2% - {% - \ifodd##1 - \ekv@fi@firstofone - \fi - \@gobble{\expandafter\@gobble\csname#1\endcsname}% - }% - } -\expandafter\ekvc@flag@set@true\expandafter - {\ekvc@flag@namescheme{\string#2}{#1}} -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ekvcFlagIf} -% As already explained, truthiness is defined as a flag's height being odd, so -% we just branch accordingly here. -% \begin{macrocode} -\def\ekvcFlagIf#1% - {% - \ifodd#1% - \ekv@fi@firstoftwo - \fi - \@secondoftwo - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcFlagIfRaised} -% This macro uses flags as a switch, if a flag's current height is bigger than -% $0$ this test yields true. -% \begin{macrocode} -\ekv@exparg{\def\ekvcFlagIfRaised#1}% - {% - \expandafter\ifcsname\ekvc@flag@namescheme{\string#1}0\endcsname - \ekv@fi@firstoftwo - \fi - \@secondoftwo - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcFlagReset} -% \begin{macro}[internal]{\ekvc@flag@reset,\ekvc@flag@reset@} -% Resetting works by locally letting all the defined internal macros named -% after |\ekvc@flag@namescheme| to undefined. -% \begin{macrocode} -\protected\def\ekvcFlagReset#1% - {\expandafter\ekvc@flag@reset\csname\ekvc@flag@name#1\endcsname} -\protected\def\ekvc@flag@reset#1% - {% - \ifcsname#10\endcsname - \expandafter\let\csname#10\endcsname\ekvc@undefined - \ekvc@flag@reset@1\ekv@stop#1% - \fi - } -\protected\def\ekvc@flag@reset@#1\ekv@stop#2\fi - {% - \fi - \ifcsname#2{#1}\endcsname - \expandafter\let\csname#2{#1}\endcsname\ekvc@undefined - \expandafter\ekvc@flag@reset@\the\numexpr#1+1\relax\ekv@stop#2% - \fi - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ekvcFlagGetHeight} -% \begin{macro}[internal]{\ekvc@flag@get@height@single} -% These are just small helpers, first getting the height of the flag and then -% passing it on to the user supplied code. -% \begin{macrocode} -\def\ekvcFlagGetHeight#1% - {\expandafter\ekvc@flag@get@height@single\ekvcFlagHeight#1\ekv@stop} -\long\def\ekvc@flag@get@height@single#1\ekv@stop#2{#2{#1}} -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ekvcFlagGetHeights} -% \begin{macro}[internal] -% {\ekvc@flag@get@heights,\ekvc@flag@get@heights@,\ekvc@flag@get@heights@done} -% This works by a simple loop that stops at |\ekv@stop|. As long as that -% marker isn't hit, get the next flags height and put it into a list after -% |\ekv@stop|. |\ekvc@flag@get@heights@| uses the same marker name for the -% end of the height, which shouldn't clash in any case. Once we're done we -% remove the remainder of the current iteration and leave the user supplied -% code in the input stream with all the flags' heights as a single argument. -% \begin{macrocode} -\def\ekvcFlagGetHeights#1% - {% - \ekvc@flag@get@heights#1\ekv@stop{}% - } -\def\ekvc@flag@get@heights#1% - {% - \ekv@gobbleto@stop#1\ekvc@flag@get@heights@done\ekv@stop - \expandafter\ekvc@flag@get@heights@\ekvcFlagHeight#1\ekv@stop - } -\def\ekvc@flag@get@heights@#1\ekv@stop#2\ekv@stop#3% - {\ekvc@flag@get@heights#2\ekv@stop{#3{#1}}} -\long\def\ekvc@flag@get@heights@done - \ekv@stop - \expandafter\ekvc@flag@get@heights@\ekvcFlagHeight\ekv@stop\ekv@stop#1#2% - {#2{#1}} -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% -% \subsubsection{Helper Macros} -% -% \begin{macro}[internal]{\ekvc@ifspace,\ekvc@ifspace@} -% A test which can be reduced to an if-empty by gobbling everything up to the -% first space. -% \begin{macrocode} -\long\def\ekvc@ifspace#1% - {% - \ekvc@ifspace@#1 \ekv@ifempty@B - \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo - } -\long\def\ekvc@ifspace@#1 % keep this space - {% - \ekv@ifempty@\ekv@ifempty@A - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@ifnottwoargs,\ekvc@ifempty@gtwo} -% Used to test whether a token list contains exactly two \TeX\ arguments. -% \begin{macrocode} -\long\def\ekvc@ifnottwoargs#1% - {% - \ekvc@ifempty@gtwo#1\ekv@ifempty@B - \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo - } -\long\def\ekvc@ifempty@gtwo#1#2{\ekv@ifempty@\ekv@ifempty@A} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@extract@mark,\ekvc@extract@mark@} -% This is used to extract the mark of a split or hash key from its definition. -% This is kind of fragile, it assumes |#1| is always a macro used for hashing -% or splitting. Also it assumes that the escape character is a backslash. -% \begin{macrocode} -\def\ekvc@extract@mark#1{\expandafter\ekvc@extract@mark@\meaning#1\ekv@stop} -\begingroup -\lccode`;=`# -\lccode`/=`\\ -\lowercase{\endgroup -\def\ekvc@extract@mark@#1:#2/#3 ;#4\ekv@stop{#3}% -} -% \end{macrocode} -% \end{macro} -% -% -% \subsubsection{Assertions} -% \begin{macro}[internal]{\ekvc@assert@not@long} -% Some keys don't want to be |long| and we have to educate the user, so let's -% throw an error if someone wanted these to be long. -% \begin{macrocode} -\long\def\ekvc@assert@not@long#1{\ifx\ekvc@long\long\ekvc@err@no@long{#1}\fi} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@assert@twoargs} -% Some keys need exactly two arguments as their definition, so we have to -% somehow assert this. -% \begin{macrocode} -\protected\long\def\ekvc@assert@twoargs#1#2% - {\ekvc@ifnottwoargs{#1}{\ekvc@err@not@two{#2}}} -% \end{macrocode} -% \end{macro} -% -% -% \subsubsection{Messages} -% -% \begin{macro}[internal] -% { -% \ekvc@err@toomany,\ekvc@err@value@required,\ekvc@err@missing@type, -% \ekvc@err@already@defined,\ekvc@err@no@key@macro, -% \ekvc@err@not@two -% } -% Boring unexpandable error messages. -% \begin{macrocode} -\protected\def\ekvc@err@toomany#1% - {% - \errmessage{expkv-cs Error: Too many keys for macro `\string#1'}% - } -\protected\long\def\ekvc@err@value@required#1% - {% - \errmessage{expkv-cs Error: Missing value for key `\ekv@unexpanded{#1}'}% - } -\protected\long\def\ekvc@err@missing@type#1% - {% - \errmessage - {expkv-cs Error: Missing type for secondary key `\ekv@unexpanded{#1}'}% - } -\protected\long\def\ekvc@err@no@long#1% - {% - \errmessage - {expkv-cs Error: prefix `long' not accepted for `\ekv@unexpanded{#1}'}% - } -\protected\long\def\ekvc@err@already@defined#1% - {% - \errmessage{expkv-cs Error: Macro `\string#1' already defined}% - } -\protected\long\def\ekvc@err@unknown@keytype#1% - {% - \errmessage{expkv-cs Error: Unknown key type `\ekv@unexpanded{#1}'}% - } -\protected\long\def\ekvc@err@unknown@key#1% - {% - \errmessage - {expkv-cs Error: Unknown key `\ekv@unexpanded{#1}' for macro `\ekvc@set'}% - } -\protected\long\def\ekvc@err@no@key@macro#1% - {\errmessage{expkv-cs Error: \string#1 is no key=val macro}} -\protected\long\def\ekvc@err@not@two#1% - {% - \errmessage - {% - expkv-cs Error: Definition of `\ekv@unexpanded{#1}' doesn't contain - exactly two arguments% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvc@err} -% We need a way to throw error messages expandably in some contexts. -% \begin{macrocode} -\ekv@exparg{\long\def\ekvc@err#1}{\ekverr{expkv-cs}{#1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvc@err@unknown@hash,\ekvc@err@empty@hash,\ekvc@err@missing@hash, -% \ekvc@err@invalid@bool,\ekvc@err@unknown@enum -% } -% And here are the expandable error messages. -% \begin{macrocode} -\long\def\ekvc@err@unknown@hash#1{\ekvc@err{unknown hash `#1'}} -\long\def\ekvc@err@missing@hash#1{\ekvc@err{hash `#1' not found}} -\long\def\ekvc@err@empty@hash{\ekvc@err{empty hash}} -\def\ekvc@err@invalid@bool#1{\ekvc@err{invalid boolean value `#1'}} -\long\def\ekvc@err@unknown@key@or@macro#1#2% - {\ekvc@err{unknown key `#2' for macro #1}} -\def\ekvc@err@unknown@enum#1#2#3% - {\ekvc@err{unknown value `#3' for enum `#2' in macro #1}} -% \end{macrocode} -% \end{macro} -% -% -% Now everything that's left is to reset the category code of |@|. -% \begin{macrocode} -\catcode`\@=\ekvc@tmp -% \end{macrocode} -% -% \gobbledocstriptag -%</tex> -%^^A=<< -% -%^^A=<< -% -% \end{implementation}^^A=<< -% -% \clearpage -% \PrintIndex -% -\endinput -% -^^A vim: ft=tex fdm=marker fmr=>>=,=<< diff --git a/macros/generic/expkv-cs/expkv-cs.pdf b/macros/generic/expkv-cs/expkv-cs.pdf Binary files differdeleted file mode 100644 index 0c37223ebf..0000000000 --- a/macros/generic/expkv-cs/expkv-cs.pdf +++ /dev/null diff --git a/macros/generic/expkv-def/README.md b/macros/generic/expkv-def/README.md deleted file mode 100644 index 79a69057e4..0000000000 --- a/macros/generic/expkv-def/README.md +++ /dev/null @@ -1,32 +0,0 @@ -------------------------------------------------------------------------------- -# expkv-def -- a key-defining frontend for expkv - -Version 2022-01-29 v0.9 - -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt - -Hosted at https://github.com/Skillmon/tex_expkv-def - -------------------------------------------------------------------------------- - -Copyright (C) 2020-2021 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. - -------------------------------------------------------------------------------- - -This provides a key=val frontend to define keys for -[`expkv`](https://github.com/Skillmon/tex_expkv). It is generic code and only -requires `expkv` for its parsing. There is a LaTeX package `expkv-def.sty` -included to play nice on LaTeX's package loading system, but that package is not -needed and does not provide more functionality than the generic code in -`expkv-def.tex`. diff --git a/macros/generic/expkv-def/expkv-def.dtx b/macros/generic/expkv-def/expkv-def.dtx deleted file mode 100644 index 059c7dda01..0000000000 --- a/macros/generic/expkv-def/expkv-def.dtx +++ /dev/null @@ -1,2493 +0,0 @@ -% \iffalse meta-comment -% -% File: expkv-def.dtx Copyright (C) 2020-2022 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 -% -% ------------------------------------------------------------------------------ -% -%<*driver>^^A>>= -\def\nameofplainTeX{plain} -\ifx\fmtname\nameofplainTeX\else - \expandafter\begingroup -\fi -\input l3docstrip.tex -\askforoverwritefalse -\preamble - --------------------------------------------------------------- -expkv-def -- a key-defining frontend for expkv -E-mail: jspratte@yahoo.de -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt --------------------------------------------------------------- - -Copyright (C) 2020-2022 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. - -This work consists of the file expkv-def.dtx -and the derived files expkv-def.pdf - expkv-def.sty - expkv-def.tex - t-expkv-def.tex - -\endpreamble -% stop docstrip adding \endinput -\postamble -\endpostamble -\generate{\file{expkv-def.sty}{\from{expkv-def.dtx}{pkg}}} -\generate{\file{expkv-def.tex}{\from{expkv-def.dtx}{tex}}} -\generate{\file{t-expkv-def.tex}{\from{expkv-def.dtx}{ctx}}} -\ifx\fmtname\nameofplainTeX - \expandafter\endbatchfile -\else - \expandafter\endgroup -\fi -% -\IfFileExists{expkv-def.tex}{\input{expkv-def.tex}}{} -\ProvidesFile{expkv-def.dtx} - [\csname ekvdDate\endcsname\ a key-defining frontend for expkv] -\PassOptionsToPackage{full}{textcomp} -\documentclass{l3doc} -\RequirePackage[oldstylenums,nott]{kpfonts} -\input{glyphtounicode} -\pdfgentounicode=1 -\RequirePackage{tcolorbox} -\newtcolorbox{exresult}[2][] - {colback=expkvgrey!10!white,colframe=expkvgrey,fontupper=\small,width={#2},#1} -\newtcbox\exres[1][] - { - colback=expkvgrey!10!white - ,colframe=expkvgrey - ,size=small - ,nobeforeafter - ,tcbox raise base - ,fontupper=\small - ,#1 - } -\RequirePackage{listings} -\RequirePackage{booktabs} -\RequirePackage{array} -\RequirePackage{collcell} -\RequirePackage{siunitx} -\RequirePackage{xcolor} -\RequirePackage{caption} -\RequirePackage{microtype} -\RequirePackage{accsupp} -\lstdefinelanguage{expkvd}[primitive]{TeX} - { - ,moretexcs=[4]^^A e-TeX - { - expanded, - numexpr, - protected, - } - ,moretexcs=[5]^^A plain/LaTeX - { - approx, - begin, - empty, - item, - LaTeX, - makeatletter,makeatother, - newcommand,newdimen, - RequirePackage, - rule, - TeX, - textit,texttt, - usepackage, - } - ,moretexcs=[6]^^A used packages - { - ^^A xfp - fpeval, - ^^A keyval - setkeys, - ^^A yax - defactiveparameter,storevalue,setparameterlist - } - ,moretexcs=[2]^^A expkv-def macros - { - ekvdefinekeys - } - ,moretexcs=[3]^^A expkv-pkg macros - { - ^^A expkv - ekvdef,ekvdefNoVal,^^A - ekvlet,ekvletNoVal,ekvletkv,ekvletkvNoVal,^^A - ekvdefunknown,ekvdefunknownNoVal,^^A - ekvredirectunknown,ekvredirectunknownNoVal,^^A - ekvset,ekvsetSneaked,^^A - ekvsetdef,ekvsetSneakeddef,ekvsetdefSneaked,^^A - ekvparse,^^A - ekvVersion,ekvDate,^^A - ekvifdefined,ekvifdefinedNoVal,ekvifdefinedset,^^A - ekvbreak,ekvbreakPreSneak,ekvbreakPostSneak,^^A - ekvsneak,ekvsneakPre,^^A - ekvchangeset,^^A - ekvoptarg,ekvoptargTF,^^A - ekverr, - ^^A expkv-cs - ekvcSecondaryKeys,ekvcSplitAndForward, - } - } -\colorlet{codeparam}{cyan!65!black} -\def\mylstwd{.55em} -\lstset - { - ,language=expkvd - ,flexiblecolumns=false - ,basewidth=\mylstwd - ,gobble=2 - ,basicstyle=\fontfamily{jkp}\itshape - ,texcsstyle=*[1]{\bfseries\color{expkvgrey}} ^^A primitives - ,texcsstyle=*[2]{\bfseries\color{expkvred}} ^^A expkv - ,texcsstyle=*[3]{\color{expkvred}} ^^A expkv-pkg - ,texcsstyle=*[4]{\bfseries\color{expkvgrey}} ^^A e-TeX - ,texcsstyle=*[5]{\bfseries\color{expkvgrey}} ^^A plain/LaTeX macros - ,texcsstyle=*[6]{} ^^A macros of other packages - ,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} - ^^A,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}}} -\RequirePackage{randtext} -\let\metaORIG\meta -\protected\def\meta #1{\texttt{\metaORIG{#1}}} -\renewcommand*\thefootnote{\fnsymbol{footnote}} -\definecolor{expkvred}{HTML}{9F393D} -\colorlet{expkvgrey}{black!75} -\makeatletter -\newcommand*\expkv - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv}^^A - \href{https://github.com/Skillmon/tex_expkv} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv}^^A - } -\newcommand*\expkvd - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv-def}^^A - \href{https://github.com/Skillmon/tex_expkv-def} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - {\color{expkvgrey}^^A - \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{def}^^A - }^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv-def}^^A - } -\newcommand\kv{\meta{key}=\meta{value}} -\newcommand\key{\meta{key}} -\newcommand\val{\meta{value}} -\newcommand\set{\meta{set}} -\newcommand\enfprefix{\textcolor{black}} -\newcommand\allprefix{\textcolor{gray}} -\newcommand\notprefix{\textcolor{red!80!black}} -\newcommand\printprefix[2] - {^^A - \ifcase\numexpr#1\relax\or - \enfprefix{#2}\or - \allprefix{#2}\or - \notprefix{#2}\fi - } -\newcommand\prefixes[4] - {^^A - \hfill - \printprefix{#1}{new} - \printprefix{#2}{also} - \printprefix{#3}{protected} - \printprefix{#4}{long}^^A - } -\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} -\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} -\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long} -\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space} -\DoNotIndex{\@,\unexpanded,\string,\expanded,\dimexpr,\global,\glueexpr,\hbox} -\DoNotIndex{\numexpr,\RequirePackage,\setbox,\the,\unless,\xdef,\@firstofone} -\DoNotIndex{\@firstoftwo,\@gobble,\@secondoftwo,\meaning,\detokenize} -\DoNotIndex{\romannumeral,\^} -\DoNotIndex{\ifcsname} -\DoNotIndex{\ifx} -\DoNotIndex{\ifdefined} -\DoNotIndex{\iffalse} -\DoNotIndex{\iftrue} -\DoNotIndex{\else} -\DoNotIndex{\fi} -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} -\makeatother -\begin{document} - \title - {^^A - \texorpdfstring - {^^A - \huge\expkvd - \\[\medskipamount] - \Large a key-defining frontend for \expkv - }{expkv-def - a key-defining frontend for expkv}^^A - } - \date{\ekvdDate\space v\ekvdVersion} - \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}} - \DocInput{expkv-def.dtx} -\end{document} -%</driver>^^A=<< -% \fi -% -% \maketitle -% \renewcommand*\thefootnote{\arabic{footnote}} -% -% \begin{abstract} -% \noindent\parfillskip=0pt -% \expkvd\ provides a small \kv\ interface to define keys for \expkv. Key-types -% are declared using prefixes, similar to static typed languages. The stylised -% name is \expkvd\ but the files use \file{expkv-def}, this is due to CTAN-rules -% which don't allow \string| in package names since that is the pipe symbol in -% *nix shells. -% \end{abstract} -% -% \tableofcontents -% -% \begin{documentation}^^A>>= -% -% \section{Documentation} -% -% 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, besides its -% unique selling points of being fully expandable, and fast and reliable. But at -% the same time I don't want to widen \expkv's initial scope. So here it 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 space is way easier than parsing for the last one. -% \expkvd's prefixes are sorted into two categories: |p|-type, which are -% equivalent to \TeX's prefixes like |\long|, and |t|-type defining the type of -% the key. For a description of the available |p|-prefixes take a look at -% \autoref{sec:prefix:p}, the |t|-prefixes are described in -% \autoref{sec:prefix:t}. -% -% \expkvd\ is usable as generic code, as a \LaTeX\ package, and as a \ConTeXt\ -% module. It'll automatically load \expkv\ in the same mode as well. To use it, -% just use one of -% \begin{lstlisting} -% \input expkv-def % plainTeX -% \usepackage{expkv-def} % LaTeX -% \usemodule[expkv-def] % ConTeXt -% \end{lstlisting} -% -% \subsection{Macros} -% Apart from version and date containers there is only a single user-facing -% macro, and that should be used to define keys. -% -% \begin{function}{\ekvdefinekeys} -% \begin{syntax} -% \cs{ekvdefinekeys}\marg{set}\{\kv, \ldots\} -% \end{syntax} -% In \set, define \key\ to have definition \val. The general syntax for \key\ -% should be -% \begin{quote}\ttfamily\small -% \meta{prefix} \meta{name} -% \end{quote} -% Where \meta{prefix} is a space separated list of optional |p|-type prefixes -% followed by one |t|-type prefix. The syntax of \val\ is dependent on the -% used |t|-prefix. -% \end{function} -% -% \begin{function}{\ekvdDate,\ekvdVersion} -% These two macros store the version and date of the package. -% \end{function} -% -% \subsection{Prefixes} -% As already said there are |p|-prefixes and |t|-prefixes. Not every |p|-prefix -% is allowed for all |t|-prefixes. -% -% \subsubsection{\texttt{p}-Prefixes}\label{sec:prefix:p} -% The two |p|-type prefixes |long| and |protected| are pretty simple by nature, -% so their description is pretty simple. They affect the \key\ at use-time, so -% omitting |long| doesn't mean that a \meta{definition} can't contain a |\par| -% token, only that the \key\ will not accept a |\par| in \val. On the other -% hand |new| and |also| might be simple on first sight as well, but their rules -% are a bit more complicated. -% -% \begin{function}{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 |long| or -% |protected|, but you can as well add |long| or |protected| with the |also| -% definition. -% -% As a small example, suppose you want to create a boolean key, but -% additionally to setting a boolean value you want to execute some more code -% as well, you can use the following -% \begin{lstlisting} -% \ekvdefinekeys{also-example} -% { -% bool key = \ifmybool -% ,also code key = \domystuff{#1} -% } -% \end{lstlisting} -% -% 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. -% \end{function} -% -% \begin{function}{protected,protect} -% The following key will be defined |\protected|. Note that key-types which -% can't be defined expandable will always use |\protected|. -% \end{function} -% -% \begin{function}{long} -% The following key will be defined |\long|. -% \end{function} -% -% \begin{function}{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 |NoVal| keys and other |NoVal| -% keys or value taking keys and other value taking keys. For example you can -% use the following without an error: -% \begin{lstlisting} -% \ekvdefinekeys{new-example} -% { -% code key = \domystuffwitharg{#1} -% ,new noval key = \domystuffwithoutarg -% } -% \end{lstlisting} -% \end{function} -% -% \subsubsection{\texttt{t}-Prefixes}\label{sec:prefix:t} -% Since the |p|-type prefixes apply to some of the |t|-prefixes 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}{code,ecode} -% \begin{syntax} -% code \key\ = \marg{definition} \prefixes2222 -% \end{syntax} -% Define \key\ to expand to \meta{definition}. The \key\ will require a \val\ -% for which you can use |#1| inside \meta{definition}. The |ecode| variant -% will fully expand \meta{definition} inside an |\edef|. -% \end{function} -% -% \begin{function}{noval,enoval} -% \begin{syntax} -% noval \key\ = \marg{definition} \prefixes2223 -% \end{syntax} -% The |noval| type defines \key\ to expand to \meta{definition}. The \key\ -% will not take a \val. |enoval| fully expands \meta{definition} inside an -% |\edef|. -% \end{function} -% -% \begin{function}{default,qdefault,odefault,fdefault,edefault} -% \begin{syntax} -% default \key\ = \marg{definition} \prefixes3223 -% \end{syntax} -% This serves to place a default \val\ for a \key\ that takes an argument, the -% \key\ can be of any argument-grabbing kind, and when used without a \val\ -% it will be passed \meta{definition} instead. The |qdefault| variant will -% expand the \key's code once, so will be slightly quicker, but not change if -% you redefine \key. |odefault| is just another name for |qdefault|. 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 inside of an |\edef|. -% \end{function} -% \footnotetext -% {^^A -% For those familiar with \TeX-coding: This uses a -% \cs[no-index]{romannumeral}-expansion. -% } -% -% \begin{function}{initial,oinitial,finitial,einitial} -% \begin{syntax} -% initial \key\ = \{\val\} \prefixes3333 -% initial \key -% \end{syntax} -% With |initial| you can set an initial \val\ for an already defined argument -% taking \key. It'll just call the key-macro of \key\ and pass it \val. The -% |einitial| variant will expand \val\ using an |\edef| expansion prior to -% passing it to the key-macro 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 value (and no equals sign) a |noval| \key\ of the -% same name is called once (or, if you specified a |default| for a value -% taking key that would be used). -% \end{function} -% \footnotetext{Again using \cs[no-index]{romannumeral}.} -% -% \begin{function}{bool,gbool,boolTF,gboolTF} -% \begin{syntax} -% bool \key\ = \meta{cs} \prefixes2223 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\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 without a \val\ it'll have the same effect as if you use -% \texttt{\key=true}. |bool| and |gbool| will behave like \TeX-ifs so either -% be |\iftrue| or |\iffalse|. The |boolTF| and |gboolTF| variants will both -% 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 control sequence globally, the others 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| \key\ is the -% same as a |choice| key which is set up to handle |true| and |false| as -% choices. -% \end{function} -% -% \begin{function}{invbool,ginvbool,invboolTF,ginvboolTF} -% \begin{syntax} -% bool \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 -% \texttt{key=true} is used |invbool| will set \meta{cs} to |\iffalse| and -% vice versa. -% \end{function} -% -% \begin{function}{boolpair,gboolpair,boolpairTF,gboolpairTF} -% \begin{syntax} -% boolpair \key\ = \meta{cs_1}\meta{cs_2} \prefixes2223 -% \end{syntax} -% The |boolpair| key 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}{store,estore,gstore,xstore} -% \begin{syntax} -% store \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. -% This will define \key\ 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 |store| and |gstore| will allow you to store macro -% parameters inside of them by using |\unexpanded|. -% \end{function} -% -% \begin{function}{data,edata,gdata,xdata} -% \begin{syntax} -% data \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. This will -% define \key\ 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 \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. The |p|-prefixes will -% only affect the key-macro, \meta{cs} will always be expandable and |\long|. -% \end{function} -% -% \begin{function}{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 a \val\ was -% given to \key\ the \meta{cs} will behave like |\@firstofone| appended by -% \marg{value}. -% \end{function} -% -% \begin{function}{int,eint,gint,xint} -% \begin{syntax} -% int \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. -% An |int| key will be a \TeX-count register. If \meta{cs} isn't defined yet, -% |\newcount| will be used to initialise it. The |eint| and |xint| versions -% will use |\numexpr| to allow basic computations in their \val. The |gint| -% and |xint| variants set the register globally. -% \end{function} -% -% \begin{function}{dimen,edimen,gdimen,xdimen} -% \begin{syntax} -% dimen \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. -% This is just like |int| but uses a dimen register, |\newdimen| and -% |\dimexpr| instead. -% \end{function} -% -% \begin{function}{skip,eskip,gskip,xskip} -% \begin{syntax} -% skip \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. -% This is just like |int| but uses a skip register, |\newskip| and |\glueexpr| -% instead. -% \end{function} -% -% \begin{function}{toks,gtoks,apptoks,gapptoks,pretoks,gpretoks} -% \begin{syntax} -% toks \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\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}{box,gbox} -% \begin{syntax} -% box \key\ = \meta{cs} \prefixes2212 -% \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. -% Typesets \val\ into a |\hbox| and stores the result in a box register. The -% boxes are colour safe. \expkvd\ doesn't provide a |vbox| type. -% \end{function} -% -% \begin{function}{meta} -% \begin{syntax} -% meta \key\ = \{\kv, \ldots\} \prefixes2222 -% \end{syntax} -% This key type can set other keys, you can access the \val\ which was passed -% to \key\ inside the \kv\ list with |#1|. It works by calling a sub-|\ekvset| -% on the \kv\ list, so a |set| key will only affect that \kv\ list and not the -% current |\ekvset|. Since it runs in a separate |\ekvset| you can't use -% |\ekvsneak| using keys or similar macros in the way you normally could. -% \end{function} -% -% \begin{function}{nmeta} -% \begin{syntax} -% nmeta \key\ = \{\kv, \ldots\} \prefixes2223 -% \end{syntax} -% This key type can set other keys, the difference to |meta| is, that this key -% doesn't take a value, so the \kv\ list is static. -% \end{function} -% -% \begin{function}{smeta} -% \begin{syntax} -% smeta \key\ = \marg{set}\{\kv, \ldots\} \prefixes2222 -% \end{syntax} -% Yet another |meta| variant. An |smeta| key will take a \val\ which you can -% access using |#1|, but it sets the \kv\ list inside of \set, so is equal to -% \texttt{\cs[no-index]{ekvset}\marg{set}\{\kv, \ldots\}}. -% \end{function} -% -% \begin{function}{snmeta} -% \begin{syntax} -% snmeta \key\ = \marg{set}\{\kv, \ldots\} \prefixes2223 -% \end{syntax} -% And the last |meta| variant. |snmeta| is a combination of |smeta| and -% |nmeta|. It doesn't take an argument and sets the \kv\ list inside of \set. -% \end{function} -% -% \begin{function}{set} -% \begin{syntax} -% set \key\ = \marg{set} \prefixes2233 -% \end{syntax} -% This will define \key\ to change the set of the current |\ekvset| -% invocation to \set. You can omit \set\ (including the equals sign), -% which is the same as using \texttt{set \key\ = \{\key\}}. The created |set| -% key will not take a \val. Note that just like in \expkv\ it'll not be -% checked whether \set\ is defined and you'll get a low-level \TeX\ error if -% you use an undefined \set. -% \end{function} -% -% \begin{function}{choice} -% \begin{syntax} -% choice \key\ = \{\val=\meta{definition}, \ldots\} \prefixes2223 -% \end{syntax} -% Defines \key\ to be a |choice| key, meaning it will only accept a limited -% set of values. You should define each possible \val\ inside of the -% \val=\meta{definition} list. If a defined \val\ is passed to \key\ the -% \meta{definition} will be left in the input stream. You can make -% individual values |protected| inside the \val=\meta{definition} list. By -% default a |choice| key is expandable, an undefined \val\ will throw an error -% in an expandable way (but see the |unknown-choice| prefix). You can add -% additional choices after the \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. -% \end{function} -% -% \begin{function}{choice-store} -% \begin{syntax} -% choice-store \key\ = \meta{cs}\{\val, \ldots\} \prefixes2223 -% \end{syntax} -% This is defines a special type of |choice| key that'll store the given -% choice inside the macro \meta{cs} (so \meta{cs} should be a single control -% sequence name such as |\foo|). Since storing inside a macro can't be done -% expandably every choice-code is |\protected|, you might define the -% |choice-store| key itself as |protected| as well if you want. Since the -% definition of each choice is predefined with this key type the choice list -% should just be a comma separated list of valid choices. -% \end{function} -% This means that the following |choice| and |choice-store| keys are equivalent -% at use time: -% \begin{lstlisting} -% \newcommand*\mya{} -% \ekvdefinekeys{example} -% { -% choice key1 = {a=\def\mya{a}, b=\def\mya{b}, c=\def\mya{c}} -% ,choice-store key2 = \mya{a,b,c} -% } -% \end{lstlisting} -% -% \begin{function}{choice-enum} -% \begin{syntax} -% choice-enum \key\ = \meta{cs}\{\val, \ldots\} \prefixes2223 -% \end{syntax} -% This is similar to |choice-store|, the differences are: \meta{cs} should be -% a count-register or is initialised as such if the \meta{cs} is undefined -% (via |\newcount|); instead of the value the position of the value in the -% given list is stored in this register (zero-based). -% \end{function} -% This means that the following |choice| and |choice-enum| keys are equivalent -% at use time: -% \begin{lstlisting} -% \newcount\myb -% \ekvdefinekeys{example} -% { -% choice key1 = {a={\myb=0 }, b={\myb=1 }, c={\myb=2 }} -% ,choice-enum key2 = \myb{a,b,c} -% } -% \end{lstlisting} -% -% \begin{function}{unknown-choice} -% \begin{syntax} -% unknown-choice \key\ = \marg{definition} \prefixes2323 -% \end{syntax} -% By default an unknown \val\ passed to a |choice| or |bool| key 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 choice which was passed in with |#1|. -% \end{function} -% -% \begin{function}{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| prefix you can define handlers that deal with undefined -% keys, instead of a \key\ name you have to specify a subtype for this prefix, -% here the subtype is |code|.\par -% With |unknown code| the \meta{definition} is used for unknown keys which -% were provided a value (so corresponds to |\ekvdefunknown|), you can access -% the key name with |#1| and the value with |#2|.\footnotemark -% \end{function} -% \footnotetext -% {^^A -% 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.^^A -% } -% -% \begin{function}{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 key name with |#1|. -% \end{function} -% -% \begin{function}{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 \key\ in each \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 \key\ isn't found in any \set\ an -% expandable error will be thrown eventually. Internally \expkv's -% |\ekvredirectunknown| will be used. -% \end{function} -% -% \begin{function}{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}{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} -% -% \subsection{Bugs} -% I don't think there are any (but every developer says that), if you find some -% please let me know, either via the email address on the first page or on -% GitHub: -% \url{https://github.com/Skillmon/tex_expkv-def} -% -% \subsection{Example} -% The following is an example code defining each base key-type once. Please -% admire the very creative key-name examples. -% -% \begin{lstlisting} -% \ekvdefinekeys{example} -% { -% long code keyA = #1 -% ,noval keyA = NoVal given -% ,bool keyB = \keyB -% ,boolTF keyC = \keyC -% ,store keyD = \keyD -% ,data keyE = \keyE -% ,dataT keyF = \keyF -% ,int keyG = \keyG -% ,dimen keyH = \keyH -% ,skip keyI = \keyI -% ,toks keyJ = \keyJ -% ,default keyJ = \empty test -% ,new box keyK = \keyK -% ,qdefault keyK = K -% ,choice keyL = -% { -% protected 1 = \texttt{a} -% ,2 = b -% ,3 = c -% ,4 = d -% ,5 = e -% } -% ,edefault keyL = 2 -% ,meta keyM = {keyA={#1},keyB=false} -% ,invbool keyN = \keyN -% ,boolpair keyO = \keyOa\keyOb -% } -% \end{lstlisting} -% -% \bigskip -% Since the |data| type might be a bit strange, here is another usage example -% for it. -% \begin{lstlisting} -% \ekvdefinekeys{ex} -% { -% data name = \Pname -% ,data age = \Page -% ,dataT hobby = \Phobby -% } -% \newcommand\Person[1] -% {% -% \begingroup -% \ekvset{ex}{#1}% -% \begin{description} -% \item[\Pname{}{\errmessage{A person requires a name}}] -% \item[Age] \Page{\textit}{\errmessage{A person requires an age}} -% \Phobby{\item[Hobbies]} -% \end{description} -% \endgroup -% } -% \Person{name=Jonathan P. Spratte, age=young, hobby=\TeX\ coding} -% \Person{name=Some User, age=unknown, hobby=Reading Documentation} -% \Person{name=Anybody, age=any} -% \end{lstlisting} -% In this example a person should have a name and an age, but doesn't have to -% have hobbies. The name will be displayed as the description item and the age -% in Italics. If a person has no hobbies the description item will be silently -% left out. The result of the above code looks like this: -% -% \begin{exresult}{\linewidth} -% \ekvdefinekeys{ex} -% { -% data name = \Pname -% ,data age = \Page -% ,dataT hobby = \Phobby -% } -% \newcommand\Person[1] -% {% -% \begingroup -% \ekvset{ex}{#1}% -% \begin{description} -% \item[\Pname{}{\errmessage{A person requires a name}}] -% \item[Age] \Page{\textit}{\errmessage{A person requires an age}} -% \Phobby{\item[Hobbies]} -% \end{description} -% \endgroup -% } -% \Person{name=Jonathan P. Spratte, age=young, hobby=\TeX\ coding} -% \Person{name=Some User, age=unknown, hobby=Reading Documentation} -% \Person{name=Anybody, age=any} -% \end{exresult} -% -% \subsection{License} -% -% Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\ -% Jonathan P. Spratte -% -% \medskip\noindent -% 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: -% -% \url{http://www.latex-project.org/lppl.txt} -% -% \noindent -% This work is ``maintained'' (as per LPPL maintenance status) by -% -% \mbox{Jonathan P. Spratte}. -% -% \end{documentation}^^A=<< -% -% \begin{implementation}^^A>>= -% -% \clearpage -% -% \section{Implementation}^^A>>= -%^^A the LaTeX package >>= -% \subsection{The \LaTeX\ Package} -% Just like for \expkv\ we provide a small \LaTeX\ package that sets up things -% such that we behave nicely on \LaTeX\ packages and files system. It'll -% |\input| the generic code which implements the functionality. -% \gobbledocstriptag -%<*pkg> -% \begin{macrocode} -\RequirePackage{expkv} -\def\ekvd@tmp - {% - \ProvidesFile{expkv-def.tex}% - [\ekvdDate\space v\ekvdVersion\space a key-defining frontend for expkv]% - } -\input{expkv-def.tex} -\ProvidesPackage{expkv-def}% - [\ekvdDate\space v\ekvdVersion\space a key-defining frontend for expkv] -% \end{macrocode} -% \gobbledocstriptag -%</pkg> -%^^A=<< -%^^A the ConTeXt module >>= -% \subsection{The \ConTeXt\ module} -% \gobbledocstriptag -%<*ctx> -% \begin{macrocode} -\writestatus{loading}{ConTeXt User Module / expkv-def} -\usemodule[expkv] -\unprotect -\input expkv-def.tex -\writestatus{loading} - {ConTeXt User Module / expkv-def / Version \ekvdVersion\space loaded} -\protect\endinput -% \end{macrocode} -% \gobbledocstriptag -%</ctx> -%^^A=<< -%^^A main file >>= -% \subsection{The Generic Code} -% The rest of this implementation will be the generic code. -% \gobbledocstriptag -%<*tex> -% -% Load \expkv\ if the package didn't already do so -- since \expkv\ has -% safeguards against being loaded twice this does no harm and the overhead -% isn't that big. Also we reuse some of the internals of \expkv\ to save us from -% retyping them. -% \begin{macrocode} -\input expkv -% \end{macrocode} -% -% We make sure that \file{expkv-def.tex} is only input once: -% \begin{macrocode} -\expandafter\ifx\csname ekvdVersion\endcsname\relax -\else - \expandafter\endinput -\fi -% \end{macrocode} -% -% \begin{macro}{\ekvdVersion,\ekvdDate} -% We're on our first input, so lets store the version and date in a macro. -% \begin{macrocode} -\def\ekvdVersion{0.9} -\def\ekvdDate{2022-01-29} -% \end{macrocode} -% \end{macro} -% -% If the \LaTeX\ format is loaded we want to be a good file and report back who -% we are, for this the package will have defined |\ekvd@tmp| to use -% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. -% \begin{macrocode} -\csname ekvd@tmp\endcsname -% \end{macrocode} -% -% Store the category code of |@| to later be able to reset it and change it to -% 11 for now. -% \begin{macrocode} -\expandafter\chardef\csname ekvd@tmp\endcsname=\catcode`\@ -\catcode`\@=11 -% \end{macrocode} -% |\ekvd@tmp| will be reused later to handle expansion during the key -% defining. But we don't need it to ever store information long-term after -% \expkvd\ was initialized. -% -% \begin{macro}[internal]{\ekvd@ifprimitive} -% \begin{macrocode} -\protected\long\def\ekvd@ifprimitive#1% - {% - \begingroup - \edef\ekvd@tmpa{\string #1}% - \edef\ekvd@tmpb{\meaning#1}% - \expandafter - \endgroup - \ifx\ekvd@tmpa\ekvd@tmpb - \ekv@fi@firstoftwo - \fi - \@secondoftwo - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvd@long,\ekvd@prot,\ekvd@clear@prefixes,\ekvd@ifalso} -% \expkvd\ will use |\ekvd@long|, |\ekvd@prot|, and |\ekvd@ifalso| to store -% whether a key should be defined as |\long| or |\protected| or adds an action -% to an existing key, and we have to clear them for every new key. By default -% |long| and |protected| will just be empty, |ifalso| will be -% |\@secondoftwo|, and |ifnew| will just use its third argument. -% \begin{macrocode} -\protected\def\ekvd@clear@prefixes - {% - \let\ekvd@long\ekv@empty - \let\ekvd@prot\ekv@empty - \let\ekvd@ifalso\@secondoftwo - \long\def\ekvd@ifnew##1##2##3{##3}% - } -\ekvd@clear@prefixes -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvdefinekeys} -% This is the one front-facing macro which provides the interface to define -% keys. It's using |\ekvparse| to handle the \kv\ list, the interpretation will -% be done by |\ekvd@noarg| and |\ekvd@|. The \set\ for which the keys should be -% defined is stored in |\ekvd@set|. -% \begin{macrocode} -\protected\def\ekvdefinekeys#1% - {% - \def\ekvd@set{#1}% - \ekvparse\ekvd@noarg\ekvd@arg - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@noarg,\ekvd@arg,\ekvd@handle} -% |\ekvd@noarg| and |\ekvd@arg| store whether there was a value in the \kv\ -% pair. |\ekvd@handle| has to test whether there is a space inside the key and -% if so calls the prefix grabbing routine, else we throw an error and ignore the -% key. -% \begin{macrocode} -\protected\long\def\ekvd@noarg#1% - {% - \let\ekvd@ifnoarg\@firstoftwo - \expandafter\ekvd@handle\detokenize{#1}\ekvd@stop{}% - } -\protected\long\def\ekvd@arg#1% - {% - \let\ekvd@ifnoarg\@secondoftwo - \expandafter\ekvd@handle\detokenize{#1}\ekvd@stop - } -\protected\long\def\ekvd@handle#1\ekvd@stop#2% - {% - \ekvd@clear@prefixes - \def\ekvd@cur{#1}% - \ekvd@ifspace{#1}% - {\ekvd@prefix\ekv@mark#1\ekv@stop{#2}}% - \ekvd@err@missing@type - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@prefix,\ekvd@prefix@} -% \expkvd\ separates prefixes into two groups, the first being prefixes in the -% \TeX\ sense (|long| and |protected|) which use |@p@| in their name, the other -% being key-types (|code|, |int|, \emph{etc.}) which use |@t@| instead. -% |\ekvd@prefix| splits at the first space and checks whether its a |@p@| -% or |@t@| type prefix. If it is neither throw an error and gobble the -% definition (the value). -% \begin{macrocode} -\protected\def\ekvd@prefix#1 {\ekv@strip{#1}\ekvd@prefix@\ekv@mark} -\protected\def\ekvd@prefix@#1#2\ekv@stop - {% - \ekv@ifdefined{ekvd@t@#1}% - {\ekv@strip{#2}{\csname ekvd@t@#1\endcsname}}% - {% - \ekv@ifdefined{ekvd@p@#1}% - {\csname ekvd@p@#1\endcsname\ekvd@prefix@after@p{#2}}% - {\ekvd@err@undefined@prefix{#1}\@gobble}% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@prefix@after@p} -% The |@p@| type prefixes are all just modifying a following |@t@| type, so they -% will need to search for another prefix. This is true for all of them, so we -% use a macro to handle this. It'll throw an error if there is no other prefix. -% \begin{macrocode} -\protected\def\ekvd@prefix@after@p#1% - {% - \ekvd@ifspace{#1}% - {\ekvd@prefix#1\ekv@stop}% - {\ekvd@err@missing@type\@gobble}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvd@p@long,\ekvd@p@protected,\ekvd@p@protect,\ekvd@p@also,\ekvd@p@new} -% Define the |@p@| type prefixes, they all just store some information in a -% temporary macro. -% \begin{macrocode} -\protected\def\ekvd@p@long{\let\ekvd@long\long} -\protected\def\ekvd@p@protected{\let\ekvd@prot\protected} -\let\ekvd@p@protect\ekvd@p@protected -\protected\def\ekvd@p@also{\let\ekvd@ifalso\@firstoftwo} -\protected\def\ekvd@p@new{\let\ekvd@ifnew\ekvd@assert@new} -% \end{macrocode} -% \end{macro} -% -% \subsubsection{Key Types} -% -% \begin{macro}[internal]{\ekvd@type@set,\ekvd@t@set} -% The |set| type is quite straight forward, just define a |NoVal| key to call -% |\ekvchangeset|. -% \begin{macrocode} -\protected\def\ekvd@type@set#1#2% - {% - \ekvd@assert@not@long - \ekvd@assert@not@protected - \ekvd@ifnew{NoVal}{#1}% - {% - \ekv@ifempty{#2}% - {\ekvd@err@missing@definition}% - {% - \ekvd@ifalso - {% - \ekv@expargtwice{\ekvd@add@noval{#1}}% - {\ekvchangeset{#2}}% - \ekvd@assert@not@protected@also - }% - {\ekv@expargtwice{\ekvdefNoVal\ekvd@set{#1}}{\ekvchangeset{#2}}}% - }% - }% - } -\protected\def\ekvd@t@set#1#2% - {% - \ekvd@ifnoarg - {\ekvd@type@set{#1}{#1}}% - {\ekvd@type@set{#1}{#2}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@type@noval,\ekvd@t@noval,\ekvd@t@enoval} -% Another pretty simple type, |noval| just needs to assert that there is a -% definition and that |long| wasn't specified. -% There are types where the difference in the variants is so small, that we -% define a common handler for them, those common handlers are named with -% |@type@|. |noval| and |enoval| are so similar that we can use such a |@type@| -% macro, even if we could've done |noval| in a slightly faster way without it. -% \begin{macrocode} -\protected\long\def\ekvd@type@noval#1#2#3% - {% - \ekvd@ifnew{NoVal}{#2}% - {% - \ekvd@assert@arg - {% - \ekvd@assert@not@long - \ekvd@prot#1\ekvd@tmp{#3}% - \ekvd@ifalso - {\ekv@exparg{\ekvd@add@noval{#2}}\ekvd@tmp{}}% - {\ekvletNoVal\ekvd@set{#2}\ekvd@tmp}% - }% - }% - } -\protected\def\ekvd@t@noval{\ekvd@type@noval\def} -\protected\def\ekvd@t@enoval{\ekvd@type@noval\edef} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@type@code,\ekvd@t@code,\ekvd@t@ecode} -% |code| is simple as well, |ecode| has to use |\edef| on a temporary macro, -% since \expkv\ doesn't provide an |\ekvedef|. -% \begin{macrocode} -\protected\long\def\ekvd@type@code#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@arg - {% - \ekvd@prot\ekvd@long#1\ekvd@tmp##1{#3}% - \ekvd@ifalso - {\ekv@exparg{\ekvd@add@val{#2}}{\ekvd@tmp{##1}}{}}% - {\ekvlet\ekvd@set{#2}\ekvd@tmp}% - }% - }% - } -\protected\def\ekvd@t@code{\ekvd@type@code\def} -\protected\def\ekvd@t@ecode{\ekvd@type@code\edef} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@default,\ekvd@t@default,\ekvd@t@qdefault,\ekvd@t@odefault, -% \ekvd@t@fdefault -% } -% |\ekvd@type@default| asserts there was an argument, also the key for which one -% wants to set a default has to be already defined (this is not so important for -% |default|, but |qdefault| requires is). If everything is good, |\edef| a -% temporary macro that expands |\ekvd@set| and the |\csname| for the key, and in -% the case of |qdefault| does the first expansion step of the key-macro. -% \begin{macrocode} -\protected\long\def\ekvd@type@default#1#2#3#4% - {% - \ekvd@assert@arg - {% - \ekvifdefined\ekvd@set{#3}% - {% - \ekvd@assert@not@new - \ekvd@assert@not@long - \ekvd@prot\edef\ekvd@tmp - {% - \ekv@unexpanded\expandafter#1% - {#2\csname\ekv@name\ekvd@set{#3}\endcsname{#4}}% - }% - \ekvd@ifalso - {\ekv@exparg{\ekvd@add@noval{#3}}\ekvd@tmp{}}% - {\ekvletNoVal\ekvd@set{#3}\ekvd@tmp}% - }% - {\ekvd@err@undefined@key{#3}}% - }% - } -\protected\def\ekvd@t@default{\ekvd@type@default{}{}} -\protected\def\ekvd@t@qdefault{\ekvd@type@default{\expandafter\expandafter}{}} -\let\ekvd@t@odefault\ekvd@t@qdefault -\protected\def\ekvd@t@fdefault{\ekvd@type@default{}{\romannumeral`\^^@}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@t@edefault} -% |edefault| is too different from |default| and |qdefault| to reuse the -% |@type@| macro, as it doesn't need |\unexpanded| inside of |\edef|. -% \begin{macrocode} -\protected\long\def\ekvd@t@edefault#1#2% - {% - \ekvd@assert@arg - {% - \ekvifdefined\ekvd@set{#1}% - {% - \ekvd@assert@not@new - \ekvd@assert@not@long - \ekvd@prot\edef\ekvd@tmp - {\csname\ekv@name\ekvd@set{#1}\endcsname{#2}}% - \ekvd@ifalso - {\ekv@exparg{\ekvd@add@noval{#1}}\ekvd@tmp{}}% - {\ekvletNoVal\ekvd@set{#1}\ekvd@tmp}% - }% - {\ekvd@err@undefined@key{#1}}% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvd@t@initial,\ekvd@t@oinitial,\ekvd@t@finitial,\ekvd@t@einitial} -% \begin{macrocode} -\long\def\ekvd@type@initial#1#2#3#4% - {% - \ekvd@assert@not@new - \ekvd@assert@not@also - \ekvd@assert@not@long - \ekvd@assert@not@protected - \ekvd@ifnoarg - {% - \ekvifdefinedNoVal\ekvd@set{#3}% - {\csname\ekv@name\ekvd@set{#3}N\endcsname}% - {\ekvd@err@undefined@noval{#3}}% - }% - {% - \ekvifdefined\ekvd@set{#3}% - {% - #1{#2#4}% - \csname\ekv@name\ekvd@set{#3}\expandafter\endcsname\expandafter - {\ekvd@tmp}% - }% - {\ekvd@err@undefined@key{#3}}% - }% - } -\def\ekvd@t@initial{\ekvd@type@initial{\def\ekvd@tmp}{}} -\def\ekvd@t@oinitial{\ekvd@type@initial{\ekv@exparg{\def\ekvd@tmp}}{}} -\def\ekvd@t@einitial{\ekvd@type@initial{\edef\ekvd@tmp}{}} -\def\ekvd@t@finitial - {\ekvd@type@initial{\ekv@exparg{\def\ekvd@tmp}}{\romannumeral`\^^@}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@bool,\ekvd@t@bool,\ekvd@t@gbool,\ekvd@t@boolTF,\ekvd@t@gboolTF, -% \ekvd@t@invbool,\ekvd@t@ginvbool,\ekvd@t@invboolTF,\ekvd@t@ginvboolTF, -% } -% The boolean types are a quicker version of a |choice| that accept |true| and -% |false|, and set up the |NoVal| action to be identical to \texttt{\key=true}. -% The |true| and |false| actions are always just |\let|ting the macro in |#7| to -% some other macro (\emph{e.g.}, \cs[no-index]{iftrue}). -% \begin{macrocode} -\protected\def\ekvd@type@bool#1#2#3#4#5% - {% - \ekvd@ifnew{}{#4}% - {% - \ekvd@ifnew{NoVal}{#4}% - {% - \ekvd@assert@filledarg{#5}% - {% - \ekvd@newlet#5#3% - \ekvd@type@choice{#4}% - \protected\ekvdefNoVal\ekvd@set{#4}{#1\let#5#2}% - \protected\expandafter\def - \csname\ekvd@choice@name\ekvd@set{#4}{true}\endcsname - {#1\let#5#2}% - \protected\expandafter\def - \csname\ekvd@choice@name\ekvd@set{#4}{false}\endcsname - {#1\let#5#3}% - }% - }% - }% - } -\protected\def\ekvd@t@bool{\ekvd@type@bool{}\iftrue\iffalse} -\protected\def\ekvd@t@gbool{\ekvd@type@bool\global\iftrue\iffalse} -\protected\def\ekvd@t@boolTF{\ekvd@type@bool{}\@firstoftwo\@secondoftwo} -\protected\def\ekvd@t@gboolTF{\ekvd@type@bool\global\@firstoftwo\@secondoftwo} -\protected\def\ekvd@t@invbool{\ekvd@type@bool{}\iffalse\iftrue} -\protected\def\ekvd@t@ginvbool{\ekvd@type@bool\global\iffalse\iftrue} -\protected\def\ekvd@t@invboolTF{\ekvd@type@bool{}\@secondoftwo\@firstoftwo} -\protected\def\ekvd@t@ginvboolTF - {\ekvd@type@bool\global\@secondoftwo\@firstoftwo} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@boolpair, -% \ekvd@t@boolpair,\ekvd@t@gboolpair,\ekvd@t@boolpairTF,\ekvd@t@gboolpairTF -% } -% The boolean pair types are essentially the same as the boolean types, but set -% two macros instead of one. -% \begin{macrocode} -\protected\def\ekvd@type@boolpair#1#2#3#4#5#6% - {% - \ekvd@ifnew{}{#4}% - {% - \ekvd@ifnew{NoVal}{#4}% - {% - \ekvd@newlet#5#3% - \ekvd@newlet#6#2% - \ekvd@type@choice{#4}% - \protected\ekvdefNoVal\ekvd@set{#4}{#1\let#5#2#1\let#6#3}% - \protected\expandafter\def - \csname\ekvd@choice@name\ekvd@set{#4}{true}\endcsname - {#1\let#5#2#1\let#6#3}% - \protected\expandafter\def - \csname\ekvd@choice@name\ekvd@set{#4}{false}\endcsname - {#1\let#5#3#1\let#6#2}% - }% - }% - } -\protected\def\ekvd@t@boolpair#1#2% - {\ekvd@assert@twoargs{#2}{\ekvd@type@boolpair{}\iftrue\iffalse{#1}#2}} -\protected\def\ekvd@t@gboolpair#1#2% - {\ekvd@assert@twoargs{#2}{\ekvd@type@boolpair\global\iftrue\iffalse{#1}#2}} -\protected\def\ekvd@t@boolpairTF#1#2% - {% - \ekvd@assert@twoargs{#2}% - {\ekvd@type@boolpair{}\@firstoftwo\@secondoftwo{#1}#2}% - } -\protected\def\ekvd@t@gboolpairTF#1#2% - {% - \ekvd@assert@twoargs{#2}% - {\ekvd@type@boolpair\global\@firstoftwo\@secondoftwo{#1}#2}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@data,\ekvd@t@data,\ekvd@t@gdata,\ekvd@t@dataT,\ekvd@t@gdataT -% } -% \begin{macrocode} -\protected\def\ekvd@type@data#1#2#3#4#5#6% - {% - \ekvd@ifnew{}{#5}% - {% - \ekvd@assert@filledarg{#6}% - {% - \ekvd@newlet#6#1% - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#5}{\long#2#6####1#3{####1{#4}}}{}% - }% - {% - \protected\ekvd@long\ekvdef\ekvd@set{#5}% - {\long#2#6####1#3{####1{#4}}}% - }% - }% - }% - } -\protected\def\ekvd@t@data - {\ekvd@type@data\@secondoftwo\edef{####2}{\ekv@unexpanded{##1}}} -\protected\def\ekvd@t@edata{\ekvd@type@data\@secondoftwo\edef{####2}{##1}} -\protected\def\ekvd@t@gdata - {\ekvd@type@data\@secondoftwo\xdef{####2}{\ekv@unexpanded{##1}}} -\protected\def\ekvd@t@xdata{\ekvd@type@data\@secondoftwo\xdef{####2}{##1}} -\protected\def\ekvd@t@dataT - {\ekvd@type@data\@gobble\edef{}{\ekv@unexpanded{##1}}} -\protected\def\ekvd@t@edataT{\ekvd@type@data\@gobble\edef{}{##1}} -\protected\def\ekvd@t@gdataT - {\ekvd@type@data\@gobble\xdef{}{\ekv@unexpanded{##1}}} -\protected\def\ekvd@t@xdataT{\ekvd@type@data\@gobble\xdef{}{##1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@type@box,\ekvd@t@box,\ekvd@t@gbox} -% Set up our boxes. Though we're a generic package we want to be colour safe, so -% we put an additional grouping level inside the box contents, for the case that -% someone uses \pkg{color}. |\ekvd@newreg| is a small wrapper which tests -% whether the first argument is defined and if not does -% |\csname new#2\endcsname#1|. -% \begin{macrocode} -\protected\def\ekvd@type@box#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@filledarg{#3}% - {% - \ekvd@newreg#3{box}% - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#2}{#1\setbox#3=\hbox{\begingroup##1\endgroup}}{}% - }% - {% - \protected\ekvd@long\ekvdef\ekvd@set{#2}% - {#1\setbox#3=\hbox{\begingroup##1\endgroup}}% - }% - }% - }% - } -\protected\def\ekvd@t@box{\ekvd@type@box{}} -\protected\def\ekvd@t@gbox{\ekvd@type@box\global} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@type@toks,\ekvd@t@toks,\ekvd@t@gtoks} -% Similar to |box|, but set the |toks|. -% \begin{macrocode} -\protected\def\ekvd@type@toks#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@filledarg{#3}% - {% - \ekvd@newreg#3{toks}% - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#2}{#1#3={##1}}{}% - }% - {\protected\ekvd@long\ekvdef\ekvd@set{#2}{#1#3={##1}}}% - }% - }% - } -\protected\def\ekvd@t@toks{\ekvd@type@toks{}} -\protected\def\ekvd@t@gtoks{\ekvd@type@toks\global} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@type@preapptoks,\ekvd@t@apptoks,\ekvd@t@gapptoks} -% Just like |toks|, but expand the current contents of the |toks| register to -% append the new contents. -% \begin{macrocode} -\ekvd@ifprimitive\toksapp - {% - \protected\def\ekvd@type@preapptoks#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@filledarg{#3}% - {% - \ekvd@newreg#3{toks}% - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#2}{#1#3{##1}}{}% - }% - {\protected\ekvd@long\ekvdef\ekvd@set{#2}{#1#3{##1}}}% - }% - }% - } - \protected\def\ekvd@t@apptoks{\ekvd@type@preapptoks\toksapp} - \protected\def\ekvd@t@gapptoks{\ekvd@type@preapptoks\gtoksapp} - \protected\def\ekvd@t@pretoks{\ekvd@type@preapptoks\tokspre} - \protected\def\ekvd@t@gpretoks{\ekvd@type@preapptoks\gtokspre} - } - {% - \protected\def\ekvd@type@apptoks#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@filledarg{#3}% - {% - \ekvd@newreg#3{toks}% - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#2}{#1#3=\expandafter{\the#3##1}}{}% - }% - {% - \protected\ekvd@long\ekvdef\ekvd@set{#2}% - {#1#3=\expandafter{\the#3##1}}% - }% - }% - }% - } - \protected\def\ekvd@t@apptoks{\ekvd@type@apptoks{}} - \protected\def\ekvd@t@gapptoks{\ekvd@type@apptoks\global} - \newtoks\ekvd@toks - \protected\def\ekvd@type@pretoks#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@filledarg{#3}% - {% - \ekvd@newreg#3{toks}% - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#2}% - {% - \ekvd@toks={##1}% - #1#3=\expandafter{\the\expandafter\ekvd@toks\the#3}% - }% - {}% - }% - {% - \protected\ekvd@long\ekvdef\ekvd@set{#2}% - {% - \ekvd@toks={##1}% - #1#3=\expandafter{\the\expandafter\ekvd@toks\the#3}% - }% - }% - }% - }% - } - \protected\def\ekvd@t@pretoks{\ekvd@type@pretoks{}} - \protected\def\ekvd@t@gpretoks{\ekvd@type@pretoks\global} - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@reg,\ekvd@t@int,\ekvd@t@eint,\ekvd@t@gint,\ekvd@t@xint, -% \ekvd@t@dimen,\ekvd@t@edimen,\ekvd@t@gdimen,\ekvd@t@xdimen, -% \ekvd@t@skip,\ekvd@t@eskip,\ekvd@t@gskip,\ekvd@t@xskip -% } -% The |\ekvd@type@reg| can handle all the types for which the assignment will -% just be \texttt{\meta{register}=\meta{value}}. -% \begin{macrocode} -\protected\def\ekvd@type@reg#1#2#3#4#5#6% - {% - \ekvd@ifnew{}{#5}% - {% - \ekvd@assert@filledarg{#6}% - {% - \ekvd@newreg#6{#1}% - \ekvd@ifalso - {% - \let\evkd@prot\protected - \ekvd@add@val{#5}{#2#6=#3##1#4\relax}{}% - }% - {\protected\ekvd@long\ekvdef\ekvd@set{#5}{#2#6=#3##1#4\relax}}% - }% - }% - } -\protected\def\ekvd@t@int{\ekvd@type@reg{count}{}{}{}} -\protected\def\ekvd@t@eint{\ekvd@type@reg{count}{}\numexpr\relax} -\protected\def\ekvd@t@gint{\ekvd@type@reg{count}\global{}{}} -\protected\def\ekvd@t@xint{\ekvd@type@reg{count}\global\numexpr\relax} -\protected\def\ekvd@t@dimen{\ekvd@type@reg{dimen}{}{}{}} -\protected\def\ekvd@t@edimen{\ekvd@type@reg{dimen}{}\dimexpr\relax} -\protected\def\ekvd@t@gdimen{\ekvd@type@reg{dimen}\global{}{}} -\protected\def\ekvd@t@xdimen{\ekvd@type@reg{dimen}\global\dimexpr\relax} -\protected\def\ekvd@t@skip{\ekvd@type@reg{skip}{}{}{}} -\protected\def\ekvd@t@eskip{\ekvd@type@reg{skip}{}\glueexpr\relax} -\protected\def\ekvd@t@gskip{\ekvd@type@reg{skip}\global{}{}} -\protected\def\ekvd@t@xskip{\ekvd@type@reg{skip}\global\glueexpr\relax} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@type@store,\ekvd@t@store,\ekvd@t@gstore} -% The none-expanding |store| types use an |\edef| or |\xdef| and |\unexpanded| -% to be able to also store |#| easily. -% \begin{macrocode} -\protected\def\ekvd@type@store#1#2#3#4% - {% - \ekvd@ifnew{}{#3}% - {% - \ekvd@assert@filledarg{#4}% - {% - \ekvd@newlet#4\ekv@empty - \ekvd@ifalso - {% - \let\ekvd@prot\protected - \ekvd@add@val{#3}{#1#4{#2}}{}% - }% - {\protected\ekvd@long\ekvdef\ekvd@set{#3}{#1#4{#2}}}% - }% - }% - } -\protected\def\ekvd@t@store{\ekvd@type@store\edef{\ekv@unexpanded{##1}}} -\protected\def\ekvd@t@gstore{\ekvd@type@store\xdef{\ekv@unexpanded{##1}}} -\protected\def\ekvd@t@estore{\ekvd@type@store\edef{##1}} -\protected\def\ekvd@t@xstore{\ekvd@type@store\xdef{##1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@meta,\ekvd@type@meta@a,\ekvd@type@meta@b,\ekvd@type@meta@c, -% \ekvd@t@meta,\ekvd@t@nmeta -% } -% |meta| sets up things such that another instance of |\ekvset| will be run on -% the argument, with the same \set. -% \begin{macrocode} -\protected\long\def\ekvd@type@meta#1#2#3#4#5#6#7% - {% - \ekvd@ifnew{#1}{#6}% - {% - \ekvd@assert@filledarg{#7}% - {% - \edef\ekvd@tmp{\ekvd@set}% - \expandafter\ekvd@type@meta@a\expandafter{\ekvd@tmp}{#7}{#2}% - \ekvd@ifalso - {\ekv@exparg{#3{#6}}{\ekvd@tmp#4}{#5}}% - {\csname ekvlet#1\endcsname\ekvd@set{#6}\ekvd@tmp}% - }% - }% - } -\protected\long\def\ekvd@type@meta@a#1#2% - {% - \expandafter\ekvd@type@meta@b\expandafter{\ekvset{#1}{#2}}% - } -\protected\def\ekvd@type@meta@b - {% - \expandafter\ekvd@type@meta@c\expandafter - } -\protected\long\def\ekvd@type@meta@c#1#2% - {% - \ekvd@prot\ekvd@long\def\ekvd@tmp#2{#1}% - } -\protected\def\ekvd@t@meta{\ekvd@type@meta{}{##1}\ekvd@add@val{{##1}}{}} -\protected\def\ekvd@t@nmeta - {% - \ekvd@assert@not@long - \ekvd@type@meta{NoVal}{}\ekvd@add@noval{}\ekvd@assert@not@long@also - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvd@type@smeta,\ekvd@type@smeta@,\ekvd@t@smeta,\ekvd@t@snmeta} -% |smeta| is pretty similar to |meta|, but needs two arguments inside of \val, -% such that the first is the \set\ for which the sub-|\ekvset| and the second is -% the \kv\ list. -% \begin{macrocode} -\protected\long\def\ekvd@type@smeta#1#2#3#4#5#6#7% - {% - \ekvd@ifnew{#1}{#6}% - {% - \ekvd@assert@twoargs{#7}% - {% - \ekvd@type@meta@a#7{#2}% - \ekvd@ifalso - {\ekv@exparg{#3{#6}}{\ekvd@tmp#4}{#5}}% - {\csname ekvlet#1\endcsname\ekvd@set{#6}\ekvd@tmp}% - }% - }% - } -\protected\def\ekvd@t@smeta{\ekvd@type@smeta{}{##1}\ekvd@add@val{{##1}}{}} -\protected\def\ekvd@t@snmeta - {% - \ekvd@assert@not@long - \ekvd@type@smeta{NoVal}{}\ekvd@add@noval{}\ekvd@assert@not@long@also - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@choice, -% \ekvd@populate@choice,\ekvd@populate@choice@,\ekvd@populate@choice@noarg, -% \ekvd@choice@prefix,\ekvd@choice@prefix@,\ekvd@choice@prefix@done, -% \ekvd@choice@p@protected,\ekvd@choice@p@protect, -% \ekvd@choice@p@long,\ekvd@choice@p@long@, -% \ekvd@t@choice -% } -% The |choice| type is by far the most complex type, as we have to run a -% sub-parser on the choice-definition list, which should support the |@p@| type -% prefixes as well (but |long| will always throw an error, as they are not -% allowed to be long). |\ekvd@type@choice| will just define the choice-key, the -% handling of the choices definition will be done by |\ekvd@populate@choice|. -% \begin{macrocode} -\protected\def\ekvd@type@choice#1% - {% - \ekvd@assert@not@long - \ekv@expargtwice{\ekvd@prot\def\ekvd@tmp##1}% - {% - \expandafter\expandafter\expandafter - \ekvd@h@choice - \expandafter\expandafter\expandafter - {\expandafter\ekvd@choice@name\expandafter{\ekvd@set}{#1}{##1}}% - }% - \ekvd@ifalso - {% - \ekvd@assert@val{#1}% - {% - \ekvd@if@not@already@choice{#1}% - {% - \ekv@exparg - {% - \expandafter\ekvd@add@aux - \csname\ekv@name\ekvd@set{#1}\endcsname{{##1}}{#1}% - }% - {\ekvd@tmp{##1}}% - {\ekvd@long\ekvdef}\ekvd@assert@not@long@also - }% - }% - }% - {\ekvlet\ekvd@set{#1}\ekvd@tmp}% - } -% \end{macrocode} -% |\ekvd@populate@choice| just uses |\ekvparse| and then gives control to -% |\ekvd@populate@choice@noarg|, which throws an error, and -% |\ekvd@populate@choice@|. -% \begin{macrocode} -\protected\def\ekvd@populate@choice - {% - \ekvparse\ekvd@populate@choice@noarg\ekvd@populate@choice@ - } -\protected\long\def\ekvd@populate@choice@noarg#1% - {% - \expandafter\ekvd@err@missing@definition@msg\expandafter{\ekvd@cur : #1}% - } -% \end{macrocode} -% |\ekvd@populate@choice@| runs the prefix-test, if there is none we can -% directly define the choice, for that |\ekvd@set@choice| will expand to the -% current choice-key's name, which will have been defined by |\ekvd@t@choice|. -% If there is a prefix run the prefix grabbing routine, which was altered for -% |@type@choice|. -% \begin{macrocode} -\protected\long\def\ekvd@populate@choice@#1#2% - {% - \ekvd@clear@prefixes - \ekvd@ifspace{#1}% - {\ekvd@choice@prefix{\ekv@mark#1}\ekv@mark#1\ekv@stop}% - {% - \expandafter\edef - \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#1}\endcsname - }% - {\unexpanded{#2}}% - } -\protected\def\ekvd@choice@prefix#1#2 - {% - \ekv@strip{#2}{\ekvd@choice@prefix@{#1}}\ekv@mark - } -\protected\def\ekvd@choice@prefix@#1#2#3\ekv@stop - {% - \ekv@ifdefined{ekvd@choice@p@#2}% - {% - \csname ekvd@choice@p@#2\endcsname - \ekvd@ifspace{#3}% - {\ekvd@choice@prefix{#3}#3\ekv@stop}% - {\ekvd@choice@prefix@done{#3}}% - }% - {\ekvd@choice@prefix@done{#1}}% - } -\protected\def\ekvd@choice@prefix@done#1% - {% - \ekvd@prot\expandafter\edef - \csname - \ekv@strip{#1}{\ekvd@choice@name\ekvd@set\ekvd@set@choice}% - \endcsname - } -\protected\def\ekvd@choice@p@protected{\let\ekvd@prot\protected} -\let\ekvd@choice@p@protect\ekvd@choice@p@protected -\protected\def\ekvd@choice@invalid@p#1\ekvd@ifspace#2% - {% - \expandafter\ekvd@choice@invalid@p@\expandafter{\ekv@gobble@mark#2}{#1}% - \ekvd@ifspace{#2}% - } -\protected\def\ekvd@choice@invalid@p@#1#2% - {% - \expandafter\ekvd@err@no@prefix@msg\expandafter{\ekvd@cur : #2 #1}{#2}% - } -\protected\def\ekvd@choice@p@long{\ekvd@choice@invalid@p{long}}% -\protected\def\ekvd@choice@p@also{\ekvd@choice@invalid@p{also}}% -\protected\def\ekvd@choice@p@new{\ekvd@choice@invalid@p{new}}% -% \end{macrocode} -% Finally we're able to set up the |@t@choice| macro, which has to store the -% current choice-key's name, define the key, and parse the available choices. -% \begin{macrocode} -\protected\long\def\ekvd@t@choice#1#2% - {% - \ekvd@ifnew{}{#1}% - {% - \ekvd@assert@arg - {% - \ekvd@type@choice{#1}% - \def\ekvd@set@choice{#1}% - \ekvd@populate@choice{#2}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@t@choice-store,\ekvd@t@choice-enum} -% These two types define a special kind of |choice| key and are quite similar, -% the only difference is what the different choices do (hence they use a -% shared initialisation which differs in the chosen |populate| step). -% \begin{macrocode} -\protected\long\expandafter\def\csname ekvd@t@choice-store\endcsname - {\ekvd@type@choicespecial\ekvd@populate@choicestore} -\protected\long\expandafter\def\csname ekvd@t@choice-enum\endcsname - {\ekvd@type@choicespecial\ekvd@populate@choiceenum} -% \end{macrocode} -% \begin{macro}[internal]{\ekvd@type@choicespecial} -% Initialise similar to a |choice| key. The difference is that we require two -% arguments (which we assert), a macro to store things in, and a |csv|-list -% containing the allowed values. |#1| is the |populate| macro according to the -% type used. -% \begin{macrocode} -\protected\long\def\ekvd@type@choicespecial#1#2#3% - {% - \ekvd@ifnew{}{#2}% - {% - \ekvd@assert@twoargs{#3}% - {% - \ekvd@type@choice{#2}% - \def\ekvd@set@choice{#2}% - #1#3% - }% - }% - } -% \end{macrocode} -% \end{macro} -% \begin{macro}[internal] -% {\ekvd@populate@choicestore,\ekvd@populate@choicestore@} -% We initialise the storing macro if it doesn't yet exist, and then we loop -% over the value list. The |\edef|s with |\unexpanded| are both necessary to -% be able to store macro parameter tokens (the outer protects at define time, -% the inner at use time). -% \begin{macrocode} -\protected\long\def\ekvd@populate@choicestore#1% - {% - \ekvd@newlet#1\ekv@empty - \ekvcsvloop{\ekvd@populate@choicestore@#1}% - } -\protected\long\def\ekvd@populate@choicestore@#1#2% - {% - \protected\expandafter\edef - \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname - {\unexpanded{\edef#1{\unexpanded{#2}}}}% - } -% \end{macrocode} -% \end{macro} -% \begin{macro}[internal] -% {\ekvd@populate@choiceenum,\ekvd@populate@choiceenum@} -% This is similar to the population of a |choice-store| type, but instead of -% storing the values in a macro this initialises a count and stores the -% position of the value in the list inside that count (zero-indexed). The -% space is necessary to terminate the number scanning, which is the reason we -% use |\@firstofone| (so that the space after the macro name isn't gobbled by -% \TeX). -% \begin{macrocode} -\protected\long\def\ekvd@populate@choiceenum#1% - {% - \ekvd@newreg#1{count}% - \def\ekvd@tmp{0}% - \ekvcsvloop{\ekvd@populate@choiceenum@#1}% - } -\protected\long\def\ekvd@populate@choiceenum@#1#2% - {% - \protected\expandafter\edef - \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname - {#1=\@firstofone{\ekvd@tmp} }% - \edef\ekvd@tmp{\the\numexpr\ekvd@tmp+1\relax}% - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@t@unknown-choice} -% \begin{macrocode} -\protected\long\expandafter\def\csname ekvd@t@unknown-choice\endcsname#1#2% - {% - \ekvd@assert@new@for@name{\ekvd@unknown@choice@name\ekvd@set{#1}}% - {% - \ekvd@assert@arg - {% - \ekvd@assert@not@long - \ekvd@assert@not@also - \ekvd@prot\expandafter - \def\csname\ekvd@unknown@choice@name\ekvd@set{#1}\endcsname##1{#2}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvd@t@unknown,\ekvd@type@unknown@code,\ekvd@type@unknown@noval} -% The |unknown| type has different subtypes which would be the key names for -% other types. It is first checked whether that subtype is defined, if it -% isn't throw an error, else use that subtype. -% \begin{macrocode} -\protected\long\def\ekvd@t@unknown#1#2% - {% - \ekv@ifdefined{ekvd@type@unknown@\detokenize{#1}}% - {\csname ekvd@type@unknown@\detokenize{#1}\endcsname{#2}}% - \ekvd@err@misused@unknown - } -% \end{macrocode} -% The |unknown noval| type can use |\ekvdefunknownNoVal| directly (after -% asserting some prefixes). -% \begin{macrocode} -\protected\long\def\ekvd@type@unknown@noval#1% - {% - \ekvd@assert@new@for@name{\ekv@name\ekvd@set{}uN}% - {% - \ekvd@assert@arg - {% - \ekvd@assert@not@also - \ekvd@assert@not@long - \ekvd@prot\ekvdefunknownNoVal\ekvd@set{#1}% - }% - }% - } -% \end{macrocode} -% The |unknown code| type uses some trickery during the definition in order to -% swap out |#1| and |#2| in the user supplied definition. This is done via a -% temporary macro that stores the definition but gets the parameter numbers -% reversed while the real definition is done. -% \begin{macrocode} -\protected\long\def\ekvd@type@unknown@code#1% - {% - \ekvd@assert@new@for@name{\ekv@name\ekvd@set{}u}% - {% - \ekvd@assert@arg - {% - \ekvd@assert@not@also - \begingroup - \def\ekvd@tmp##1##2{#1}% - \ekv@exparg - {% - \endgroup - \ekvd@prot\ekvd@long\ekvdefunknown\ekvd@set - }% - {\ekvd@tmp{##2}{##1}}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@type@unknown@redirect, -% \ekvd@type@unknown@redirect-code, -% \ekvd@type@unknown@redirect-noval -% } -% The |unknown redirect| types also just forward to |\ekvredirectunknown| -% after asserting some prefixes. -% \begin{macrocode} -\protected\edef\ekvd@type@unknown@redirect#1% - {% - \expandafter\noexpand\csname ekvd@type@unknown@redirect-code\endcsname{#1}% - \expandafter\noexpand\csname ekvd@type@unknown@redirect-noval\endcsname{#1}% - } -\protected\expandafter\def\csname ekvd@type@unknown@redirect-code\endcsname#1% - {% - \ekvd@assert@new@for@name{\ekv@name\ekvd@set{}u}% - {% - \ekvd@assert@arg - {% - \ekvd@assert@not@also - \ekvd@assert@not@protected - \expandafter\ekvredirectunknown\expandafter{\ekvd@set}{#1}% - }% - }% - } -\protected\expandafter\def\csname ekvd@type@unknown@redirect-noval\endcsname#1% - {% - \ekvd@assert@new@for@name{\ekv@name\ekvd@set{}uN}% - {% - \ekvd@assert@arg - {% - \ekvd@assert@not@also - \ekvd@assert@not@protected - \ekvd@assert@not@long - \expandafter\ekvredirectunknownNoVal\expandafter{\ekvd@set}{#1}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% \subsubsection{Key Type Helpers} -% There are some keys that might need helpers during their execution (not during -% their definition, which are gathered as |@type@| macros). These helpers are -% named |@h@|. -% -% \begin{macro}[internal]{\ekvd@h@choice,\ekvd@h@choice@} -% The |choice| helper will just test whether the given choice was defined, if -% not throw an error expandably, else call the macro which stores the code for -% this choice. -% \begin{macrocode} -\def\ekvd@h@choice#1% - {% - \expandafter\ekvd@h@choice@ - \csname\ifcsname#1\endcsname#1\else relax\fi\endcsname - {#1}% - } -\def\ekvd@h@choice@#1#2% - {% - \ifx#1\relax - \ekvd@err@choice@invalid{#2}% - \expandafter\@gobble - \fi - #1% - } -% \end{macrocode} -% \end{macro} -% -% -% \subsubsection{Handling \texttt{also}} -% -% \begin{macro}[internal] -% {\ekvd@add@val,\ekvd@add@noval,\ekvd@add@aux,\ekvd@add@aux@} -% \begin{macrocode} -\protected\long\def\ekvd@add@val#1#2#3% - {% - \ekvd@assert@val{#1}% - {% - \expandafter\ekvd@add@aux\csname\ekv@name\ekvd@set{#1}\endcsname{{##1}}% - {#1}{#2}{\ekvd@long\ekvdef}{#3}% - }% - } -\protected\long\def\ekvd@add@noval#1#2#3% - {% - \ekvd@assert@noval{#1}% - {% - \expandafter\ekvd@add@aux\csname\ekv@name\ekvd@set{#1}N\endcsname{}% - {#1}{#2}\ekvdefNoVal{#3}% - }% - } -\protected\long\def\ekvd@add@aux#1#2% - {% - \ekvd@extract@prefixes#1% - \expandafter\ekvd@add@aux@\expandafter{#1#2}% - } -\protected\long\def\ekvd@add@aux@#1#2#3#4#5% - {% - #5% - \ekvd@prot#4\ekvd@set{#2}{#1#3}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@extract@prefixes,\ekvd@extract@prefixes@, -% \ekvd@extract@prefixes@long,\ekvd@extract@prefixes@prot -% } -% This macro checks which prefixes were used for the definition of a macro and -% sets |\ekvd@long| and |\ekvd@prot| accordingly. -% \begin{macrocode} -\protected\def\ekvd@extract@prefixes#1% - {% - \expandafter\ekvd@extract@prefixes@\meaning#1\ekvd@stop - } -% \end{macrocode} -% In the following definition |#1| will get replaced by |macro:|, |#2| by -% |\long| and |#3| by |\protected| (in each, all tokens will have category -% other). This allows us to parse the |\meaning| of a macro for those strings. -% \begin{macrocode} -\protected\def\ekvd@extract@prefixes@#1#2#3% - {% - \protected\def\ekvd@extract@prefixes@##1#1##2\ekvd@stop - {% - \ekvd@extract@prefixes@long - ##1\ekvd@mark\@firstofone#2\ekvd@mark\@gobble\ekvd@stop - {\let\ekvd@long\long}% - \ekvd@extract@prefixes@prot - ##1\ekvd@mark\@firstofone#3\ekvd@mark\@gobble\ekvd@stop - {\let\ekvd@prot\protected}% - }% - \protected\def\ekvd@extract@prefixes@long##1#2##2\ekvd@mark##3##4\ekvd@stop - {##3}% - \protected\def\ekvd@extract@prefixes@prot##1#3##2\ekvd@mark##3##4\ekvd@stop - {##3}% - } -% \end{macrocode} -% We use a temporary macro to expand the three arguments of -% |\ekvd@extract@prefixes@|, which will set up the real meaning of itself and -% the parsing for |\long| and |\protected|. -% \begin{macrocode} -\begingroup -\edef\ekvd@tmp - {% - \endgroup - \ekvd@extract@prefixes@ - {\detokenize{macro:}}% - {\string\long}% - {\string\protected}% - } -\ekvd@tmp -% \end{macrocode} -% \end{macro} -% -% -% \subsubsection{Tests} -% -% \begin{macro}[internal]{\ekvd@newlet,\ekvd@newreg} -% These macros test whether a control sequence is defined, if it isn't they -% define it, either via |\let| or via the correct \cs[no-index]{new\meta{reg}}. -% \begin{macrocode} -\protected\def\ekvd@newlet#1#2% - {% - \ifdefined#1\ekv@fi@gobble\fi\@firstofone{\let#1#2}% - } -\protected\def\ekvd@newreg#1#2% - {% - \ifdefined#1\ekv@fi@gobble\fi\@firstofone{\csname new#2\endcsname#1}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekvd@assert@twoargs,\ekvd@ifnottwoargs,\ekvd@ifempty@gtwo} -% A test for exactly two tokens can be reduced for an empty-test after gobbling -% two tokens, in the case that there are fewer tokens than two in the argument, -% only macros will be gobbled that are needed for the true branch, which doesn't -% hurt, and if there are more this will not be empty. -% \begin{macrocode} -\long\def\ekvd@assert@twoargs#1% - {% - \ekvd@ifnottwoargs{#1}{\ekvd@err@missing@definition}% - } -\long\def\ekvd@ifnottwoargs#1% - {% - \ekvd@ifempty@gtwo#1\ekv@ifempty@B - \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo - } -\long\def\ekvd@ifempty@gtwo#1#2{\ekv@ifempty@\ekv@ifempty@A} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@assert@val,\ekvd@assert@val@,\ekvd@assert@noval,\ekvd@assert@noval@, -% \ekvd@extract@args,\ekvd@extracted@args,\ekvd@one@arg@string -% } -% Assert that a given key is defined as a value taking key or a |NoVal| key with -% the correct argument structure, respectively. -% \begin{macrocode} -\protected\def\ekvd@assert@val#1% - {% - \ekvifdefined\ekvd@set{#1}% - {\expandafter\ekvd@assert@val@\csname\ekv@name\ekvd@set{#1}\endcsname}% - {% - \ekvifdefinedNoVal\ekvd@set{#1}% - \ekvd@err@add@val@on@noval - {\ekvd@err@undefined@key{#1}}% - \@gobble - }% - } -\protected\def\ekvd@assert@val@#1% - {% - \expandafter\ekvd@extract@args\meaning#1\ekvd@stop - \unless\ifx\ekvd@extracted@args\ekvd@one@arg@string - \ekvd@err@unsupported@arg - \fi - \@firstofone - }% -\protected\def\ekvd@assert@noval#1% - {% - \ekvifdefinedNoVal\ekvd@set{#1}% - {\expandafter\ekvd@assert@noval@\csname\ekv@name\ekvd@set{#1}N\endcsname}% - {% - \ekvifdefined\ekvd@set{#1}% - \ekvd@err@add@noval@on@val - {\ekvd@err@undefined@key{#1}}% - \@gobble - }% - } -\protected\def\ekvd@assert@noval@#1% - {% - \expandafter\ekvd@extract@args\meaning#1\ekvd@stop - \unless\ifx\ekvd@extracted@args\ekv@empty - \ekvd@err@unsupported@arg - \fi - \@firstofone - } -\protected\def\ekvd@extract@args#1% - {% - \protected\def\ekvd@extract@args##1#1##2->##3\ekvd@stop - {\def\ekvd@extracted@args{##2}}% - } -\expandafter\ekvd@extract@args\expandafter{\detokenize{macro:}} -\edef\ekvd@one@arg@string{\string#1} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@assert@arg,\ekvd@assert@arg@msg,\ekvd@ifnoarg} -% There is no need to actually define |\ekvd@ifnoarg| here, as it will be set by -% either |\ekvd@arg| or |\ekvd@noarg|. -% \begin{macrocode} -\def\ekvd@assert@arg{\ekvd@ifnoarg\ekvd@err@missing@definition} -\long\def\ekvd@assert@arg@msg#1% - {% - \ekvd@ifnoarg{\ekvd@err@missing@definition@msg{#1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@assert@filledarg,\ekvd@ifnoarg@or@empty} -% \begin{macrocode} -\long\def\ekvd@assert@filledarg#1% - {% - \ekvd@ifnoarg@or@empty{#1}\ekvd@err@missing@definition - } -\long\def\ekvd@ifnoarg@or@empty#1% - {% - \ekvd@ifnoarg - \@firstoftwo - {\ekv@ifempty{#1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@assert@not@long,\ekvd@assert@not@protected,\ekvd@assert@not@also -% \ekvd@assert@not@long@also,\ekvd@assert@not@protected@also, -% \ekvd@assert@new,\ekvd@assert@not@new -% } -% Some key-types don't want to be |also|, |\long| or |\protected|, so we provide -% macros to test this and throw an error, this could be silently ignored but now -% users will learn to not use unnecessary stuff which slows the compilation -% down. -% \begin{macrocode} -\def\ekvd@assert@not@long{\ifx\ekvd@long\long\ekvd@err@no@prefix{long}\fi} -\def\ekvd@assert@not@protected - {\ifx\ekvd@prot\protected\ekvd@err@no@prefix{protected}\fi} -\def\ekvd@assert@not@also{\ekvd@ifalso{\ekvd@err@no@prefix{also}}{}} -\def\ekvd@assert@not@long@also - {\ifx\ekvd@long\long\ekvd@err@no@prefix@also{long}\fi} -\def\ekvd@assert@not@protected@also - {\ifx\ekvd@prot\protected\ekvd@err@no@prefix@also{protected}\fi} -\def\ekvd@assert@new#1#2% - {\csname ekvifdefined#1\endcsname\ekvd@set{#2}{\ekvd@err@not@new}} -\def\ekvd@assert@not@new - {\ifx\ekvd@ifnew\ekvd@assert@new\ekvd@err@no@prefix{new}\fi} -\def\ekvd@assert@new@for@name#1% - {% - \ifx\ekvd@ifnew\ekvd@assert@new - \ekv@fi@firstoftwo - \fi - \@secondoftwo - {\ekv@ifdefined{#1}\ekvd@err@not@new}% - \@firstofone - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@if@not@already@choice, \ekvd@if@not@already@choice@a, -% \ekvd@if@not@already@choice@b -% } -% It is bad to use |also| on a key that already contains a |choice|, as both -% choices would share the same valid values and thus lead to each callback being -% used twice. The following is a rudimentary test against this. -% \begin{macrocode} -\protected\def\ekvd@if@not@already@choice#1% - {% - \expandafter\ekvd@if@not@already@choice@a - \csname\ekv@name\ekvd@set{#1}\endcsname - {}\ekvd@h@choice\ekvd@stop - } -\protected\def\ekvd@if@not@already@choice@a - {% - \expandafter\ekvd@if@not@already@choice@b - } -\long\protected\def\ekvd@if@not@already@choice@b#1\ekvd@h@choice#2\ekvd@stop - {% - \ekv@ifempty{#2}\@firstofone\@gobble - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@ifspace,\ekvd@ifspace@} -% Yet another test which can be reduced to an if-empty, this time by gobbling -% everything up to the first space. -% \begin{macrocode} -\long\def\ekvd@ifspace#1% - {% - \ekvd@ifspace@#1 \ekv@ifempty@B - \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo - } -\long\def\ekvd@ifspace@#1 % keep this space - {% - \ekv@ifempty@\ekv@ifempty@A - } -% \end{macrocode} -% \end{macro} -% -% \subsubsection{Messages} -% -% Most messages of \expkvd\ are not expandable, since they only appear during -% key-definition, which is not expandable anyway. -% -% \begin{macro}[internal] -% { -% \ekvd@errm,\ekvd@err@missing@definition,\ekvd@err@missing@definition@msg, -% \ekvd@err@missing@type,\ekvd@err@undefined@prefix,\ekvd@err@undefined@key, -% \ekvd@err@no@prefix,\ekvd@err@no@prefix@msg,\ekvd@err@no@prefix@also, -% \ekvd@err@add@val@on@noval,\ekvd@err@add@noval@on@val, -% \ekvd@err@unsupported@arg,\ekvd@err@not@new -% } -% The non-expandable error messages are boring, so here they are: -% \begin{macrocode} -\protected\def\ekvd@errm#1{\errmessage{expkv-def Error: #1}} -\protected\def\ekvd@err@missing@definition - {\ekvd@errm{Missing definition for key `\ekvd@cur'}} -\protected\def\ekvd@err@missing@definition@msg#1% - {\ekvd@errm{Missing definition for key `\ekv@unexpanded{#1}'}} -\protected\def\ekvd@err@missing@type - {\ekvd@errm{Missing type prefix for key `\ekvd@cur'}} -\protected\def\ekvd@err@undefined@prefix#1% - {% - \ekvd@errm - {% - Undefined prefix `\ekv@unexpanded{#1}' found while processing - `\ekvd@cur'% - }% - } -\protected\def\ekvd@err@undefined@key#1% - {% - \ekvd@errm - {Undefined key `\ekv@unexpanded{#1}' found while processing `\ekvd@cur'}% - } -\protected\def\ekvd@err@undefined@noval#1% - {% - \ekvd@errm - {% - Undefined noval key `\unexpanded{#1}' found while processing - `\ekvd@cur'% - }% - } -\protected\def\ekvd@err@no@prefix#1% - {\ekvd@errm{prefix `#1' not accepted in `\ekvd@cur'}} -\protected\def\ekvd@err@no@prefix@msg#1#2% - {\ekvd@errm{prefix `#2' not accepted in `\ekv@unexpanded{#1}'}} -\protected\def\ekvd@err@no@prefix@also#1% - {\ekvd@errm{`\ekvd@cur' not allowed with a `#1' key}} -\protected\def\ekvd@err@add@val@on@noval - {\ekvd@errm{`\ekvd@cur' not allowed with a NoVal key}} -\protected\def\ekvd@err@add@noval@on@val - {\ekvd@errm{`\ekvd@cur' not allowed with a value taking key}} -\protected\def\ekvd@err@unsupported@arg\fi\@firstofone#1% - {% - \fi - \ekvd@errm - {% - Existing key-macro has the unsupported argument string - `\ekvd@extracted@args' for key `\ekvd@cur'% - }% - } -\protected\def\ekvd@err@not@new - {\ekvd@errm{The key for `\ekvd@cur' is already defined}} -\protected\long\def\ekvd@err@misused@unknown - {\ekvd@errm{Misuse of the unknown type found while processing `\ekvd@cur'}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvd@err@choice@invalid,\ekvd@err@choice@invalid@,\ekvd@choice@name, -% \ekvd@unknown@choice@name -% } -% |\ekvd@err@choice@invalid| will have to use this mechanism to throw its -% message. Also we have to retrieve the name parts of the choice in an easy way, -% so we use parentheses of catcode 8 here, which should suffice in most cases to -% allow for a correct separation. -% \begin{macrocode} -\def\ekvd@err@choice@invalid#1% - {% - \ekvd@err@choice@invalid@#1% - } -\begingroup -\catcode40=8 -\catcode41=8 -\@firstofone{\endgroup -\def\ekvd@choice@name#1#2#3% - {% - ekvd#1(#2)\detokenize{#3}% - } -\def\ekvd@unknown@choice@name#1#2% - {% - ekvd:u:#1(#2)% - } -\def\ekvd@err@choice@invalid@ ekvd#1(#2)\detokenize#3% - {% - \ekv@ifdefined{\ekvd@unknown@choice@name{#1}{#2}}% - {\csname\ekvd@unknown@choice@name{#1}{#2}\endcsname{#3}}% - {\ekvd@err{invalid choice `#3' for `#2' in set `#1'}}% - } -} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvd@err} -% The expandable error messages use |\ekvd@err|, which is just like |\ekv@err| -% from \expkv. It uses a runaway argument to start the error message. -% \begin{macrocode} -\ekv@exparg{\long\def\ekvd@err#1}{\ekverr{expkv-def}{#1}} -% \end{macrocode} -% \end{macro} -% -% Now everything that's left is to reset the category code of |@|. -% \begin{macrocode} -\catcode`\@=\ekvd@tmp -% \end{macrocode} -% -% \gobbledocstriptag -%</tex> -%^^A=<< -% -%^^A=<< -% -% \end{implementation}^^A=<< -% -% \clearpage -% \PrintIndex -% -\endinput -% -^^A vim: ft=tex fdm=marker fmr=>>=,=<< diff --git a/macros/generic/expkv-def/expkv-def.pdf b/macros/generic/expkv-def/expkv-def.pdf Binary files differdeleted file mode 100644 index ea5678d280..0000000000 --- a/macros/generic/expkv-def/expkv-def.pdf +++ /dev/null diff --git a/macros/generic/expkv-opt/README.md b/macros/generic/expkv-opt/README.md deleted file mode 100644 index 447c4a87da..0000000000 --- a/macros/generic/expkv-opt/README.md +++ /dev/null @@ -1,29 +0,0 @@ -------------------------------------------------------------------------------- -# expkv-opt -- parse class and package options with expkv - -Version 2021-04-04 v0.2 - -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt - -Hosted at https://github.com/Skillmon/tex_expkv-opt - -------------------------------------------------------------------------------- - -Copyright (C) 2020 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. - -------------------------------------------------------------------------------- - -This package allows to parse key=val options for LaTeX classes and packages -using [`expkv`](https://github.com/Skillmon/tex_expkv). It handles global and -local options completely separately. The only required package is `expkv`. diff --git a/macros/generic/expkv-opt/expkv-opt.dtx b/macros/generic/expkv-opt/expkv-opt.dtx deleted file mode 100644 index 22a9a9b0c3..0000000000 --- a/macros/generic/expkv-opt/expkv-opt.dtx +++ /dev/null @@ -1,1012 +0,0 @@ -% \iffalse meta-comment -% -% File: expkv-opt.dtx Copyright (C) 2020 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 -% -% ------------------------------------------------------------------------------ -% -%<*driver>^^A>>= -\def\nameofplainTeX{plain} -\ifx\fmtname\nameofplainTeX\else - \expandafter\begingroup -\fi -\input l3docstrip.tex -\askforoverwritefalse -\preamble - --------------------------------------------------------------- -expkv-opt -- parse class and package options with expkv -E-mail: jspratte@yahoo.de -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt --------------------------------------------------------------- - -Copyright (C) 2020 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. - -This work consists of the file expkv-opt.dtx -and the derived files expkv-opt.pdf - expkv-opt.sty - -\endpreamble -% stop docstrip adding \endinput -\postamble -\endpostamble -\generate{\file{expkv-opt.sty}{\from{expkv-opt.dtx}{pkg}}} -\ifx\fmtname\nameofplainTeX - \expandafter\endbatchfile -\else - \expandafter\endgroup -\fi -% -\IfFileExists{expkv-opt.sty}{\RequirePackage{expkv-opt}}{} -\ProvidesFile{expkv-opt.dtx} - [\csname ekvoDate\endcsname\ parse class and package options with expkv] -\PassOptionsToPackage{full}{textcomp} -\documentclass{l3doc} -\RequirePackage[oldstylenums,nott]{kpfonts} -\input{glyphtounicode} -\pdfgentounicode=1 -\RequirePackage{listings} -\RequirePackage{booktabs} -\RequirePackage{array} -\RequirePackage{collcell} -\RequirePackage{siunitx} -\RequirePackage{xcolor} -\RequirePackage{caption} -\RequirePackage{microtype} -\RequirePackage{accsupp} -\lstset - { - ,flexiblecolumns=false - ,basewidth=.53em - ,gobble=2 - ,basicstyle=\fontfamily{jkp}\itshape - ,morekeywords=^^A - {^^A - \ekvoProcessLocalOptions, - \ekvoProcessGlobalOptions, - \ekvoProcessUnusedGlobalOptions, - \ekvoProcessOptionsList, - \ekvoUseUnknownHandlers - } - ,morecomment=[l]\% - ,commentstyle=\color[gray]{0.4} - ,literate={\{}{{\CodeSymbol\{}}{1} - {\}}{{\CodeSymbol\}}}{1} - ^^A,literate=*{<key>}{\key}{4}{<set>}{\set}{4} - } -\newcommand*\CodeSymbol[1]{\textbf{#1}} -\RequirePackage{randtext} -\let\metaORIG\meta -\protected\def\meta #1{\texttt{\metaORIG{#1}}} -\renewcommand*\thefootnote{\fnsymbol{footnote}} -\definecolor{expkvred}{HTML}{9F393D} -\colorlet{expkvgrey}{black!75} -\makeatletter -\newcommand*\expkv - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv}^^A - \href{https://github.com/Skillmon/tex_expkv} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv}^^A - } -\newcommand*\expkvpkg[1] - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv-#1}^^A - \href{https://github.com/Skillmon/tex_expkv-#1} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - {\color{expkvgrey}^^A - \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{#1}^^A - }^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv-#1}^^A - } -\newcommand*\expkvd{\expkvpkg{def}} -\newcommand*\expkvc{\expkvpkg{cs}} -\newcommand*\expkvo{\expkvpkg{opt}} -\newcommand\kv{\meta{key}=\meta{value}} -\newcommand\key{\meta{key}} -\newcommand\val{\meta{value}} -\newcommand\set{\meta{set}} -\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} -\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} -\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long} -\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space} -\DoNotIndex{\@,\unexpanded,\string,\expanded,\dimexpr,\global,\glueexpr,\hbox} -\DoNotIndex{\numexpr,\RequirePackage,\setbox,\the,\unless,\xdef,\@firstofone} -\DoNotIndex{\@firstoftwo,\@gobble,\@secondoftwo,\AtEndOfPackage,\newcommand} -\DoNotIndex{\PackageError,\@classoptionslist,\@clsextension,\@currext} -\DoNotIndex{\@currname,\@empty,\@gobbletwo,\@onlypreamble,\@raw@classoptionslist} -\DoNotIndex{\@unprocessedoptions,\@unusedoptionlist} -\DoNotIndex{\DeclareRelease,\DeclareCurrentRelease,\IfFormatAtLeastTF} -\DoNotIndex{\PackageWarning,\requestedLaTeXdate,\@ifstar} -\DoNotIndex{\pkgcls@parse@date@arg,\pkgcls@targetdate,\pkgcls@targetlabel} -\DoNotIndex{\ifcsname} -\DoNotIndex{\ifx} -\DoNotIndex{\ifdefined} -\DoNotIndex{\iffalse} -\DoNotIndex{\iftrue} -\DoNotIndex{\ifnum} -\DoNotIndex{\else} -\DoNotIndex{\fi} -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi -\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} -\makeatother -\begin{document} - \title - {^^A - \texorpdfstring - {^^A - \huge\expkvo - \\[\medskipamount] - \Large parse class and package options with \expkv - }{expkv-opt - parse class and package options with expkv}^^A - } - \date{\ekvoDate\space v\ekvoVersion} - \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}} - \DocInput{expkv-opt.dtx} -\end{document} -%</driver>^^A=<< -% \fi -% -% \maketitle -% \renewcommand*\thefootnote{\arabic{footnote}} -% -% \begin{abstract} -% \noindent\parfillskip=0pt -% \expkvo\ provides option parsing for classes and packages in \LaTeXe\ based on -% \expkv. Global and local options are parsed individually by different -% commands. The stylised name is \expkvo\ but the files use \file{expkv-opt}, -% this is due to CTAN-rules which don't allow \string| in package names since -% that is the pipe symbol in *nix shells. -% \end{abstract} -% -% \tableofcontents -% -% \begin{documentation}^^A>>= -% -% \section{Documentation} -% -% The \expkv\ family provides at its core a \kv\ parser and additionally -% packages, one to conveniently define new keys (\expkvd) and another to build -% expandable \kv\ taking control sequences (\expkvc). Still missing from the mix -% was a solution to parse \LaTeXe\ class and package options, a gap that's -% hereby filled with \expkvo. -% -% 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. One thing 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. -% \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. The one thing that \expkvo\ doesn't support, which \expkv\ -% alone would, is active commas. But there is no good reason why a comma could -% be active in the preamble. -% -% The package can be loaded with -% \begin{lstlisting} -% \usepackage{expkv-opt} -% \end{lstlisting} -% and if you need a specific version you can use \LaTeXe's rollback support, so -% to load v0.1 explicitly use: -% \begin{lstlisting} -% \usepackage{expkv-opt}[=v0.1] -% \end{lstlisting} -% which will load the latest subversion of v0.1 -% (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) -% Unlike the other packages in the \expkv\ family, \expkvo\ is only provided as -% a \LaTeX\ package. -% -% Before reading this documentation you should read \expkv's documentation and -% might want to also read the documentation of \expkvd. -% -% -% \subsection{Macros} -% -% \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 \cs{CurrentOption}. -% -% \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} -% \begin{description} -% \item[Class:] -% \begin{description} -% \item[defined] \emph{nothing} -% \item[undefined] -% add the key to the list of unused global options (if the local option -% list matches the option list of the main class) -% \end{description} -% \item[Package:] -% \begin{description} -% \item[defined] \emph{nothing} -% \item[undefined] throw an error -% \end{description} -% \end{description} -% -% \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} -% \begin{description} -% \item[Class:] -% \begin{description} -% \item[defined] remove the option from the list of unused global options -% \item[undefined] \emph{nothing} -% \end{description} -% \item[Package:] -% \begin{description} -% \item[defined] remove the option from the list of unused global options -% \item[undefined] \emph{nothing} -% \end{description} -% \end{description} -% -% \begin{function}{\ekvoProcessUnusedGlobalOptions} -% \begin{syntax} -% \cs{ekvoProcessUnusedGlobalOptions}\marg{set} -% \end{syntax} -% This does no longer work with \LaTeXe\ kernels starting from 2021-05-01, -% since the handling of the unused option list changed and no longer includes -% the values. As a result this will throw a warning and else will be ignored. -% \end{function} -% -% \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} -% \begin{description} -% \item[Class:] -% \begin{description} -% \item[defined] \emph{nothing} -% \item[undefined] \emph{nothing} -% \end{description} -% \item[Package:] -% \begin{description} -% \item[defined] \emph{nothing} -% \item[undefined] \emph{nothing} -% \end{description} -% \end{description} -% -% \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 \key\ without a \val\ -% is encountered and get one argument, being the \key. Analogous the macro -% \meta{cs_2} will be called if an undefined \key\ with a \val\ was specified. -% It will get two arguments, the first being the \key\ 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{Example} -% -% 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 -% \cs[no-index]{thefootnote} and we'll call this package \pkg{ex-footnote}. -% First we report back which package we are: -% \begin{lstlisting} -% \ProvidesPackage{ex-footnote}[2020-02-02 v1 change footnotes] -% \end{lstlisting} -% Next we'll need to provide the options we want the package to have. -% \begin{lstlisting} -% \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{lstlisting} -% We can provide initial values just by defining the two macros storing the -% value. -% \begin{lstlisting} -% \newcommand*\exfn@color{} -% \newcommand*\exfn@format{arabic} -% \end{lstlisting} -% Next we need to process the options given to the package. The package should -% only obey options directly passed to it, so we're only using -% |\ekvoProcessLocalOptions|: -% \begin{lstlisting} -% \ekvoProcessLocalOptions{ex-footnote} -% \end{lstlisting} -% Now everything that's still missing is actually changing the way footnotes -% appear: -% \begin{lstlisting} -% \renewcommand*\thefootnote -% {% -% \ifx\exfn@color\@empty -% \csname\exfn@format\endcsname{footnote}% -% \else -% \textcolor{\exfn@color}{\csname\exfn@format\endcsname{footnote}}% -% \fi -% } -% \end{lstlisting} -% \bigskip -% -% \noindent -% So the complete code of the package would look like this: -% \begin{lstlisting} -% \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} -% -% \renewcommand*\thefootnote -% {% -% \ifx\exfn@color\@empty -% \csname\exfn@format\endcsname{footnote}% -% \else -% \textcolor{\exfn@color}{\csname\exfn@format\endcsname{footnote}}% -% \fi -% } -% \end{lstlisting} -% \bigskip -% -% \noindent -% And it could be used with one of the following lines: -% \begin{lstlisting} -% \usepackage{ex-footnote} -% \usepackage[format=fnsymbol]{ex-footnote} -% \usepackage[color=green]{ex-footnote} -% \usepackage[color=red,format=roman]{ex-footnote} -% \end{lstlisting} -% -% -% \subsection{Bugs} -% -% If you happen to find bugs, it'd be great if you let me know. Just write me an -% email (see the front page) or submit a bug report on GitHub: -% \url{https://github.com/Skillmon/tex_expkv-opt} -% -% -% \subsection{License} -% -% Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\ -% Jonathan P. Spratte -% -% \medskip\noindent -% 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: -% -% \url{http://www.latex-project.org/lppl.txt} -% -% \noindent -% This work is ``maintained'' (as per LPPL maintenance status) by -% -% \mbox{Jonathan P. Spratte}. -% -% \end{documentation}^^A=<< -% -% \begin{implementation}^^A>>= -% -% \clearpage -% -% -% \section{Implementation} -% -% \gobbledocstriptag -%<*pkg> -% -% First we check whether the \LaTeXe\ kernel supports raw options. If it doesn't -% we check whether a specific version was requested, and if that's not the case -% we manually run |\pkgcls@parse@date@arg| with the last version that supported -% non-raw options. -% \begin{macrocode} -\IfFormatAtLeastTF{2021/05/01} - {} - {% - \ifx\pkgcls@targetlabel\@empty - \ifnum\requestedLaTeXdate=\pkgcls@targetdate - \pkgcls@parse@date@arg{=v0.1}% - \fi - \fi - } -% \end{macrocode} -% Then we tell \LaTeXe\ where to find which release so that the package rollback -% code of \LaTeXe\ can do its thing. -% \begin{macrocode} -\DeclareRelease{v0.1}{2020/10/10}{expkv-opt-2020-10-10.sty} -\DeclareCurrentRelease{v0.2}{2021/04/04} -% \end{macrocode} -% -% Start the package with the typical \LaTeX\ standards. -% -% \begin{macro}{\ekvoVersion,\ekvoDate} -% Store the packages version and date in two macros. -% \begin{macrocode} -\newcommand*\ekvoVersion{0.2} -\newcommand*\ekvoDate{2021-04-04} -% \end{macrocode} -% \end{macro} -% And we report who we are and what we need. -% \begin{macrocode} -\ProvidesPackage{expkv-opt} - [% - \ekvoDate\space v\ekvoVersion\space - parse class and package options with expkv% - ] -\RequirePackage{expkv} -% \end{macrocode} -% -% -% \subsection{Loop} -% -% \begin{macro}[internal] -% {\ekvo@CurrentOption@loop,\ekvo@CurrentOption@loop@,\ekvo@end@loop} -% We'll need some loop which can iterate over a comma separated list. The loop -% is very basic and only works for commas of category~12. First we insert the -% delimiters for the actual loop. -% \begin{macrocode} -\protected\long\def\ekvo@CurrentOption@loop#1#2% - {% - \ekvo@CurrentOption@loop@#2\ekv@mark#1,\ekv@stop,\ekvo@tail - } -% \end{macrocode} -% The actual loop checks whether the final element has been read and if so -% ends the loop. Else blank elements are ignored, |\CurrentOption| is set and -% the macro which parses the list elements called. Then call the next -% iteration. -% \begin{macrocode} -\long\def\ekvo@CurrentOption@loop@#1#2,% - {% - \ekv@gobble@from@mark@to@stop#2\ekvo@end@loop\ekv@stop - \ekv@ifblank{#2}% - {}% - {% - \edef\CurrentOption{\unexpanded\expandafter{\@gobble#2}}% - #1{#2}% - }% - \ekvo@CurrentOption@loop@#1\ekv@mark - } -\long\def\ekvo@end@loop#1\ekvo@tail{} -% \end{macrocode} -% \end{macro} -% -% -% \subsection{Tests} -% -% \begin{macro}[internal]{\ekvo@ifx@TF,\ekvo@ifx@F} -% We'll need branching |\ifx| tests so that user input containing unbalanced -% \TeX\ ifs doesn't break (at least not because of us, everything else is the -% fault of \LaTeXe). -% \begin{macrocode} -\def\ekvo@ifx@TF#1#2{\ifx#1#2\ekv@fi@firstoftwo\fi\@secondoftwo} -\def\ekvo@ifx@F#1#2{\ifx#1#2\ekv@fi@gobble\fi\@firstofone} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvo@do@with@set,\ekvo@name,\ekvo@setname} -% This test checks whether the \set\ is defined. If it is we store it in -% |\ekvo@setname| and set |\ekvo@name| to a short cut to get the \key's -% callback name. Next we execute the code in |#2|, if the \set\ isn't defined -% |#2| is gobbled. -% \begin{macrocode} -\protected\def\ekvo@do@with@set#1#2% - {% - \ekvifdefinedset{#1}% - {% - \expandafter - \let\expandafter\ekvo@name\csname\ekv@undefined@set{#1}\endcsname - \def\ekvo@setname{#1}% - #2% - }% - {\ekvo@err@undefined@set{#1}}% - } -% \end{macrocode} -% \end{macro} -% -% -% \subsection{Key handlers} -% -% \expkvo\ uses handlers specifying what happens if a parsed \key\ is defined or -% undefined. -% -% \begin{macro}[internal] -% {\ekvo@handle@undefined@k@pkg,\ekvo@handle@undefined@kv@pkg} -% The case for undefined keys in a local list of a package is easy, just throw -% appropriate errors. -% \begin{macrocode} -\protected\long\def\ekvo@handle@undefined@k@pkg#1% - {% - \ekv@ifdefined{\ekvo@name{#1}}% - {\ekvo@err@value@required{#1}}% - {\ekvo@err@undefined@key{#1}}% - } -\def\ekvo@handle@undefined@kv@pkg#1#2% - {% - \ekv@ifdefined{\ekvo@name{#1}N}% - {\ekvo@err@value@forbidden{#1}}% - {\ekvo@err@undefined@key{#1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvo@addto@unused@one,\ekvo@addto@unused@two, -% \ekvo@rmfrom@unused@one,\ekvo@rmfrom@unused@two -% } -% These macros will add or remove the |\CurrentOption| to or from the list of -% unused global options. Since |\ekvo@do@unusedoptionlist| will have some -% overhead before calling the list changing macro in filtering the current -% option, we use an optimization here in that we check whether the list is -% empty before calling the |rmfrom| function. -% \begin{macrocode} -\long\def\ekvo@addto@unused@one#1{\ekvo@do@unusedoptionlist\ekvo@addto@list} -\long\def\ekvo@addto@unused@two#1#2{\ekvo@do@unusedoptionlist\ekvo@addto@list} -\long\def\ekvo@rmfrom@unused@one#1% - {% - \ekvo@ifx@F\@unusedoptionlist\@empty - {\ekvo@do@unusedoptionlist\ekvo@rmfrom@list}% - } -\long\def\ekvo@rmfrom@unused@two#1#2% - {% - \ekvo@ifx@F\@unusedoptionlist\@empty - {\ekvo@do@unusedoptionlist\ekvo@rmfrom@list}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvo@do@unusedoptionlist, -% \ekvo@prepare@unusedoption, -% \ekvo@prepare@unusedoption@a, -% \ekvo@prepare@unusedoption@b, -% \ekvo@prepare@unusedoption@c -% } -% The way the new \LaTeXe\ kernel handles the unused option list changed. Now -% not the entire |\CurrentOption| is listed, but just everything up to the -% first equals sign, and spaces got zapped, doesn't matter whether the raw -% option list gets used or not. So we have to zap spaces and remove everything -% from the first equals sign onwards. The code used here will fail if the -% current option contains an |\ekv@mark| or |\ekv@stop| before the first -% equals sign (this seems rather unlikely). -% \begin{macrocode} -\protected\def\ekvo@do@unusedoptionlist#1% - {% - \let\ekvo@unpreparedCurrentOption\CurrentOption - \edef\CurrentOption - {\expandafter\ekvo@prepare@unusedoption\CurrentOption=\ekv@mark}% - #1\@unusedoptionlist - \let\CurrentOption\ekvo@unpreparedCurrentOption - } -\def\ekvo@prepare@unusedoption{\ekvo@prepare@unusedoption@a\@empty} -\def\ekvo@prepare@unusedoption@a#1% - {% - \long\def\ekvo@prepare@unusedoption@a##1=##2\ekv@mark - {% - \ekvo@prepare@unusedoption@b##1\ekv@stop - \ekv@mark\ekvo@prepare@unusedoption@b - #1\ekv@mark\ekvo@prepare@unusedoption@c - }% - } -\ekvo@prepare@unusedoption@a{ } -\long\def\ekvo@prepare@unusedoption@b#1 #2\ekv@mark#3{#3#1#2\ekv@mark#3} -\long\def\ekvo@prepare@unusedoption@c - #1\ekv@stop - \ekv@mark\ekvo@prepare@unusedoption@b\ekv@mark\ekvo@prepare@unusedoption@c - {\unexpanded\expandafter{#1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekvo@set@handlers@local,\ekvo@set@handlers@global, -% \ekvo@set@handlers@list -% } -% These macros are boring. They just set up the handlers to respect the rules -% documented earlier. -% \begin{macrocode} -\protected\def\ekvo@set@handlers@local - {% - \ekvo@if@need@handlers - {% - \ifx\@currext\@clsextension - \ifx\@classoptionslist\relax - \let\ekvo@handle@undefined@k\@gobble - \let\ekvo@handle@undefined@kv\@gobbletwo - \else - \expandafter - \ifx - \csname @raw@opt@\@currname.\@currext\endcsname - \@raw@classoptionslist - \let\ekvo@handle@undefined@k\ekvo@addto@unused@one - \let\ekvo@handle@undefined@kv\ekvo@addto@unused@two - \else - \let\ekvo@handle@undefined@k\@gobble - \let\ekvo@handle@undefined@kv\@gobbletwo - \fi - \fi - \else - \let\ekvo@handle@undefined@k\ekvo@handle@undefined@k@pkg - \let\ekvo@handle@undefined@kv\ekvo@handle@undefined@kv@pkg - \fi - }% - } -\protected\def\ekvo@set@handlers@global - {% - \unless\ifx\@unusedoptionlist\@empty - \let\ekvo@handle@defined@k\ekvo@rmfrom@unused@one - \let\ekvo@handle@defined@kv\ekvo@rmfrom@unused@two - \fi - \ekvo@if@need@handlers - {% - \let\ekvo@handle@undefined@k\@gobble - \let\ekvo@handle@undefined@kv\@gobbletwo - }% - } -\protected\def\ekvo@set@handlers@list - {% - \ekvo@if@need@handlers - {% - \let\ekvo@handle@undefined@k\@gobble - \let\ekvo@handle@undefined@kv\@gobbletwo - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvo@if@need@handlers,\ekvo@dont@need@handlers} -% If the user specifies handlers this macro will be let to -% |\ekvo@dont@need@handlers|, which will act like |\@gobble| and also let it -% to |\@firstofone| afterwards. -% \begin{macrocode} -\let\ekvo@if@need@handlers\@firstofone -\protected\long\def\ekvo@dont@need@handlers#1% - {% - \let\ekvo@if@need@handlers\@firstofone - }% -% \end{macrocode} -% \end{macro} -% -% We have to set the default for the handlers of defined keys, because they -% don't necessarily get defined before a list is parsed. -% \begin{macrocode} -\let\ekvo@handle@defined@k\@gobble -\let\ekvo@handle@defined@kv\@gobbletwo -% \end{macrocode} -% -% -% \subsection{Processing list elements} -% -% \begin{macro}[internal]{\ekvo@process@common} -% All the key processing frontend macros use the same basic structure. |#1| -% will be a simple test, deciding whether the list will really be parsed or -% not, |#3| will be the \set, and |#2| will be the individual code of the -% frontend macro which should be executed if both the test in |#1| is true and -% the \set\ is defined. -% \begin{macrocode} -\protected\def\ekvo@process@common#1#2#3% - {% - #1{\ekvo@do@with@set{#3}{#2}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvo@process@list} -% This macro only expands the list holding macro and forwards it to the -% loop macro. -% \begin{macrocode} -\protected\def\ekvo@process@list#1% - {% - \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@parse - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvo@parse} -% This macro calls internals of |\ekvparse| such that the code splitting at -% commas isn't executed, else this is equivalent to -% |\ekvparse\ekvo@set@k\ekvo@set@kv{#1}|. -% \begin{macrocode} -\protected\long\def\ekvo@parse#1% - {% - \ekv@eq@other#1\ekv@nil\ekv@mark\ekv@parse@eq@other@a - =\ekv@mark\ekv@parse@eq@active - \ekvo@set@k\ekvo@set@kv - \ekvo@tail - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvo@set@k,\ekvo@set@kv} -% These two macros check whether the key is defined and if so call the handler -% for defined keys and execute the key, else the handler for undefined keys is -% called. They have to clean up a bit of code which is left by |\ekvo@parse|. -% \begin{macrocode} -\protected\def\ekvo@set@k#1#2\ekvo@tail - {% - \ekv@ifdefined{\ekvo@name{#1}N}% - {% - \ekvo@handle@defined@k{#1}% - \csname\ekvo@name{#1}N\endcsname - }% - {\ekvo@handle@undefined@k{#1}}% - } -\protected\def\ekvo@set@kv#1#2#3\ekvo@tail - {% - \ekv@ifdefined{\ekvo@name{#1}}% - {% - \ekvo@handle@defined@kv{#1}{#2}% - \csname\ekvo@name{#1}\endcsname{#2}% - }% - {\ekvo@handle@undefined@kv{#1}{#2}}% - } -% \end{macrocode} -% \end{macro} -% -% -% \subsection{List variable helpers} -% -% \begin{macro}[internal]{\ekvo@addto@list} -% This macro is rather simple. If the list to which the |\CurrentOption| -% should be added is empty we can just let the list to the |\CurrentOption|. -% Else we have to expand the list once and the |\CurrentOption| once. -% \begin{macrocode} -\protected\def\ekvo@addto@list#1% - {% - \ekvo@ifx@TF#1\@empty - {\let#1\CurrentOption}% - {% - \edef#1% - {% - \unexpanded\expandafter{#1},% - \unexpanded\expandafter{\CurrentOption}% - }% - }% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekvo@rmfrom@list,\ekvo@rmfrom@list@} -% This works by looping over every list item and comparing it to -% |\ekvo@curropt| which stores the real |\CurrentOption|. This is -% comparatively slow, but works for items containing braces unlike what -% \LaTeXe\ does. We could be faster for items not containing braces, though. -% \begin{macrocode} -\protected\def\ekvo@rmfrom@list#1% - {% - \ekvo@ifx@F#1\@empty - {% - \let\ekvo@tmp@list\@empty - \let\ekvo@curropt\CurrentOption - \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@rmfrom@list@ - \let\CurrentOption\ekvo@curropt - \let#1\ekvo@tmp@list - }% - } -\protected\long\def\ekvo@rmfrom@list@#1% - {% - \ekvo@ifx@F\CurrentOption\ekvo@curropt - {\ekvo@addto@list\ekvo@tmp@list}% - } -% \end{macrocode} -% \end{macro} -% -% -% \subsection{Errors} -% -% \begin{macro}[internal] -% { -% \ekvo@err@undefined@key,\ekvo@err@value@required, -% \ekvo@err@value@forbidden,\ekvo@err@undefined@set -% } -% Just some macros to throw errors in the few cases an error has to be thrown. -% \begin{macrocode} -\protected\def\ekvo@err@undefined@key#1% - {% - \PackageError{expkv-opt}{Undefined key `#1' in set `\ekvo@setname'}{}% - } -\protected\def\ekvo@err@value@required#1% - {% - \PackageError{expkv-opt}% - {Value required for key `#1' in set `\ekvo@setname'}% - {}% - } -\protected\def\ekvo@err@value@forbidden#1% - {% - \PackageError{expkv-opt}% - {Value forbidden for key `#1' in set `\ekvo@setname'}% - {}% - } -\protected\def\ekvo@err@undefined@set#1% - {% - \PackageError{expkv-opt}% - {Undefined set `#1'}% - {The set for which you try to parse options isn't defined in expkv.}% - } -% \end{macrocode} -% \end{macro} -% -% -% \subsection{User Interface} -% -% The user interface macros just put together the bits and pieces. -% \begin{macro}{\ekvoProcessLocalOptions} -% \begin{macrocode} -\protected\def\ekvoProcessLocalOptions - {% - \ekvo@process@common - {\ekv@ifdefined{@raw@opt@\@currname.\@currext}\@firstofone\@gobble}% - {% - \ekvo@set@handlers@local - \expandafter - \ekvo@process@list\csname @raw@opt@\@currname.\@currext\endcsname - \AtEndOfPackage{\let\@unprocessedoptions\relax}% - }% - } -% \end{macrocode} -% \end{macro} -% \begin{macro}{\ekvoProcessGlobalOptions} -% \begin{macrocode} -\protected\def\ekvoProcessGlobalOptions - {% - \ekvo@process@common{\ekvo@ifx@F\@classoptionslist\relax}% - {% - \ekvo@set@handlers@global - \ekvo@process@list\@raw@classoptionslist - \let\ekvo@handle@defined@k\@gobble - \let\ekvo@handle@defined@kv\@gobbletwo - }% - } -% \end{macrocode} -% \end{macro} -% \begin{macro}{\ekvoProcessUnusedGlobalOptions} -% \begin{macrocode} -\protected\def\ekvoProcessUnusedGlobalOptions#1% - {% - \PackageWarning{expkv-opt}% - {This macro no longer works because of changes in the LaTeX2e kernel.}% - } -% \end{macrocode} -% \end{macro} -% \begin{macro}{\ekvoProcessOptionsList} -% \begin{macrocode} -\protected\def\ekvoProcessOptionsList#1% - {% - \ekvo@process@common{\ekvo@ifx@F#1\@empty}% - {% - \ekvo@set@handlers@list - \ekvo@process@list#1% - }% - } -% \end{macrocode} -% \end{macro} -% \begin{macro}{\ekvoUseUnknownHandlers} -% \begin{macrocode} -\protected\def\ekvoUseUnknownHandlers - {\@ifstar\ekvoUseUnknownHandlers@s\ekvoUseUnknownHandlers@n} -\protected\def\ekvoUseUnknownHandlers@s - {% - \def\ekvo@handle@undefined@k - {% - \ekv@ifdefined{\ekvo@name{}uN}% - {\csname\ekvo@name{}uN\endcsname}% - {\@gobble}% - }% - \long\def\ekvo@handle@undefined@kv##1##2% - {% - \ekv@ifdefined{\ekvo@name{}u}% - {\csname\ekvo@name{}u\endcsname{##2}{##1}}% - {}% - }% - \let\ekvo@if@need@handlers\ekvo@dont@need@handlers - } -\protected\def\ekvoUseUnknownHandlers@n#1#2% - {% - \let\ekvo@handle@undefined@k#1\relax - \let\ekvo@handle@undefined@kv#2\relax - \let\ekvo@if@need@handlers\ekvo@dont@need@handlers - } -% \end{macrocode} -% \end{macro} -% -% All user interface macros should be only used in the preamble. -% \begin{macrocode} -\@onlypreamble\ekvoProcessLocalOptions -\@onlypreamble\ekvoProcessGlobalOptions -\@onlypreamble\ekvoProcessUnusedGlobalOptions -\@onlypreamble\ekvoProcessOptionsList -\@onlypreamble\ekvoUseUnknownHandlers -% \end{macrocode} -% -% \gobbledocstriptag -%</pkg> -% -% \end{implementation}^^A=<< -% -% \clearpage -% \PrintIndex -% -% \Finale -\endinput -% -^^A vim: ft=tex fdm=marker fmr=>>=,=<< diff --git a/macros/generic/expkv-opt/expkv-opt.pdf b/macros/generic/expkv-opt/expkv-opt.pdf Binary files differdeleted file mode 100644 index 5ecded074a..0000000000 --- a/macros/generic/expkv-opt/expkv-opt.pdf +++ /dev/null diff --git a/macros/generic/expkv/README.md b/macros/generic/expkv/README.md deleted file mode 100644 index ccfa3194b3..0000000000 --- a/macros/generic/expkv/README.md +++ /dev/null @@ -1,35 +0,0 @@ -------------------------------------------------------------------------------- -# expkv -- an expandable key=val implementation - -Version 2021-09-20 v1.9a - -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt - -Hosted at https://github.com/Skillmon/tex_expkv - -------------------------------------------------------------------------------- - -Copyright (C) 2020-2021 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. - -------------------------------------------------------------------------------- - -This provides an expandable key=val implementation that is somewhat fast, but -not the fastest key=val implementation available. It is generic code and -completely self-contained. There is a LaTeX package `expkv.sty` included to play -nice on LaTeX's package loading system, but that package is not needed and does -not provide more functionality than the generic code in `expkv.tex`. - -A key-defining interface that is not as rudimentary as the macros provided in -this package is contained in expkv-def: -https://github.com/Skillmon/tex_expkv-def diff --git a/macros/generic/expkv/expkv.dtx b/macros/generic/expkv/expkv.dtx deleted file mode 100644 index 8651ad97a3..0000000000 --- a/macros/generic/expkv/expkv.dtx +++ /dev/null @@ -1,2980 +0,0 @@ -% \iffalse meta-comment -% -% File: expkv.dtx Copyright (C) 2020-2021 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 -% -% ------------------------------------------------------------------------------ -% -%<*driver>^^A>>= -\def\nameofplainTeX{plain} -\ifx\fmtname\nameofplainTeX\else - \expandafter\begingroup -\fi -\input l3docstrip.tex -\askforoverwritefalse -\preamble - --------------------------------------------------------------- -expkv -- an expandable key=val implementation -E-mail: jspratte@yahoo.de -Released under the LaTeX Project Public License v1.3c or later -See http://www.latex-project.org/lppl.txt --------------------------------------------------------------- - -Copyright (C) 2020-2021 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. - -This work consists of the file expkv.dtx -and the derived files expkv.pdf - expkv.sty - expkv.tex - t-expkv.tex - -\endpreamble -% stop docstrip adding \endinput -\postamble -\endpostamble -\generate{\file{expkv.sty}{\from{expkv.dtx}{pkg}}} -\generate{\file{expkv.tex}{\from{expkv.dtx}{tex}}} -\generate{\file{t-expkv.tex}{\from{expkv.dtx}{ctx}}} -\ifx\fmtname\nameofplainTeX - \expandafter\endbatchfile -\else - \expandafter\endgroup -\fi -% -\IfFileExists{expkv.tex}{\input{expkv.tex}}{} -\ProvidesFile{expkv.dtx} - [\csname ekvDate\endcsname\ an expandable key=val implementation] -\PassOptionsToPackage{full}{textcomp} -\documentclass{l3doc} -\RequirePackage[oldstylenums,nott]{kpfonts} -\input{glyphtounicode} -\pdfgentounicode=1 -\RequirePackage{xfp} ^^A required for an example -\RequirePackage{listings} -\RequirePackage{booktabs} -\RequirePackage{array} -\RequirePackage{collcell} -\RequirePackage{siunitx} -\DeclareSIUnit\ops{ops} -\RequirePackage{xcolor} -\RequirePackage{caption} -\RequirePackage{microtype} -\RequirePackage{accsupp} -\RequirePackage{enumitem} -\RequirePackage{tcolorbox} -\newtcolorbox{exresult}[2][] - {colback=expkvgrey!10!white,colframe=expkvgrey,fontupper=\small,width={#2},#1} -\newtcbox\exres[1][] - { - colback=expkvgrey!10!white - ,colframe=expkvgrey - ,size=small - ,nobeforeafter - ,tcbox raise base - ,fontupper=\small - ,#1 - } -\def\mylstwd{.55em} -\lstdefinelanguage{expkv}[primitive]{TeX} - { - ,moretexcs=[4]^^A e-TeX - { - expanded, - numexpr, - protected, - } - ,moretexcs=[5]^^A plain/LaTeX/ConTeXt - { - approx, - begin, - empty, - item, - LaTeX, - makeatletter,makeatother, - newcommand,newdimen, - RequirePackage, - rule, - TeX, - textit,texttt, - usepackage, - usemodule, - } - ,moretexcs=[6]^^A used packages - { - ^^A xfp - fpeval, - ^^A keyval - setkeys, - ^^A yax - defactiveparameter,storevalue,setparameterlist - } - ,moretexcs=[2]^^A expkv macros - { - ekvdef,ekvdefNoVal,^^A - ekvlet,ekvletNoVal,ekvletkv,ekvletkvNoVal,^^A - ekvdefunknown,ekvdefunknownNoVal,^^A - ekvredirectunknown,ekvredirectunknownNoVal,^^A - ekvset,ekvsetSneaked,^^A - ekvsetdef,ekvsetSneakeddef,ekvsetdefSneaked,^^A - ekvparse,^^A - ekvVersion,ekvDate,^^A - ekvifdefined,ekvifdefinedNoVal,ekvifdefinedset,^^A - ekvbreak,ekvbreakPreSneak,ekvbreakPostSneak,^^A - ekvsneak,ekvsneakPre,^^A - ekvchangeset,^^A - ekvoptarg,ekvoptargTF,^^A - ekverr - } - ,moretexcs=[3]^^A expkv-pkg macros - { - ^^A expkv-cs - ekvcSecondaryKeys,ekvcSplitAndForward, - ^^A expkv-def - ekvdefinekeys - } - } -\colorlet{codeparam}{cyan!65!black} -\lstset - { - ,language=expkv - ,flexiblecolumns=false - ,basewidth=\mylstwd - ,gobble=2 - ,basicstyle=\fontfamily{jkp}\itshape - ,texcsstyle=*[1]{\bfseries\color{expkvgrey}} ^^A primitives - ,texcsstyle=*[2]{\bfseries\color{expkvred}} ^^A expkv - ,texcsstyle=*[3]{\color{expkvred}} ^^A expkv-pkg - ,texcsstyle=*[4]{\bfseries\color{expkvgrey}} ^^A e-TeX - ,texcsstyle=*[5]{\bfseries\color{expkvgrey}} ^^A plain/LaTeX macros - ,texcsstyle=*[6]{} ^^A macros of other packages - ,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} - ^^A,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}}} -\RequirePackage{randtext} -\let\metaORIG\meta -\protected\def\meta #1{\texttt{\metaORIG{#1}}} -\renewcommand*\thefootnote{\fnsymbol{footnote}} -\definecolor{expkvred}{HTML}{9F393D} -\colorlet{expkvgrey}{black!75} -\makeatletter -\newcommand*\example{\par\smallskip\noindent\textit{Example:} \ignorespaces} -\newcommand*\expFormat - {^^A - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex\hbox{{\color{expkvgrey}P}}^^A - }^^A - } -\newcommand*\kvstyle - {^^A - \normalfont\rmfamily\bfseries\color{expkvred}^^A - } -\newcommand*\expkv - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv}^^A - \href{https://github.com/Skillmon/tex_expkv} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv}^^A - } -\newcommand*\expkvpkg[1] - {^^A - \texorpdfstring - {^^A - \mbox - {^^A - \BeginAccSupp{ActualText=expkv-#1}^^A - \href{https://github.com/Skillmon/tex_expkv-#1} - {^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - {\color{expkvgrey}^^A - \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{#1}^^A - }^^A - }^^A - \EndAccSupp{}^^A - }^^A - } - {expkv-#1}^^A - } -\newcommand*\expkvd{\expkvpkg{def}} -\newcommand*\expkvc{\expkvpkg{cs}} -\newcommand*\expkvo{\expkvpkg{opt}} -\ExplSyntaxOn -\newcommand*\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 -\newcommand\kv{\meta{key}=\meta{value}} -\newcommand\key{\meta{key}} -\newcommand\val{\meta{value}} -\newcommand\set{\meta{set}} -\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} -\DoNotIndex{\directlua} -\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} -\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long} -\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space} -\DoNotIndex{\^,\@,\unexpanded,\string,\expanded,\detokenize,\meaning} -\DoNotIndex{\lastnamedcs,\romannumeral,\z@} -\DoNotIndex{\noexpand,\normalexpanded,\normalunexpanded} -\DoNotIndex{\par,\protect} -\DoNotIndex{\unprotect} -\DoNotIndex{\writestatus} -\DoNotIndex{\ifcsname} -\DoNotIndex{\ifx} -\DoNotIndex{\iffalse} -\DoNotIndex{\ifnum} -\DoNotIndex{\else} -\DoNotIndex{\fi} -\@gobble\fi ^^A ignoring \ifx etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx etc., but only one \fi -\@gobble\fi ^^A ignoring \ifx etc., but only one \fi -\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} -\newcommand*\pmso[1] ^^A poor man's strike out - {^^A - \leavevmode - \begingroup - \sbox0{#1}^^A - \rlap{\vrule height .6ex depth -.5ex width \wd0\relax}^^A - \usebox0\relax - \endgroup - } -\makeatother -\begin{document} - \title - {^^A - \texorpdfstring - {^^A - \huge\expkv - \\[\medskipamount] - \Large an {\expFormat}andable - \meta{{\kvstyle k}\kern-.05em ey}=^^A - \meta{{\kvstyle v}\kern-.05em alue} - implementation^^A - }{expkv - an expandable <key>=<value> implementation}^^A - } - \date{\ekvDate\space v\ekvVersion} - \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}} - \DocInput{expkv.dtx} -\end{document} -%</driver>^^A=<< -% \fi -% -% \maketitle -% \renewcommand*\thefootnote{\arabic{footnote}} -% -% \begin{abstract} -% \noindent\parfillskip=0pt -% \expkv\ provides a small interface for \kv\ parsing. The parsing macro is -% \emph{fully expandable}, the \meta{code} of your keys might be not. \expkv\ is -% \emph{swift}, close to the fastest \kv\ implementation. However it is the -% fastest which copes with active commas and equal signs and doesn't strip -% braces accidentally. -% \end{abstract} -% -% \tableofcontents -% -% \begin{documentation}^^A>>= -% -% \section{Documentation} -% -% \expkv\ provides an expandable \kv\ parser. The \kv\ pairs should be given as -% a comma separated list and the separator between a \key\ and the associated -% \val\ should be an equal sign. Both, the commas and the equal signs, might be -% of category 12 (other) or 13 (active). To support this is necessary as for -% example \pkg{babel} turns characters active for some languages, for instance -% the equal sign is turned active for Turkish. -% -% \expkv\ is usable as generic code, as a \LaTeX\ package or as a \ConTeXt\ -% module. To use it, just use one of: -% \begin{lstlisting} -% \input expkv % plainTeX -% \usepackage{expkv} % LaTeX -% \usemodule[expkv] % ConTeXt -% \end{lstlisting} -% Both the \LaTeX\ package and the \ConTeXt\ module don't do more than -% \file{expkv.tex}, except calling |\ProvidesPackage| and setting things up such -% that \file{expkv.tex} will use |\ProvidesFile|, or printing some status -% information. The \ConTeXt\ support is not thoroughly tested, though (since I -% don't use \ConTeXt\ myself I don't know if there are additional pitfalls I -% wasn't aware of). -% -% In the \expkv\ family are other packages contained which provide additional -% functionality. Those packages currently are: -% \begin{description}[format=\expkvpkg] -% \item[def] -% a key-defining frontend for \expkv\ using a \kv\ syntax -% \item[cs] -% define expandable \kv\ macros using \expkv -% \item[opt] -% parse package and class options with \expkv -% \end{description} -% Note that while the package names are stylised with a vertical rule, their -% names are all lower case with a hyphen (\emph{e.g.}, \file{expkv-def}). -% -% A list of concise comparisons to other \kv\ packages is contained in -% \autoref{sec:cmp}. -% -% \subsection{Setting up Keys}\label{sec:define} -% \expkv\ provides a rather simple approach to setting up keys, similar to -% \pkg{keyval}. However there is an auxiliary package named \expkvd\ which -% provides a more sophisticated interface, similar to well established packages -% like \pkg{pgfkeys} or \pkg{l3keys}. -% -% Keys in \expkv\ (as in almost all other \kv\ implementations) belong to a -% \emph{set} such 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 values and keys that don't (the latter -% are called |NoVal| keys by \expkv), but both can have the same name (on the -% user level). -% -% The following macros are available to define new keys. Those macros containing -% ``|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), -% prefixes allowed for |\let| can prefix those with ``|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 \key\ taking a value in a \set\ to expand to \meta{code}. In -% \meta{code} you can use |#1| to refer to the given value. -% \end{function} -% \example Define |text| in |foo| to store the value inside |\foo@text|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \protected\long\ekvdef{foo}{text}{\def\foo@text{#1}} -% \end{lstlisting} -% -% \begin{function}{\ekvdefNoVal} -% \begin{syntax} -% \cs{ekvdefNoVal}\marg{set}\marg{key}\marg{code} -% \end{syntax} -% Defines a no value taking \key\ in a \set\ to expand to \meta{code}. -% \end{function} -% \example Define |bool| in |foo| to set |\iffoo@bool| to |true|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \protected\ekvdefNoVal{foo}{bool}{\foo@booltrue} -% \end{lstlisting} -% -% \begin{function}{\ekvlet} -% \begin{syntax} -% \cs{ekvlet}\marg{set}\marg{key}\meta{cs} -% \end{syntax} -% Let the value taking \key\ in \set\ to \meta{cs}, there are no checks on -% \meta{cs} enforced, but the code should expect the value as a single braced -% argument directly following it. -% \end{function} -% \example Let |cmd| in |foo| do the same as |\foo@cmd|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvlet{foo}{cmd}\foo@cmd -% \end{lstlisting} -% -% \begin{function}{\ekvletNoVal} -% \begin{syntax} -% \cs{ekvletNoVal}\marg{set}\marg{key}\meta{cs} -% \end{syntax} -% Let the no value taking \key\ in \set\ to \meta{cs}, it is not checked -% whether \meta{cs} exists or that it takes no parameter. -% \end{function} -% \example See above. -% -% \begin{function}{\ekvletkv} -% \begin{syntax} -% \cs{ekvletkv}\marg{set}\marg{key}\marg{set2}\marg{key2} -% \end{syntax} -% Let the \key\ in \set\ to \meta{key2} in \meta{set2}, it is not checked -% whether that second key exists (but take a look at -% \cs[no-index]{ekvifdefined}). -% \end{function} -% \example Let |B| in |bar| be an alias for |A| in |foo|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvletkv{bar}{B}{foo}{A} -% \end{lstlisting} -% -% \begin{function}{\ekvletkvNoVal} -% \begin{syntax} -% \cs{ekvletkvNoVal}\marg{set}\marg{key}\marg{set2}\marg{key2} -% \end{syntax} -% Let the \key\ in \set\ to \meta{key2} in \meta{set2}, it is not checked -% whether that second key exists (but take a look at -% \cs[no-index]{ekvifdefinedNoVal}). -% \end{function} -% \example See above. -% -% \begin{function}{\ekvdefunknown} -% \begin{syntax} -% \cs{ekvdefunknown}\marg{set}\marg{code} -% \end{syntax} -% By default an error will be thrown if an unknown \key\ is encountered. With -% this macro you can define \meta{code} that will be executed for a given -% \set\ when an unknown \key\ with a \val\ was encountered instead of throwing -% an error. You can refer to the given \val\ with |#1| and to the unknown -% \key's name with |#2| in \meta{code}.\footnotemark{} -% |\ekvdefunknown| and |\ekvredirectunknown| are mutually exclusive, -% you can't use both. -% \end{function} -% \footnotetext{That order is correct, this way the code is faster.} -% \example Also search |bar| for undefined keys of set |foo|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \long\ekvdefunknown{foo}{\ekvset{bar}{#2={#1}}} -% \end{lstlisting} -% 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 -% |\ekvredirectunknown|. -% -% \begin{function}{\ekvdefunknownNoVal} -% \begin{syntax} -% \cs{ekvdefunknownNoVal}\marg{set}\marg{code} -% \end{syntax} -% As already explained for \cs[no-index]{ekvdefunknown}, \expkv\ would throw -% an error when encountering an unknown \key. With this you can instead let it -% execute \meta{code} if an unknown |NoVal| \key\ was encountered. You can -% refer to the given \key\ with |#1| in \meta{code}. -% |\ekvdefunknownNoVal| and |\ekvredirectunknownNoVal| are mutually exclusive, -% you can't use both. -% \end{function} -% \example Also search |bar| for undefined keys of set |foo|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvdefunknownNoVal{foo}{\ekvset{bar}{#1}} -% \end{lstlisting} -% -% \begin{function}{\ekvredirectunknown} -% \begin{syntax} -% \cs{ekvredirectunknown}\marg{set}\marg{set-list} -% \end{syntax} -% This is a short cut to set up a special |\ekvdefunknown| for \set\ that will -% check each set in the comma separated \meta{set-list} for the unknown \key. -% You can't use prefixes (so no |\long| or |\protected|) with this macro, the -% resulting unknown-key handler will always be |\long|. The first set in the -% \meta{set-list} has highest priority. Once the \key\ is found the remaining -% sets are discarded, if the \key\ isn't found in any set an error will be -% thrown eventually. Note that the error messages are affected by the use of -% this macro, in particular, it isn't checked whether a |NoVal| key of the -% same name is defined in order to throw an |unwanted value| error. -% |\ekvdefunknown| and |\ekvredirectunknown| are mutually exclusive, -% you can't use both. -% \end{function} -% \example For every key not defined in the set |foo| also search the sets |bar| -% and |baz|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvredirectunknown{foo}{bar, baz} -% \end{lstlisting} -% -% \begin{function}{\ekvredirectunknownNoVal} -% \begin{syntax} -% \cs{ekvredirectunknownNoVal}\marg{set}\marg{set-list} -% \end{syntax} -% This behaves just like |\ekvredirectunknown| and does the same but for the -% |NoVal| keys. Again no prefixes are supported. Note that the error messages -% are affected by the use of this macro, in particular, it isn't checked -% whether a normal key of the same name is defined in order to throw a -% |missing value| error. -% |\ekvdefunknownNoVal| and |\ekvredirectunknownNoVal| are mutually exclusive, -% you can't use both. -% \end{function} -% \example See above. -% -% \subsection{Parsing Keys} -% -% \begin{function}{\ekvset} -% \begin{syntax} -% \cs{ekvset}\marg{set}\{\kv,\ldots\} -% \end{syntax} -% Splits \kv\ pairs on commas. From both \key\ and \val\ up to one space is -% stripped from both ends, if then only a braced group remains the braces are -% stripped as well. So |\ekvset{foo}{bar=baz}| and -% |\ekvset{foo}{ {bar}= {baz} }| will both do -% \texttt{\cs[no-index]{\meta{foobarcode}}\{baz\}}, so you can hide commas, -% equal signs and spaces at the ends of either \key\ or \val\ by putting -% braces around them. If you omit the equal sign the code of the key created -% with the |NoVal| variants described in \autoref{sec:define} will be -% executed. If \kv\ contains more than a single unhidden equal sign, it will -% be split at the first one and the others are considered part of the value. -% |\ekvset| should be nestable. -% -% |\ekvset| is currently \emph{not} alignment safe.\footnotemark\ As a result, -% key names and values that contain an |&| must be wrapped in braces when -% |\ekvset| is used inside an alignment (like \LaTeXe's |tabular| environment) -% or you have to create a wrapper that ensures an alignment safe context. -% \end{function} -% \footnotetext{This might change in the future, I've not decided yet.} -% \example Parse |key=arg, key| in the set |foo|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvset{foo}{key=arg, key} -% \end{lstlisting} -% -% \begin{function}{\ekvsetSneaked} -% \begin{syntax} -% \cs{ekvsetSneaked}\marg{set}\marg{sneak}\{\kv,\ldots\} -% \end{syntax} -% Just like \cs[no-index]{ekvset}, this macro parses the \kv\ pairs within the -% given \set. But \cs[no-index]{ekvsetSneaked} will behave as if -% \cs[no-index]{ekvsneak} has been called with \meta{sneak} as its argument as -% the first action. -% \end{function} -% \example Parse |key=arg, key| in the set |foo| with |\afterwards| sneaked out: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvsetSneaked{foo}{\afterwards}{key=arg, key} -% \end{lstlisting} -% -% \begin{function}{\ekvsetdef} -% \begin{syntax} -% \cs{ekvsetdef}\meta{cs}\marg{set} -% \end{syntax} -% With this function you can define a shorthand macro \meta{cs} to parse keys -% of a specified \set. It is always defined |\long|, but if you need to you -% can also prefix it with |\global|. The resulting macro is faster than but -% else equivalent to the idiomatic definition:\par -% \texttt -% {\string\long\string\def\meta{cs}\#1\{\string\ekvset\marg{set}\{\#1\}\}} -% \end{function} -% \example Define the macro |\foosetup| to parse keys in the set |foo| and use -% it to parse |key=arg, key|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvsetdef\foosetup{foo} -% \foosetup{key=arg, key} -% \end{lstlisting} -% -% \begin{function}{\ekvsetSneakeddef} -% \begin{syntax} -% \cs{ekvsetSneakeddef}\meta{cs}\marg{set} -% \end{syntax} -% Just like \cs[no-index]{ekvsetdef} this defines a shorthand macro \meta{cs}, -% but this macro will make it a shorthand for \cs[no-index]{ekvsetSneaked}, -% meaning that \meta{cs} will take two arguments, the first being stuff that -% should be given to \cs[no-index]{ekvsneak} and the second the \kv\ list. The -% resulting macro is faster than but else equivalent to the idiomatic -% definition:\par -% \texttt -% {% -% \string\long\string\def\meta{cs}\#1\#2\{^^A -% \string\ekvsetSneaked\marg{set}\{\#1\}\{\#2\}\}^^A -% } -% \end{function} -% \example 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{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvsetSneakeddef\foothings{foo} -% \foothings{\afterwards}{key=arg, key} -% \end{lstlisting} -% -% \begin{function}{\ekvsetdefSneaked} -% \begin{syntax} -% \cs{ekvsetdefSneaked}\meta{cs}\marg{set}\marg{sneaked} -% \end{syntax} -% And this one behaves like \cs[no-index]{ekvsetSneakeddef} but with a fixed -% \meta{sneaked} argument. So the resulting macro is faster than but else -% equivalent to the idiomatic definition:\par -% \texttt -% {% -% \string\long\string\def\meta{cs}\#1\{^^A -% \string\ekvsetSneaked\marg{set}\marg{sneaked}\{\#1\}\}^^A -% } -% \end{function} -% \example 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{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvsetdefSneaked\barthing{bar}{\afterwards} -% \barthing{key=arg, key} -% \end{lstlisting} -% -% \begin{function}{\ekvparse} -% \begin{syntax} -% \cs{ekvparse}\marg{code1}\marg{code2}\{\kv,\ldots\} -% \end{syntax} -% This macro parses the \kv\ pairs and provides those list elements which are -% only keys as an argument to \meta{code1}, and those which are a \kv\ pair -% to \meta{code2} as two arguments. It is fully expandable as well and returns -% each element of the parsed list in |\unexpanded|, which has no effect -% outside of an |\expanded| or |\edef| context. -% Also |\ekvparse| expands in exactly two steps of expansion. -% You can use multiple tokens in \meta{code1} and \meta{code2} or just a -% single control sequence name. In both cases the found \key\ and \val\ are -% provided as a brace group following them. -% -% |\ekvparse| is alignment safe, meaning that you don't have to take any -% precautions if it is used inside an alignment context (like \LaTeXe's -% |tabular| environment) and any key or value can contain an |&|. -% -% |\ekvbreak|, |\ekvsneak|, and |\ekvchangeset| and their relatives don't work -% in |\ekvparse|. It is analogue to \pkg{expl3}'s |\keyval_parse:NNn|, but -% not with the same parsing rules -- |\keyval_parse:NNn| throws an error on -% multiple equal signs per \kv\ pair and on empty \key\ names in a \kv\ pair, -% both of which |\ekvparse| doesn't deal with. -% \end{function} -% \example -% \begin{lstlisting} -% \ekvparse{\handlekey{S}}{\handlekeyval{S}}{foo = bar, key, baz={zzz}} -% \end{lstlisting} -% would be equivalent to -% \begin{lstlisting} -% \handlekeyval{S}{foo}{bar}\handlekey{S}{key}\handlekeyval{S}{baz}{zzz} -% \end{lstlisting} -% and afterwards |\handlekey| and |\handlekeyval| would have to further handle -% the \key. There are no macros like these two contained in \expkv, you have -% to set them up yourself if you want to use |\ekvparse| (of course the names -% might differ). If you need the results of |\ekvparse| as the argument for -% another macro, you should use |\expanded|, or expand |\ekvparse| twice, as -% only then the input stream will contain the output above: -% \begin{lstlisting} -% \expandafter\parse\expanded{\ekvparse\k\kv{foo = bar, key, baz={zzz}}} -% \end{lstlisting} -% or -% \begin{lstlisting} -% \expandafter\expandafter\expandafter -% \parse\ekvparse\k\kv{foo = bar, key, baz={zzz}} -% \end{lstlisting} -% would both expand to -% \begin{lstlisting} -% \parse\kv{foo}{bar}\k{key}\kv{baz}{zzz} -% \end{lstlisting} -% -% \subsection{Other Macros} -% -% \expkv\ provides some other macros which might be of interest. -% -% \begin{function}{\ekvVersion,\ekvDate} -% These two macros store the version and date of the package. -% \end{function} -% -% \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} -% \example Check whether the key |special| is already defined in set |foo|, if -% it isn't input a file that contains more key definitions: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvifdefined{foo}{special}{}{\input{foo.morekeys.tex}} -% \end{lstlisting} -% -% \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} -% \example Check whether the set |VeRyUnLiKeLy| is already defined, if so throw -% an error, else do nothing: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvifdefinedset{VeRyUnLiKeLy} -% {\errmessage{VeRyUnLiKeLy already defined}}{} -% \end{lstlisting} -% -% \begin{function}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak} -% \begin{syntax} -% \cs{ekvbreak}\marg{after} -% \end{syntax} -% Gobbles the remainder of the current |\ekvset| macro and its argument list -% and reinserts \meta{after}. So this can be used to break out of |\ekvset|. -% The first variant will also gobble anything that has been sneaked out using -% |\ekvsneak| or |\ekvsneakPre|, while |\ekvbreakPreSneak| will put -% \meta{after} before anything that has been smuggled and |\ekvbreakPostSneak| -% will put \meta{after} after the stuff that has been sneaked out. -% \end{function} -% \example 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{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvdefNoVal{foo}{abort}{\ekvbreak{\foo@aborted}} -% \ekvdef{foo}{abort}{\ekvbreak{\foo@aborted@with{#1}}} -% \end{lstlisting} -% -% \begin{function}{\ekvsneak,\ekvsneakPre} -% \begin{syntax} -% \cs{ekvsneak}\marg{after} -% \end{syntax} -% Puts \meta{after} after the effects of |\ekvset|. The first variant will put -% \meta{after} after any other tokens which might have been sneaked before, -% while |\ekvsneakPre| will put \meta{after} before other smuggled stuff. -% This reads and reinserts the remainder of the current |\ekvset| macro and -% its argument list to do its job. After |\ekvset| has parsed the entire \kv\ -% list everything that has been |\ekvsneak|ed will be left in the input -% stream. A small usage example is shown in \autoref{sec:sneakex}. -% \end{function} -% \example Define a key |secret| in the set |foo| that will sneak out -% |\foo@secretly@sneaked|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo@secretly@sneaked}} -% \end{lstlisting} -% -% \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 -% |\ekvset| call, that call behaves as if it was called with -% \texttt{\cs[no-index]{ekvset}\marg{new-set}}. It is comparable to using -% \texttt{\meta{key}/.cd} in \pkg{pgfkeys}. -% \end{function} -% \example Define a key |cd| in set |foo| that will change to another set as -% specified in the value, if the set is undefined it'll stop the parsing and -% throw an error as defined in the macro |\foo@cd@error|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \ekvdef{foo}{cd} -% {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo@cd@error}}} -% \end{lstlisting} -% -% \begin{function}{\ekvoptarg} -% \begin{syntax} -% \cs{ekvoptarg}\marg{next}\marg{default} -% \end{syntax} -% This macro will check for a following optional argument in brackets (|[]|) -% expandably. After the optional argument there has to be a mandatory one. The -% code in \meta{next} should expect two arguments (the processed optional -% argument and the mandatory one). 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 being read once -- if -% it was already wrapped it is effectively unchanged). -% \end{function} -% |\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, the optional argument has to use nested -% braces like so: |[{arg]ument}]|. -% \example Say we have a macro that should take an optional argument defaulting -% to 1: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \newcommand\foo{\ekvoptarg\@foo{1}} -% \newcommand\@foo[2]{Mandatory: #2\par Optional: #1} -% \end{lstlisting} -% -% \begin{function}{\ekvoptargTF} -% \begin{syntax} -% \cs{ekvoptargTF}\marg{true}\marg{false} -% \end{syntax} -% This macro is similar to |\ekvoptarg|, but will result in -% \meta{true}\marg{optional}\meta{mandatory} or \meta{false}\marg{mandatory} -% instead of placing a default value. -% \end{function} -% |\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 |\ekvoptarg|. -% \example Say we have a macro that should behave differently depending on -% whether there was an optional argument or not. This could be done with: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \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.} -% \end{lstlisting} -% -% \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 -% \texttt -% {^^A -% \cs[no-index]{unexpanded}\hskip0pt -% \{\hskip0pt -% \meta{code}\hskip0pt -% \marg{item}\hskip0pt -% \}^^A -% } -% for each list item in the input stream. Blank elements are ignored (if you -% need a blank element it should be given as |{}|). It supports both active -% commas and commas of category other. You could consider it as a watered down -% version of |\ekvparse|. However it is not alignment safe, which you could -% achieve by nesting it in |\expanded| (since the braces around the argument -% of |\expanded| will hide |&|s from \TeX's alignment parsing). -% \end{function} -% \example The following splits a comma separated list and prints it in a -% typewriter font with parentheses around each element.\\ -% \begin{minipage}{.75\linewidth} -% \begin{lstlisting} -% \newcommand*\myprocessor[1]{\texttt{(#1)}} -% \ekvcsvloop\myprocessor{abc,def,ghi}\par -% \ekvcsvloop\myprocessor{1,,2,,3,,4}\par -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \newcommand*\myprocessor[1]{\texttt{(#1)}} -% \ekvcsvloop\myprocessor{abc,def,ghi}\par -% \ekvcsvloop\myprocessor{1,,2,,3,,4}\par -% \end{exresult} -% -% \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 for \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 plain \TeX\ 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. -% \example Say we set up a small calculation which works with user input. In our -% calculation we need a division, so have to watch out for division by zero. If -% we detect such a case we throw an error and do the recovery by using the -% biggest integer allowed in \TeX\ as the result. -% \begin{lstlisting} -% \newcommand*\mydivision[2] -% {% -% \the\numexpr -% \ifnum\numexpr#2=0 % space here on purpose -% \ekverr{my}{division by 0. Setting result to 2147483647.}% -% 2147483647% -% \else -% (#1)/(#2)% -% \fi -% \relax -% } -% $(10+5)/(3-3)\approx\mydivision{10+5}{3-3}$ -% \end{lstlisting} -% If that code gets executed the following will be the terminal output -% \begin{verbatim} -% Runaway argument? -% ! my Error: division by 0. Setting result to 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.15 $(10+5)/(3-3)\approx\mydivision{10+5}{3-3} -% $ -% ? -% \end{verbatim} -% and the output would contain -% \exres{$(10+5)/(3-3)\approx2147483647$} -% if we continued the \TeX\ run at the prompt. -% -% \bigskip -% -% \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 that correspond to a key in a set are build with -% these macros. The name is built from two blocks, one that is formatting the -% \set\ name (|\ekv@name@set|) and one for formatting the \key\ name -% (|\ekv@name@key|). To get the actual name the argument to |\ekv@name@key| -% must be |\detokenize|d. Both blocks are put together (with the necessary -% |\detokenize|) by |\ekv@name|. -% For |NoVal| keys an additional |N| gets appended irrespective of these -% macros' definition, so their name is -% \texttt{\cs[no-index]{ekv@name}\marg{set}\marg{key}N}.\par -% 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} -% \example Execute the callback of the |NoVal| key |key| in set |foo|: -% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \csname\ekv@name{foo}{key}N\endcsname -% \end{lstlisting} -% -% \subsection{Examples} -% -% \subsubsection{Standard Use-Case} -% -% Say we have a macro for which we want to create a \kv\ interface. The macro -% has a parameter, which is stored in the dimension |\ourdim| having a default -% value from its initialisation. Now we want to be able to change that dimension -% with the |width| key to some specified value. For that we'd do -% \begin{lstlisting} -% \newdimen\ourdim -% \ourdim=150pt -% \protected\ekvdef{our}{width}{\ourdim=#1\relax} -% \end{lstlisting} -% as you can see, we use the set |our| here. We want the key to behave different -% if no value is specified. In that case the key should not use its initial -% value, but be smart and determine the available space from -% \cs[no-index]{hsize}, so we also define -% \begin{lstlisting} -% \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize} -% \end{lstlisting} -% Now we set up our macro to use this \kv\ interface -% \begin{lstlisting} -% \protected\def\ourmacro#1% -% {\begingroup\ekvset{our}{#1}\the\ourdim\endgroup} -% \end{lstlisting} -% Finally we can use our macro like in the following\\ -% \begin{minipage}{.5\linewidth} -% \begin{lstlisting} -% \ourmacro{}\par -% \ourmacro{width}\par -% \ourmacro{width=5pt}\par -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.5\linewidth} -% \newdimen\ourdim\ourdim=150pt -% \protected\ekvdef{our}{width}{\ourdim=#1\relax}^^A -% \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize}^^A -% \protected\def\ourmacro#1^^A -% {\begingroup\ekvset{our}{#1}\the\ourdim\endgroup}^^A -% \ourmacro{}\par -% \ourmacro{width}\par -% \ourmacro{width=5pt}\par -% \end{exresult} -% -% \paragraph{The same keys using \protect\expkvd} -% Using \expkvd\ we can set up the equivalent key using a \kv\ interface, after -% the following we could use |\ourmacro| in the same way as above. \expkvd\ will -% allocate and initialise |\ourdim| and define the |width| key |\protected| for -% us, so the result will be exactly the same -- with the exception that the -% default will use |\ourdim=.9\hsize\relax| instead. -% \begin{lstlisting} -% \input expkv-def % or \usepackage{expkv-def} -% \ekvdefinekeys{our} -% { -% dimen width = \ourdim, -% qdefault width = .9\hsize, -% initial width = 150pt -% } -% \end{lstlisting} -% -% \subsubsection{A Macro to Draw Rules} -% -% Another small example could be a \kv\ driven |\rule| alternative, because I -% keep forgetting the correct order of its arguments. First we define the keys -% (and initialize the macros used to store the keys): -% \begin{lstlisting} -% \makeatletter -% \newcommand*\myrule@ht{1ex} -% \newcommand*\myrule@wd{0.1em} -% \newcommand*\myrule@raise{\z@} -% \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{lstlisting} -% Then we define a macro to change the defaults outside of |\myrule| and -% |\myrule| itself: -% \begin{lstlisting} -% \ekvsetdef\myruleset{myrule} -% \newcommand*\myrule[1][] -% {\begingroup\myruleset{#1}\myrule@out\endgroup} -% \end{lstlisting} -% And finally the output: -% \begin{lstlisting} -% \newcommand*\myrule@out{\rule[\myrule@raise]\myrule@wd\myrule@ht} -% \makeatother -% \end{lstlisting} -% And we can use it:\\ -% \begin{minipage}{.5\linewidth} -% \begin{lstlisting} -% a\myrule\par -% a\myrule[ht=2ex,lower=.5ex]\par -% \myruleset{wd=5pt} -% a\myrule -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.5\linewidth} -% \makeatletter -% \newcommand*\myrule@ht{1ex} -% \newcommand*\myrule@wd{0.1em} -% \newcommand*\myrule@raise{\z@} -% \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}} -% \ekvsetdef\myruleset{myrule} -% \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule@out\endgroup} -% \newcommand*\myrule@out{\rule[\myrule@raise]\myrule@wd\myrule@ht} -% \makeatother -% a\myrule\par -% a\myrule[ht=2ex,lower=.5ex]\par -% \myruleset{wd=5pt} -% a\myrule -% \end{exresult} -% -% -% \subsubsection{An Expandable \kv\ Macro Using \cs[no-index]{ekvsneak}} -% \label{sec:sneakex} -% -% 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, -% |\ekvset| or |\ekvparse|. For this example we also want to show the -% usage of |\ekvsneak|, hence we'll choose |\ekvset|. And we'll have to use -% |\ekvset| such that it builds a parsable list for our macro internals. To -% gain back control after |\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 -% |\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 the following will use |\ekvsneakPre| for the -% user-level keys. If we wanted some key for which the first usage should be the -% binding one we would use |\ekvsneak| instead for that key. -% -% 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 the parsing list terminator. -% -% Ordering the keys can be done simply by searching for a specific token for -% each argument which acts like a flag, so our sneaked out values will include -% specific tokens acting as markers. -% -% Now that we have answers for our technical problems, we have to decide what -% our example macro should do. How about we define a macro that calculates the -% sine of a number and rounds that to a specified precision? As a small extra -% this macro should understand input in radian and degree and the used -% trigonometric function should be selectable as well. For the hard part of this -% task (expandably evaluating trigonometric functions) we'll use the \pkg{xfp} -% package. -% -% 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 defaults -% right before it. The user macro |\sine| uses |\ekvoptargTF| to check for the -% optional argument short cutting to the final step if no optional argument was -% found. This way we safe some time in this case, though we have to specify the -% default values twice. -% -% \begin{lstlisting} -% \RequirePackage{xfp} -% \makeatletter -% \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}} -% \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}} -% \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}} -% \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}} -% \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine@rnd}} -% \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}} -% \newcommand*\sine@args[2] -% {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}} -% \end{lstlisting} -% -% Now we need to define some internal macros to extract the value of each key's -% last usage (remember that this will be the group after the first special -% flag-token). For that we use one delimited macro per key. -% \begin{lstlisting} -% \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{lstlisting} -% After the macros |\sine@rnd|, |\sine@deg|, and |\sine@f| the macro -% |\sine@final| will see -% \texttt -% {^^A -% \cs[no-index]{sine@final}^^A -% \marg{f}\marg{degree/radian}\marg{round}\marg{num}^^A -% }. -% Now |\sine@final| has to expandably deal with those arguments such that the -% |\fpeval| macro of \pkg{xfp} gets the correct input. Luckily this is pretty -% straight forward in this example. In |\fpeval| the trigonometric functions -% have names such as |sin| or |cos| and the degree taking variants |sind| or -% |cosd|. And since the |degree| key puts a |d| in |#2| and the |radian| key -% leaves |#2| empty all we have to do to get the correct function name is stick -% the two together. -% \begin{lstlisting} -% \newcommand*\sine@final[4]{\fpeval{round(#1#2(#4),#3)}} -% \makeatother -% \end{lstlisting} -% Let's test our macro:\\ -% \begin{minipage}{.75\linewidth} -% \begin{lstlisting}[gobble=4] -% \sine{60}\par -% \sine[round=10]{60}\par -% \sine[f=cos,radian]{pi}\par -% \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval} -% \end{lstlisting} -% \end{minipage}^^A -% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} -% \makeatletter -% \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}}^^A -% \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}}^^A -% \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}^^A -% \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}^^A -% \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine@rnd}}^^A -% \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}} -% \newcommand*\sine@args[2] -% {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}} -% \def\sine@rnd#1\rnd#2#3\sine@stop{\sine@deg#1#3\sine@stop{#2}}^^A -% \def\sine@deg#1\deg#2#3\sine@stop{\sine@f#1#3\sine@stop{#2}}^^A -% \def\sine@f#1\f#2#3\sine@stop{\sine@final{#2}}^^A -% \newcommand*\sine@final[4]{\fpeval{round(#1#2(#4),#3)}}^^A -% \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{exresult} -% -% \paragraph{The same macro using \protect\expkvc} -% Using \expkvc\ we can set up something equivalent with a bit less code. The -% implementation chosen in \expkvc\ is more efficient than the example above and -% way easier to code for the user. -% \begin{lstlisting} -% \makeatletter -% \newcommand*\sine{\ekvoptargTF\sine@a{\sine@b{sin}{d}{3}}} -% \ekvcSplitAndForward\sine@a\sine@b -% { -% f=sin, -% unit=d, -% round=3, -% } -% \ekvcSecondaryKeys\sine@a -% { -% nmeta degree={unit=d}, -% nmeta radian={unit={}}, -% } -% \newcommand*\sine@b[4]{\fpeval{round(#1#2(#4),#3)}} -% \makeatother -% \end{lstlisting} -% The resulting macro will behave just like the one previously defined, but will -% have an additional |unit| key, since in \expkvc\ every argument must have a -% value taking key which defines it. -% -% -% \subsection{Error Messages} -% -% \expkv\ should only send messages in case of errors, there are no warnings and -% no info messages. In this subsection those errors are listed. -% -% \subsubsection{Load Time} -% -% \file{expkv.tex} checks whether \eTeX\ and the |\expanded| primitive are -% available. If it isn't, an error will be thrown using |\errmessage|: -% \begin{lstlisting} -% ! expkv Error: e-TeX and \expanded required. -% \end{lstlisting} -% -% \subsubsection{Defining Keys} -% -% If you get any error from \expkv\ while you're trying to define a key, the -% definition will be aborted and gobbled. -% -% If you try to define a key with an empty set name you'll get: -% \begin{lstlisting} -% ! expkv Error: empty set name not allowed. -% \end{lstlisting} -% -% Similarly, if you try to define a key with an empty key name: -% \begin{lstlisting} -% ! expkv Error: empty key name not allowed. -% \end{lstlisting} -% -% Both of these messages are done in a way that doesn't throw additional errors -% due to |\global|, |\long|, etc., not being used correctly if you prefixed one -% of the defining macros. -% -% \subsubsection{Using Keys} -% -% This subsubsection contains the errors thrown during |\ekvset|. The errors are -% thrown in an expandable manner using |\ekverr|. In the following messages -% \lstinline|<key>| gets replaced with the problematic key's name, and -% \lstinline|<set>| with the corresponding set. If any errors during \kv\ -% handling are encountered, the entry in the comma separated list will be -% omitted after the error is thrown and the next \kv\ pair will be parsed. -% -% If you're using an undefined key you'll get: -% \begin{lstlisting} -% Runaway argument? -% ! expkv Error: unknown key `<key>' in set `<set>' -% \end{lstlisting} -% -% If you're using a key for which only a normal version and no |NoVal| version -% is defined, but don't provide a value, you'll get: -% \begin{lstlisting} -% Runaway argument? -% ! expkv Error: missing value for `<key>' in set `<set>' -% \end{lstlisting} -% -% If you're using a key for which only a |NoVal| version and no normal version -% is defined, but provide a value, you'll get: -% \begin{lstlisting} -% Runaway argument? -% ! expkv Error: unwanted value for `<key>' in set `<set>' -% \end{lstlisting} -% -% If you're using an undefined key in a set for which |\ekvredirectunknown| was -% used, and the key isn't found in any of the other sets as well, you'll get: -% \begin{lstlisting} -% Runaway argument? -% ! expkv Error: no key `<key>' in sets {<set1>}{<set2>}... -% \end{lstlisting} -% -% If you're using an undefined |NoVal| key in a set for which -% |\ekvredirectunknownNoVal| was used, and the key isn't found in any of the -% other sets as well, you'll get: -% \begin{lstlisting} -% Runaway argument? -% ! expkv Error: no NoVal key `<key>' in sets {<set1>}{<set2>}... -% \end{lstlisting} -% -% If you're using a set for which you never executed one of the defining macros -% from \autoref{sec:define} you'll get a low level \TeX\ error, as that isn't -% actively tested by the parser (and hence will lead to undefined behaviour and -% not be gracefully ignored). The error will look like -% \begin{lstlisting}[language={}] -% ! Missing \endcsname inserted. -% <to be read again> -% \! expkv Error: Set `<set>' undefined. -% \end{lstlisting} -% -% \subsection{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 GitHub if you have an account there: -% \url{https://github.com/Skillmon/tex_expkv} -% -% \subsection{Comparisons}\label{sec:cmp} -% -% This subsection 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), 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{lstlisting} -% \protected\ekvdef{test}{height}{\def\myheight{#1}} -% \ekvsetdef\expkvtest{test} -% \expkvtest{ height = 6 } -% \end{lstlisting} -% 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 -% the two tests (for empty key names and only a single equal sign) which are -% omitted. -% -% \paragraph{\pkg{keyval}} is about \SIrange{30}{40}{\percent} faster and has a -% comparable feature set (actually a bit smaller since \expkv\ supports -% unknown-key handlers and redirection to other sets) just a slightly different -% way how it handles keys without values. 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 bug, 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 argument isn'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{lstlisting}[belowskip=0pt] -% \setkeys{foo}{bar=baz} -% \setkeys{foo}{bar= {baz}} -% \setkeys{foo}{bar={ baz}} % should be ` baz' -% \setkeys{foo}{bar={{baz}}} % should be `{baz}' -% \end{lstlisting} -% -% \paragraph{\pkg{xkeyval}} is roughly twenty times slower, but it provides more -% functionality, e.g., it has choice keys, boolean keys, and so on. 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. -% -% \paragraph{\pkg{ltxkeys}} is no longer compatible with the \LaTeX\ kernel -% starting with the release 2020-10-01. It is over 380 times slower -- 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. 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). -% -% \paragraph{\pkg{l3keys}} is around four and a half times slower, 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 is pretty much bound to \pkg{expl3} code. Whether that's a drawback is up -% to you. -% -% \paragraph{\pkg{pgfkeys}} is around \num{2.7} times slower for one key if one -% uses the |/|\meta{path}|/.cd| syntax and almost \SI{20}{\percent} slower if -% one uses |\pgfqkeys|, but has an \emph{enormous} feature set. -% To get the best performance |\pgfqkeys| was used in the benchmark. This -% reduces the overhead for setting the base directory of the benchmark keys by -% about \SI{43}{\ops} (so 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 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. -% -% \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is about \num{4.4} times -% slower, but it works even if commas and equals have category codes different -% from 12 (just as some other packages in this list). Else the features of the -% keys are equal to those of \pkg{keyval}, the parser has more features, though. -% -% \paragraph{\pkg{options}} is \num{1.7} times slower for only a single value. -% It has a much bigger feature set. Unfortunately it also suffers from the -% premature unbracing bug \pkg{keyval} has. -% -% \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 adds -% functionality so that it can be used more like most of the other \kv\ -% packages. It has problems with stripping braces and spaces in a hard to -% predict manner just like \pkg{keyval}. Also, while it tries to be robust -% against category code changes of commas and equal signs, the used mechanism -% fails if the \kv\ list already got tokenised. 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. For up to three keys, \expkv\ is a bit faster, -% for more keys \pkg{simplekv} takes the lead. -% -% \paragraph{\protect\yax} is over twenty times slower. 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, -% it's just atypical). It has the premature unbracing bug, too. Also somehow -% loading \yax\ broke \pkg{options} for me. The tested definition was: -% \begin{lstlisting}[belowskip=0pt] -% \usepackage{yax} -% \defactiveparameter yax {\storevalue\myheight yax:height } % setup -% \setparameterlist{yax}{ height = 6 } % benchmark -% \end{lstlisting} -% -% \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 the \href{https://github.com/Skillmon/tex_expkv}{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.742 & 1.486 & 7.254 & \yes & \yes & 2014-10-28 \\ -% \expkv & 19.701 & 2.169 & 6.592 & \no & \no & 2020-10-10 \\ -% simplekv & 18.334 & 6.971 & 17.710 & \yes & \yes & 2020-04-27 \\ -% pgfkeys & 24.274 & 1.725 & 10.650 & \yes & \yes & 2020-09-05 \\ -% options & 23.600 & 15.638 & 20.830 & \yes & \yes & 2015-03-01 \\ -% kvsetkeys & {\fnsym} & {\fnsym} & 40.290 & \no & \no & 2019-12-15 \\ -% l3keys & 71.309 & 33.131 & 31.590 & \no & \no & 2020-09-24 \\ -% xkeyval & 253.563 & 202.246 & 168.300 & \yes & \yes & 2014-12-03 \\ -% \yax & 421.853 & 157.003 & 114.700 & \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{8.240}$, $p_1=\num{44.862}$, and $p_0=\num{60.793}$. 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} -% -% \subsection{License} -% -% Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\ -% Jonathan P. Spratte -% -% \medskip\noindent -% 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: -% -% \url{http://www.latex-project.org/lppl.txt} -% -% \noindent -% This work is ``maintained'' (as per LPPL maintenance status) by -% -% \mbox{Jonathan P. Spratte}. -% -% \end{documentation}^^A=<< -% -% \begin{implementation}^^A>>= -% -% \clearpage -% -% \section{Implementation}^^A>>= -%^^A the LaTeX package >>= -% \subsection{The \LaTeX\ Package} -% First we set up the \LaTeX\ package. That one doesn't really do much except -% |\input|ting the generic code and identifying itself as a package. -% \gobbledocstriptag -%<*pkg> -% \begin{macrocode} -\def\ekv@tmp - {% - \ProvidesFile{expkv.tex}% - [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]% - } -\input{expkv.tex} -\ProvidesPackage{expkv}% - [\ekvDate\space v\ekvVersion\space an expandable key=val implementation] -% \end{macrocode} -% \gobbledocstriptag -%</pkg> -%^^A=<< -%^^A the ConTeXt module >>= -% \subsection{The \ConTeXt\ module} -% This is pretty straight forward, we just have to change the error throwing -% mechanism for \ConTeXt\ (the approach taken for plain and \LaTeX\ breaks in -% \ConTeXt, effectively breaking \ConTeXt, dropping you in an interactive \TeX\ -% session with almost no means of escape). -% \gobbledocstriptag -%<*ctx> -% \begin{macrocode} -\writestatus{loading}{ConTeXt User Module / expkv} -\unprotect -\input expkv.tex -\long\def\ekv@err@collect#1\par#2% - {\directlua{tex.error[[\detokenize{#2} Error: #1]]}} -\writestatus{loading} - {ConTeXt User Module / expkv / Version \ekvVersion\space loaded} -\protect\endinput -% \end{macrocode} -% \gobbledocstriptag -%</ctx> -%^^A=<< -%^^A main file >>= -% \subsection{The Generic Code} -% The rest of this implementation will be the generic code. -% \gobbledocstriptag -%<*tex> -% -% We make sure that it's only input once: -% \begin{macrocode} -\expandafter\ifx\csname ekvVersion\endcsname\relax -\else - \expandafter\endinput -\fi -% \end{macrocode} -% -% Check whether \eTeX\ and |\expanded| are available -- \expkv\ requires \eTeX. -% \begin{macrocode} -\begingroup - \edef\ekvtmpa{\string\expanded} - \edef\ekvtmpb{\meaning\expanded} - \expandafter -\endgroup -\ifx\ekvtmpa\ekvtmpb - \expandafter\let\csname ekv@expanded\endcsname\expanded - \expandafter\let\csname ekv@unexpanded\endcsname\unexpanded -\else - \begingroup - \edef\ekvtmpa{\string\expanded} - \edef\ekvtmpb{\meaning\normalexpanded} - \expandafter - \endgroup - \ifx\ekvtmpa\ekvtmpb - \expandafter\let\csname ekv@expanded\endcsname\normalexpanded - \expandafter\let\csname ekv@unexpanded\endcsname\normalunexpanded - \else - \errmessage - {expkv Error: e-TeX and the \noexpand\expanded primitive required}% - \expandafter\endinput - \fi -\fi -% \end{macrocode} -% -% \begin{macro}{\ekvVersion,\ekvDate} -% We're on our first input, so lets store the version and date in a macro. -% \begin{macrocode} -\def\ekvVersion{1.9a} -\def\ekvDate{2021-09-20} -% \end{macrocode} -% \end{macro} -% -% If the \LaTeX\ format is loaded we want to be a good file and report back who -% we are, for this the package will have defined |\ekv@tmp| to use -% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. -% \begin{macrocode} -\csname ekv@tmp\endcsname -% \end{macrocode} -% -% Store the category code of |@| to later be able to reset it and change it to -% 11 for now. -% \begin{macrocode} -\expandafter\chardef\csname ekv@tmp\endcsname=\catcode`\@ -\catcode`\@=11 -% \end{macrocode} -% |\ekv@tmp| might later be reused to gobble any prefixes which might be -% provided to |\ekvdef| and similar in case the names are invalid, we just -% temporarily use it here as means to store the current category code of |@| to -% restore it at the end of the file, we never care for the actual definition of -% it. -% -% \begin{macro}[internal]{\ekv@if@lastnamedcs} -% If the primitive |\lastnamedcs| is available, we can be a bit faster than -% without it. So we test for this and save the test's result in this macro. -% \begin{macrocode} -\begingroup - \edef\ekv@tmpa{\string \lastnamedcs} - \edef\ekv@tmpb{\meaning\lastnamedcs} - \ifx\ekv@tmpa\ekv@tmpb - \def\ekv@if@lastnamedcs{\long\def\ekv@if@lastnamedcs##1##2{##1}} - \else - \def\ekv@if@lastnamedcs{\long\def\ekv@if@lastnamedcs##1##2{##2}} - \fi - \expandafter -\endgroup -\ekv@if@lastnamedcs -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@empty} -% Sometimes we have to introduce a token to prevent accidental brace stripping. -% This token would then need to be removed by |\@gobble| or similar. Instead we -% can use |\ekv@empty| which will just expand to nothing, that is faster than -% gobbling an argument. -% \begin{macrocode} -\def\ekv@empty{} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \@gobble,\@firstofone,\@firstoftwo,\@secondoftwo, -% \ekv@fi@gobble,\ekv@fi@firstofone,\ekv@fi@firstoftwo,\ekv@fi@secondoftwo, -% \ekv@gobble@mark,\ekv@gobbleto@stop,\ekv@gobble@from@mark@to@stop -% } -% Since branching tests are often more versatile than |\if...\else...\fi| -% constructs, we define helpers that are branching pretty fast. Also here are -% some other utility functions that just grab some tokens. The ones that are -% also contained in \LaTeX\ don't use the |ekv| prefix. Not all of the ones -% defined here are really needed by \expkv\ but are provided because packages -% like \expkvd\ or \expkvo\ need them (and I don't want to define them in each -% package which might need them). -% \begin{macrocode} -\long\def\@gobble#1{} -\long\def\@firstofone#1{#1} -\long\def\@firstoftwo#1#2{#1} -\long\def\@secondoftwo#1#2{#2} -\long\def\ekv@fi@gobble\fi\@firstofone#1{\fi} -\long\def\ekv@fi@firstofone\fi\@gobble#1{\fi#1} -\long\def\ekv@fi@firstoftwo\fi\@secondoftwo#1#2{\fi#1} -\long\def\ekv@fi@secondoftwo\fi\@firstoftwo#1#2{\fi#2} -\def\ekv@gobble@mark\ekv@mark{} -\long\def\ekv@gobbleto@stop#1\ekv@stop{} -\long\def\ekv@gobble@from@mark@to@stop\ekv@mark#1\ekv@stop{} -% \end{macrocode} -% \end{macro} -% As you can see |\ekv@gobbleto@stop| uses a special marker |\ekv@stop|. The -% package will use three such markers, the one you've seen already, |\ekv@mark| -% and |\ekv@nil|. Contrarily to how for instance \pkg{expl3} does things, we -% don't define them, as we don't need them to have an actual meaning. This has -% the advantage that if they somehow get expanded -- which should never happen -% if things work out -- they'll throw an error directly. -% -% \begin{macro}[internal] -% { -% \ekv@ifempty,\ekv@ifempty@,\ekv@ifempty@true,\ekv@ifempty@false, -% \ekv@ifempty@true@F,\ekv@ifempty@true@F@gobble, -% \ekv@ifempty@true@F@gobbletwo -% } -% We can test for a lot of things building on an if-empty test, so lets define a -% really fast one. Since some tests might have reversed logic (true if something -% is not empty) we also set up macros for the reversed branches. -% \begin{macrocode} -\long\def\ekv@ifempty#1% - {% - \ekv@ifempty@\ekv@ifempty@A#1\ekv@ifempty@B\ekv@ifempty@true - \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo - } -\long\def\ekv@ifempty@#1\ekv@ifempty@A\ekv@ifempty@B{} -\long\def\ekv@ifempty@true\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo#1#2{#1} -\long\def\ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo#1#2{#2} -\long\def\ekv@ifempty@true@F\ekv@ifempty@A\ekv@ifempty@B\@firstofone#1{} -\long\def\ekv@ifempty@true@F@gobble\ekv@ifempty@A\ekv@ifempty@B\@firstofone#1#2% - {} -\long\def\ekv@ifempty@true@F@gobbletwo - \ekv@ifempty@A\ekv@ifempty@B\@firstofone#1#2#3% - {} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@ifblank,\ekv@ifblank@} -% The obvious test that can be based on an if-empty is if-blank, meaning a test -% checking whether the argument is empty or consists only of spaces. Our version -% here will be tweaked a bit, as we want to check this, but with one leading -% |\ekv@mark| token that is to be ignored. The wrapper |\ekv@ifblank| will not -% be used by \expkv\ for speed reasons but \expkvo\ uses it. -% \begin{macrocode} -\long\def\ekv@ifblank#1% - {% - \ekv@ifblank@#1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true - \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo - } -\long\def\ekv@ifblank@\ekv@mark#1{\ekv@ifempty@\ekv@ifempty@A} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@ifdefined} -% We'll need to check whether something is defined quite frequently, so why not -% define a macro that does this. The following test is expandable and pretty -% fast. The version with |\lastnamedcs| is the fastest version to test for an -% undefined macro I know of (that considers both undefined macros and those with -% the meaning |\relax|). -% \begin{macrocode} -\ekv@if@lastnamedcs - {% - \long\def\ekv@ifdefined#1{\ifcsname#1\endcsname\ekv@ifdef@\fi\@secondoftwo} - \def\ekv@ifdef@\fi\@secondoftwo - {% - \fi - \expandafter\ifx\lastnamedcs\relax - \ekv@fi@secondoftwo - \fi - \@firstoftwo - } - } - {% - \long\def\ekv@ifdefined#1% - {% - \ifcsname#1\endcsname\ekv@ifdef@\fi\ekv@ifdef@false#1\endcsname\relax - \ekv@fi@secondoftwo - \fi - \@firstoftwo - } - \def\ekv@ifdef@\fi\ekv@ifdef@false{\fi\expandafter\ifx\csname} - \long\def\ekv@ifdef@false - #1\endcsname\relax\ekv@fi@secondoftwo\fi\@firstoftwo#2#3% - {#3} - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@strip,\ekv@strip@a,\ekv@strip@b,\ekv@strip@c} -% We borrow some ideas of \pkg{expl3}'s \pkg{l3tl} to strip spaces -% from keys and values. This |\ekv@strip| also strips one level of outer braces -% \emph{after} stripping spaces, so an input of | {abc} | becomes |abc| after -% stripping. It should be used with |#1| prefixed by |\ekv@mark|. Also this -% implementation at most strips \emph{one} space from both sides (which should -% be fine most of the time, since \TeX\ reads consecutive spaces as a single one -% during tokenisation). -% \begin{macrocode} -\def\ekv@strip#1% - {% - \long\def\ekv@strip##1% - {% - \ekv@strip@a - ##1\ekv@nil - \ekv@mark#1% - #1\ekv@nil - }% - \long\def\ekv@strip@a##1\ekv@mark#1{\ekv@strip@b##1\ekv@mark}% - } -\ekv@strip{ } -\long\def\ekv@strip@b#1 \ekv@nil{\ekv@strip@c#1\ekv@nil} -\long\def\ekv@strip@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil#3{#3{#1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekv@exparg,\ekv@exparg@,\ekv@expargtwice,\ekv@expargtwice@,\ekv@zero} -% To reduce some code doublets while gaining some speed (and also as convenience -% for other packages in the family), it is often useful to expand the first -% token in a definition once. Let's define a wrapper for this. -% -% Also, to end a |\romannumeral| expansion, we want to use |\z@|, which is -% contained in both plain \TeX\ and \LaTeX, but we use a private name for it to -% make it easier to spot and hence easier to manage. -% \begin{macrocode} -\let\ekv@zero\z@ -\long\def\ekv@exparg#1#2{\expandafter\ekv@exparg@\expandafter{#2}{#1}} -\long\def\ekv@exparg@#1#2{#2{#1}}% -\long\def\ekv@expargtwice#1#2{\expandafter\ekv@expargtwice@\expandafter{#2}{#1}} -\def\ekv@expargtwice@{\expandafter\ekv@exparg@\expandafter} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvcsvloop} -% \begin{macro}[internal]{\ekv@csv@loop@active,\ekv@csv@loop@active@end} -% An |\ekvcsvloop| will just loop over a csv list in a simple manner. First we -% split at active commas (gives better performance this way), next we have to -% check whether we're at the end of the list (checking for |\ekv@stop|). If -% not we go on splitting at commas of category other. -% \begin{macrocode} -\begingroup -\def\ekvcsvloop#1{% -\endgroup -\long\def\ekvcsvloop##1##2% - {\ekv@csv@loop@active{##1}\ekv@mark##2#1\ekv@stop#1} -% \end{macrocode} -% This does the same as |\ekv@csv@loop| but for active commas. -% \begin{macrocode} -\long\def\ekv@csv@loop@active##1##2#1% - {% - \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@active@end\ekv@stop - \ekv@csv@loop{##1}##2,\ekv@stop,% - }% -\long\def\ekv@csv@loop@active@end - \ekv@stop - \ekv@csv@loop##1\ekv@mark\ekv@stop,\ekv@stop,% - {}% -} -% \end{macrocode} -% Do the definitions with the weird catcode. -% \begin{macrocode} -\catcode`\,=13 -\ekvcsvloop, -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@csv@loop,\ekv@csv@loop@do,\ekv@csv@loop@end} -% We use temporary macros and an |\expandafter| chain to preexpand -% |\ekv@strip| here. After splitting at other commas we check again for end -% the end of the sublist, check for blank elements which should be ignored, -% and else strip spaces and execute the user code (protecting it from further -% expanding with |\unexpanded|). -% \begin{macrocode} -\def\ekv@csv@loop#1% - {% - \long\def\ekv@csv@loop##1##2,% - {% - \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@end\ekv@stop - \ekv@ifblank@##2\ekv@nil\ekv@ifempty@B\ekv@csv@loop@blank - \ekv@ifempty@A\ekv@ifempty@B - #1\ekv@csv@loop@do{##1}% - }% - } -\expandafter\ekv@csv@loop\expandafter{\ekv@strip{#2}} -\long\def\ekv@csv@loop@do#1#2{\ekv@unexpanded{#2{#1}}\ekv@csv@loop{#2}\ekv@mark} -\def\ekv@csv@loop@end#1% - {% - \long\def\ekv@csv@loop@end - \ekv@stop - \ekv@ifblank@\ekv@mark\ekv@stop\ekv@nil\ekv@ifempty@B\ekv@csv@loop@blank - \ekv@ifempty@A\ekv@ifempty@B - #1\ekv@csv@loop@do##1% - {\ekv@csv@loop@active{##1}\ekv@mark}% - } -\expandafter\ekv@csv@loop@end\expandafter{\ekv@strip{\ekv@mark\ekv@stop}} -\long\expandafter\def\expandafter\ekv@csv@loop@blank - \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B - \ekv@strip{\ekv@mark#1}\ekv@csv@loop@do#2% - {\ekv@csv@loop{#2}\ekv@mark} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekv@name,\ekv@name@set,\ekv@name@key} -% The keys will all follow the same naming scheme, so we define it here. -% \begin{macrocode} -\def\ekv@name@set#1{ekv#1(} -\long\def\ekv@name@key#1{#1)} -\edef\ekv@name - {% - \ekv@unexpanded\expandafter{\ekv@name@set{#1}}% - \ekv@unexpanded\expandafter{\ekv@name@key{\detokenize{#2}}}% - } -\long\ekv@exparg{\def\ekv@name#1#2}{\ekv@name} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@undefined@set} -% We can misuse the macro name we use to expandably store the set-name in a -% single token -- since this increases performance drastically, especially for -% long set-names -- to throw a more meaningful error message in case a set isn't -% defined. The name of |\ekv@undefined@set| is a little bit misleading, as it is -% called in either case inside of |\csname|, but the result will be a control -% sequence with meaning |\relax| if the set is undefined, hence will break the -% |\csname| building the key-macro which will throw the error message. -% \begin{macrocode} -\def\ekv@undefined@set#1{! expkv Error: Set `#1' undefined.} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@checkvalid} -% We place some restrictions on the allowed names, though, namely sets and -% keys are not allowed to be empty -- blanks are fine (meaning \mbox{set- -% or} key-names consisting of spaces). The |\def\ekv@tmp| gobbles any \TeX\ -% prefixes which would otherwise throw errors. This will, however, break the -% package if an |\outer| has been gobbled this way. I consider that good, -% because keys shouldn't be defined |\outer| anyways. -% \begin{macrocode} -\edef\ekv@checkvalid - {% - \ekv@unexpanded\expandafter{\ekv@ifempty{#1}}% - \ekv@unexpanded - {{% - \def\ekv@tmp{}% - \errmessage{expkv Error: empty set name not allowed}% - }}% - {% - \ekv@unexpanded\expandafter{\ekv@ifempty{#2}}% - \ekv@unexpanded - {% - {% - \def\ekv@tmp{}% - \errmessage{expkv Error: empty key name not allowed}% - }% - \@secondoftwo - }% - }% - \ekv@unexpanded{\@gobble}% - } -\ekv@exparg{\protected\def\ekv@checkvalid#1#2}{\ekv@checkvalid}% -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvifdefined,\ekvifdefinedNoVal} -% And provide user-level macros to test whether a key is defined. -% \begin{macrocode} -\ekv@expargtwice{\long\def\ekvifdefined#1#2}% - {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}}} -\ekv@expargtwice{\long\def\ekvifdefinedNoVal#1#2}% - {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}N}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro} -% { -% \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal, -% \ekvdefunknown,\ekvdefunknownNoVal -% } -% Set up the key defining macros |\ekvdef| etc. We use temporary macros to set -% these up with a few expansions already done. -% \begin{macrocode} -\def\ekvdef#1#2#3#4% - {% - \protected\long\def\ekvdef##1##2##3% - {#1{\expandafter\def\csname#2\endcsname####1{##3}#3}}% - \protected\long\def\ekvdefNoVal##1##2##3% - {#1{\expandafter\def\csname#2N\endcsname{##3}#3}}% - \protected\long\def\ekvlet##1##2##3% - {#1{\expandafter\let\csname#2\endcsname##3#3}}% - \protected\long\def\ekvletNoVal##1##2##3% - {#1{\expandafter\let\csname#2N\endcsname##3#3}}% - \ekv@expargtwice{\protected\long\def\ekvdefunknown##1##2}% - {% - \romannumeral - \expandafter\ekv@exparg@\expandafter - {% - \expandafter\expandafter\expandafter - \def\expandafter\csname\ekv@name{##1}{}u\endcsname####1####2{##2}% - #3% - }% - {\ekv@zero\ekv@checkvalid{##1}.}% - }% - \ekv@expargtwice{\protected\long\def\ekvdefunknownNoVal##1##2}% - {% - \romannumeral - \expandafter\ekv@exparg@\expandafter - {% - \expandafter\expandafter\expandafter - \def\expandafter\csname\ekv@name{##1}{}uN\endcsname####1{##2}% - #3% - }% - {\ekv@zero\ekv@checkvalid{##1}.}% - }% - \protected\long\def\ekvletkv##1##2##3##4% - {% - #1% - {% - \expandafter\let\csname#2\expandafter\endcsname - \csname#4\endcsname - #3% - }% - }% - \protected\long\def\ekvletkvNoVal##1##2##3##4% - {% - #1% - {% - \expandafter\let\csname#2N\expandafter\endcsname - \csname#4N\endcsname - #3% - }% - }% - } -\edef\ekvdefNoVal - {% - {\ekv@unexpanded\expandafter{\ekv@checkvalid{#1}{#2}}}% - {\ekv@unexpanded\expandafter{\ekv@name{#1}{#2}}}% - {% - \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}% - \ekv@unexpanded\expandafter{\ekv@undefined@set{#1}\endcsname{#1}}% - }% - {\ekv@unexpanded\expandafter{\ekv@name{#3}{#4}}}% - } -\expandafter\ekvdef\ekvdefNoVal -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvredirectunknown,\ekvredirectunknownNoVal} -% \begin{macro}[internal] -% { -% \ekv@defredirectunknown,\ekv@redirectunknown@aux, -% \ekv@redirectunknownNoVal@aux -% } -% The redirection macros prepare the unknown function by looping over the -% provided list of sets and leaving a |\ekv@redirect@kv| or |\ekv@redirect@k| -% for each set. Only the first of these internals will receive the \key\ and -% \val\ as arguments. -% \begin{macrocode} -\protected\def\ekvredirectunknown - {% - \ekv@defredirectunknown - \ekv@redirect@kv - \ekv@err@redirect@kv@notfound - {\long\ekvdefunknown}% - \ekv@redirectunknown@aux - } -\protected\def\ekvredirectunknownNoVal - {% - \ekv@defredirectunknown - \ekv@redirect@k - \ekv@err@redirect@k@notfound - \ekvdefunknownNoVal - \ekv@redirectunknownNoVal@aux - } -\protected\def\ekv@defredirectunknown#1#2#3#4#5#6% - {% - \begingroup - \edef\ekv@tmp - {% - \ekvcsvloop#1{#6}% - \ekv@unexpanded{#2}% - {\ekvcsvloop{}{#5,#6}}% - }% - \ekv@expargtwice - {\endgroup#3{#5}}% - {\expandafter#4\ekv@tmp\ekv@stop}% - } -\def\ekv@redirectunknown@aux#1{#1{##1}{##2}} -\def\ekv@redirectunknownNoVal@aux#1{#1{##1}} -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekv@redirect@k,\ekv@redirect@k@a,\ekv@redirect@k@a@, -% \ekv@redirect@k@b,\ekv@redirect@k@c,\ekv@redirect@k@d, -% \ekv@redirect@kv,\ekv@redirect@kv@a,\ekv@redirect@kv@a@, -% \ekv@redirect@kv@b,\ekv@redirect@kv@c,\ekv@redirect@kv@d -% } -% The redirect code works by some simple loop over all the sets, which we -% already preprocessed in |\ekv@defredirectunknown|. For some optimisation we -% blow this up a bit code wise, essentially, all this does is |\ekvifdefined| -% or |\ekvifdefinedNoVal| in each set, if there is a match gobble the -% remainder of the specified sets and execute the key macro, else go on with -% the next set (to which the \key\ and \val\ are forwarded). -% -% First we set up some code which is different depending on |\lastnamedcs| -% being available or not. All this is stored in a temporary macro to have -% pre-expanded |\ekv@name| constellations ready. -% \begin{macrocode} -\def\ekv@redirect@k#1#2#3#4% - {% - \ekv@if@lastnamedcs - {% - \def\ekv@redirect@k##1##2##3% - {% - \ifcsname#1\endcsname\ekv@redirect@k@a\fi - ##3{##1}% - }% - \def\ekv@redirect@k@a\fi{\fi\expandafter\ekv@redirect@k@b\lastnamedcs}% - \long\def\ekv@redirect@kv##1##2##3##4% - {% - \ifcsname#2\endcsname\ekv@redirect@kv@a\fi\@gobble{##1}% - ##4{##1}{##2}% - }% - \def\ekv@redirect@kv@a\fi\@gobble - {\fi\expandafter\ekv@redirect@kv@b\lastnamedcs}% - } - {% - \def\ekv@redirect@k##1##2##3% - {% - \ifcsname#1\endcsname\ekv@redirect@k@a\fi\ekv@redirect@k@a@ - #1\endcsname - ##3{##1}% - }% - \def\ekv@redirect@k@a@#3\endcsname{}% - \def\ekv@redirect@k@a\fi\ekv@redirect@k@a@ - {\fi\expandafter\ekv@redirect@k@b\csname}% - \long\def\ekv@redirect@kv##1##2##3##4% - {% - \ifcsname#2\endcsname\ekv@redirect@kv@a\fi\ekv@redirect@kv@a@ - #2\endcsname{##1}% - ##4{##1}{##2}% - }% - \long\def\ekv@redirect@kv@a@#4\endcsname##3{}% - \def\ekv@redirect@kv@a\fi\ekv@redirect@kv@a@ - {\fi\expandafter\ekv@redirect@kv@b\csname}% - }% - } -% \end{macrocode} -% The key name given to this loop will already be |\detokenize|d by |\ekvset|, -% so we can safely remove the |\detokenize| here for some performance gain. -% \begin{macrocode} -\def\ekv@redirect@kv#1\detokenize#2#3\ekv@stop{\ekv@unexpanded{#1#2#3}} -\edef\ekv@redirect@kv - {% - {\expandafter\ekv@redirect@kv\ekv@name{#2}{#1}N\ekv@stop}% - {\expandafter\ekv@redirect@kv\ekv@name{#3}{#2}\ekv@stop}% - {\expandafter\ekv@redirect@kv\ekv@name{#1}{#2}N\ekv@stop}% - {\expandafter\ekv@redirect@kv\ekv@name{#1}{#2}\ekv@stop}% - } -% \end{macrocode} -% Everything is ready to make the real definitions. -% \begin{macrocode} -\expandafter\ekv@redirect@k\ekv@redirect@kv -% \end{macrocode} -% The remaining macros here are independent on |\lastnamedcs|, starting from -% the |@b| we know that there is a hash table entry, and get the macro as a -% parameter. We still have to test whether the macro is |\relax|, depending on -% the result of that test we have to either remove the remainder of the -% current test, or the remainder of the set list and invoke the macro. -% \begin{macrocode} -\def\ekv@redirect@k@b#1% - {\ifx\relax#1\ekv@redirect@k@c\fi\ekv@redirect@k@d#1} -\def\ekv@redirect@k@c\fi\ekv@redirect@k@d#1{\fi} -\def\ekv@redirect@k@d#1#2\ekv@stop{#1} -\def\ekv@redirect@kv@b#1% - {\ifx\relax#1\ekv@redirect@kv@c\fi\ekv@redirect@kv@d#1} -\long\def\ekv@redirect@kv@c\fi\ekv@redirect@kv@d#1#2{\fi} -\long\def\ekv@redirect@kv@d#1#2#3\ekv@stop{#1{#2}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@defsetmacro} -% In order to enhance the speed the set name given to |\ekvset| will be turned -% into a control sequence pretty early, so we have to define that control -% sequence. -% \begin{macrocode} -\edef\ekv@defsetmacro - {% - \ekv@unexpanded{\ifx#1\relax\edef#1##1}% - {% - \ekv@unexpanded\expandafter{\ekv@name@set{#2}}% - \ekv@unexpanded\expandafter{\ekv@name@key{##1}}% - }% - \ekv@unexpanded{\fi}% - } -\ekv@exparg{\protected\def\ekv@defsetmacro#1#2}{\ekv@defsetmacro} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvifdefinedset} -% \begin{macrocode} -\ekv@expargtwice{\def\ekvifdefinedset#1}% - {\expandafter\ekv@ifdefined\expandafter{\ekv@undefined@set{#1}}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvset} -% Set up |\ekvset|, which should not be affected by active commas and equal -% signs. The equal signs are a bit harder to cope with and we'll do that later, -% but the active commas can be handled by just doing two comma-splitting loops -% one at actives one at others. That's why we define |\ekvset| here with a -% temporary meaning just to set up the things with two different category codes. -% |#1| will be a \texttt{,\textsubscript{13}} and |#2| will be a -% \texttt{=\textsubscript{13}}. -% \begin{macrocode} -\begingroup -\def\ekvset#1#2{% -\endgroup -\ekv@exparg{\long\def\ekvset##1##2}% - {% - \expandafter\expandafter\expandafter - \ekv@set\expandafter\csname\ekv@undefined@set{##1}\endcsname - \ekv@mark##2#1\ekv@stop#1{}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@set} -% |\ekv@set| will split the \kv\ list at active commas. Then it has to check -% whether there were unprotected other commas and resplit there. -% \begin{macrocode} -\long\def\ekv@set##1##2#1% - {% -% \end{macrocode} -% Test whether we're at the end, if so invoke |\ekv@endset|, -% \begin{macrocode} - \ekv@gobble@from@mark@to@stop##2\ekv@endset\ekv@stop -% \end{macrocode} -% else go on with other commas. -% \begin{macrocode} - \ekv@set@other##1##2,\ekv@stop,% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@endset} -% |\ekv@endset| is a hungry little macro. It will eat everything that remains -% of |\ekv@set| and unbrace the sneaked stuff. -% \begin{macrocode} -\long\def\ekv@endset - \ekv@stop\ekv@set@other##1\ekv@mark\ekv@stop,\ekv@stop,##2% - {##2} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@eq@other,\ekv@eq@active} -% Splitting at equal signs will be done in a way that checks whether there is -% an equal sign and splits at the same time. This gets quite messy and the -% code might look complicated, but this is pretty fast (faster than first -% checking for an equal sign and splitting if one is found). The splitting -% code will be adapted for |\ekvset| and |\ekvparse| to get the most speed, -% but some of these macros don't require such adaptions. |\ekv@eq@other| and -% |\ekv@eq@active| will split the argument at the first equal sign and insert -% the macro which comes after the first following |\ekv@mark|. This allows for -% fast branching based on \TeX's argument grabbing rules and we don't have to -% split after the branching if the equal sign was there. -% \begin{macrocode} -\long\def\ekv@eq@other##1=##2\ekv@mark##3{##3##1\ekv@stop\ekv@mark##2} -\long\def\ekv@eq@active##1#2##2\ekv@mark##3{##3##1\ekv@stop\ekv@mark##2} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@set@other} -% The macro |\ekv@set@other| is guaranteed to get only single \kv\ pairs. -% \begin{macrocode} -\long\def\ekv@set@other##1##2,% - {% -% \end{macrocode} -% First we test whether we're done. -% \begin{macrocode} - \ekv@gobble@from@mark@to@stop##2\ekv@endset@other\ekv@stop -% \end{macrocode} -% If not we split at the equal sign of category other. -% \begin{macrocode} - \ekv@eq@other##2\ekv@nil\ekv@mark\ekv@set@eq@other@a - =\ekv@mark\ekv@set@eq@active -% \end{macrocode} -% And insert the set name for the next recursion step of |\ekv@set@other|. -% \begin{macrocode} - ##1% - \ekv@mark - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@set@eq@other@a, \ekv@set@eq@other@b} -% The first of these two macros runs the split-test for equal signs of -% category active. It will only be inserted if the \kv\ pair contained at -% least one equal sign of category other and |##1| will contain everything up -% to that equal sign. -% \begin{macrocode} -\long\def\ekv@set@eq@other@a##1\ekv@stop - {% - \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@other@active - #2\ekv@mark\ekv@set@eq@other@b - } -% \end{macrocode} -% The second macro will have been called by |\ekv@eq@active| if no active -% equal sign was found. All it does is remove the excess tokens of that -% test and forward the \kv\ pair to |\ekv@set@pair|. Normally we would have to -% also gobble an additional |\ekv@mark| after |\ekv@stop|, but this mark is -% needed to delimit |\ekv@set@pair|'s argument anyway, so we just leave it -% there. -% \begin{macrocode} -\ekv@exparg - {% - \long\def\ekv@set@eq@other@b - ##1\ekv@nil\ekv@mark\ekv@set@eq@other@active\ekv@stop\ekv@mark - ##2\ekv@nil=\ekv@mark\ekv@set@eq@active - }% - {\ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2\ekv@nil} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@set@eq@other@active} -% |\ekv@set@eq@other@active| will be called if the \kv\ pair was wrongly -% split on an equal sign of category other but has an earlier equal sign of -% category active. |##1| will be the contents up to the active equal sign and -% |##2| everything that remains until the first found other equal sign. It has -% to reinsert the equal sign and forward things to |\ekv@set@pair|. -% \begin{macrocode} -\ekv@exparg - {% - \long\def\ekv@set@eq@other@active - ##1\ekv@stop##2\ekv@nil#2\ekv@mark - \ekv@set@eq@other@b\ekv@mark##3=\ekv@mark\ekv@set@eq@active - }% - {\ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2=##3} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@set@eq@active, \ekv@set@eq@active@} -% |\ekv@set@eq@active| will be called when there was no equal sign of category -% other in the \kv\ pair. It removes the excess tokens of the prior test and -% split-checks for an active equal sign. -% \begin{macrocode} -\long\def\ekv@set@eq@active - ##1\ekv@nil\ekv@mark\ekv@set@eq@other@a\ekv@stop\ekv@mark - {% - \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@active@ - #2\ekv@mark\ekv@set@noeq - } -% \end{macrocode} -% If an active equal sign was found in |\ekv@set@eq@active| we'll have to pass -% the now split \kv\ pair on to |\ekv@set@pair|. -% \begin{macrocode} -\ekv@exparg - {\long\def\ekv@set@eq@active@##1\ekv@stop##2\ekv@nil#2\ekv@mark\ekv@set@noeq}% - {\ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2\ekv@nil} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@set@noeq} -% If no active equal sign was found by |\ekv@set@eq@active| there is no equal -% sign contained in the parsed list entry. In that case we have to check -% whether the entry is blank in order to ignore it (in which case we'll have -% to gobble the set-name which was put after these tests by |\ekv@set@other|). -% Else this is a |NoVal| key and the entry is passed on to |\ekv@set@key|. -% \begin{macrocode} -\edef\ekv@set@noeq - {% - \ekv@unexpanded - {% - \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@set@was@blank - \ekv@ifempty@A\ekv@ifempty@B - }% - \ekv@unexpanded\expandafter - {\ekv@strip{##1}{\expandafter\ekv@set@key\detokenize}\ekv@mark}% - } -\ekv@exparg - {% - \long\def\ekv@set@noeq - ##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark - }% - {\ekv@set@noeq} -\expandafter\def\expandafter\ekv@set@was@blank - \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B - \ekv@strip{\ekv@mark##1}##2\ekv@mark - {\ekv@set@other} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@endset@other} -% All that's left for |\ekv@set@other| is the macro which breaks the recursion -% loop at the end. This is done by gobbling all the remaining tokens. -% \begin{macrocode} -\long\def\ekv@endset@other - \ekv@stop - \ekv@eq@other\ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@set@eq@other@a - =\ekv@mark\ekv@set@eq@active - {\ekv@set} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak} -% Provide macros that can completely stop the parsing of |\ekvset|, who knows -% what it'll be useful for. -% \begin{macrocode} -\long\def\ekvbreak##1##2\ekv@stop#1##3{##1} -\long\def\ekvbreakPreSneak ##1##2\ekv@stop#1##3{##1##3} -\long\def\ekvbreakPostSneak##1##2\ekv@stop#1##3{##3##1} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvsneak,\ekvsneakPre} -% One last thing we want to do for |\ekvset| is to provide macros that just -% smuggle stuff after |\ekvset|'s effects. -% \begin{macrocode} -\long\def\ekvsneak##1##2\ekv@stop#1##3{##2\ekv@stop#1{##3##1}} -\long\def\ekvsneakPre##1##2\ekv@stop#1##3{##2\ekv@stop#1{##1##3}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvparse} -% Additionally to the |\ekvset| macro we also want to provide an |\ekvparse| -% macro, that has the same scope as |\keyval_parse:NNn| from \pkg{expl3}. -% This is pretty analogue to the |\ekvset| implementation, we just put an -% |\unexpanded| here and there instead of other macros to stop the |\expanded| -% on our output. The |\unexpanded\expanded{{...}}| ensures that the material -% is in an alignment safe group at all time, and that it doesn't expand any -% further in an |\edef| or |\expanded| context. -% \begin{macrocode} -\long\def\ekvparse##1##2##3% - {% - \ekv@unexpanded\ekv@expanded - {{\ekv@parse{##1}{##2}\ekv@mark##3#1\ekv@stop#1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse} -% \begin{macrocode} -\long\def\ekv@parse##1##2##3#1% - {% - \ekv@gobble@from@mark@to@stop##3\ekv@endparse\ekv@stop - \ekv@parse@other{##1}{##2}##3,\ekv@stop,% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@endparse} -% \begin{macrocode} -\long\def\ekv@endparse - \ekv@stop\ekv@parse@other##1\ekv@mark\ekv@stop,\ekv@stop,% - {} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@other} -% \begin{macrocode} -\long\def\ekv@parse@other##1##2##3,% - {% - \ekv@gobble@from@mark@to@stop##3\ekv@endparse@other\ekv@stop - \ekv@eq@other##3\ekv@nil\ekv@mark\ekv@parse@eq@other@a - =\ekv@mark\ekv@parse@eq@active - {##1}{##2}% - \ekv@mark - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@eq@other@a,\ekv@parse@eq@other@b} -% \begin{macrocode} -\long\def\ekv@parse@eq@other@a##1\ekv@stop - {% - \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active - #2\ekv@mark\ekv@parse@eq@other@b - } -\ekv@exparg - {% - \long\def\ekv@parse@eq@other@b - ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active\ekv@stop\ekv@mark - ##2\ekv@nil=\ekv@mark\ekv@parse@eq@active - }% - {\ekv@strip{##1}\ekv@parse@pair##2\ekv@nil} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@eq@other@active} -% \begin{macrocode} -\ekv@exparg - {% - \long\def\ekv@parse@eq@other@active - ##1\ekv@stop##2\ekv@nil#2\ekv@mark - \ekv@parse@eq@other@b\ekv@mark##3=\ekv@mark\ekv@parse@eq@active - }% - {\ekv@strip{##1}\ekv@parse@pair##2=##3} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@eq@active,\ekv@parse@eq@active@} -% \begin{macrocode} -\long\def\ekv@parse@eq@active - ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@a\ekv@stop\ekv@mark - {% - \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@active@ - #2\ekv@mark\ekv@parse@noeq - } -\ekv@exparg - {\long\def\ekv@parse@eq@active@##1\ekv@stop##2#2\ekv@mark\ekv@parse@noeq}% - {\ekv@strip{##1}\ekv@parse@pair##2} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@noeq} -% \begin{macrocode} -\edef\ekv@parse@noeq - {% - \ekv@unexpanded - {% - \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@parse@was@blank - \ekv@ifempty@A\ekv@ifempty@B - }% - \ekv@unexpanded\expandafter{\ekv@strip{##1}\ekv@parse@key}% - } -\ekv@exparg - {% - \long\def\ekv@parse@noeq - ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark - }% - {\ekv@parse@noeq} -\expandafter\def\expandafter\ekv@parse@was@blank - \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B - \ekv@strip{\ekv@mark##1}\ekv@parse@key - {\ekv@parse@other} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@endparse@other} -% \begin{macrocode} -\long\def\ekv@endparse@other - \ekv@stop - \ekv@eq@other\ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@parse@eq@other@a - =\ekv@mark\ekv@parse@eq@active - {\ekv@parse} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@pair,\ekv@parse@pair@} -% \begin{macrocode} -\ekv@exparg{\long\def\ekv@parse@pair##1##2\ekv@nil}% - {\ekv@strip{##2}\ekv@parse@pair@{##1}} -\long\def\ekv@parse@pair@##1##2##3##4% - {% - \ekv@unexpanded{##4{##2}{##1}}% - \ekv@parse@other{##3}{##4}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@parse@key} -% \begin{macrocode} -\long\def\ekv@parse@key##1##2% - {% - \ekv@unexpanded{##2{##1}}% - \ekv@parse@other{##2}% - } -% \end{macrocode} -% \end{macro} -% -% Finally really setting things up with |\ekvset|'s temporary meaning: -% \begin{macrocode} -} -\catcode`\,=13 -\catcode`\==13 -\ekvset,= -% \end{macrocode} -% -% \begin{macro}{\ekvsetSneaked} -% This macro can be defined just by expanding |\ekvsneak| once after expanding -% |\ekvset|. To expand everything as much as possible early on we use a -% temporary definition. -% \begin{macrocode} -\edef\ekvsetSneaked - {% - \ekv@unexpanded{\ekvsneak{#2}}% - \ekv@unexpanded\expandafter{\ekvset{#1}{#3}}% - } -\ekv@expargtwice{\long\def\ekvsetSneaked#1#2#3}{\ekvsetSneaked} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvchangeset} -% Provide a macro that is able to switch out the current \set\ in |\ekvset|. -% This operation allows something similar to \pkg{pgfkeys}'s -% \texttt{\meta{key}/.cd} mechanism. However this operation can be more -% expensive than |/.cd| as we can't just redefine some token to reflect this, -% but have to switch out the set expandably, so this works similar to the -% |\ekvsneak| macros reading and reinserting things, but it only has to read and -% reinsert the remainder of the current key's replacement code. -% \begin{macrocode} -\ekv@exparg{\def\ekvchangeset#1}% - {% - \expandafter\expandafter\expandafter - \ekv@changeset\expandafter\csname\ekv@undefined@set{#1}\endcsname\ekv@empty - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@changeset} -% This macro does the real change-out of |\ekvchangeset|. |#2| will have a -% leading |\ekv@empty| so that braces aren't stripped accidentally, but that -% will not hurt and just expand to nothing in one step. -% \begin{macrocode} -\long\def\ekv@changeset#1#2\ekv@set@other#3{#2\ekv@set@other#1} -% \end{macrocode} -% \end{macro} -% -% -% \begin{macro}[internal] -% { -% \ekv@set@pair,\ekv@set@pair@a,\ekv@set@pair@b,\ekv@set@pair@c, -% \ekv@set@pair@d,\ekv@set@pair@e -% } -% |\ekv@set@pair| gets invoked with the space and brace stripped and -% |\detokenize|d key-name as its first, the value as the second, and the set -% name as the third argument. It provides tests for the key-macros and -% everything to be able to throw meaningful error messages if it isn't -% defined. We have two routes here, one if |\lastnamedcs| is defined and one -% if it isn't. The big difference is that if it is we can omit a |\csname| and -% instead just expand |\lastnamedcs| once to get the control sequence. -% If the macro is defined the value will be space and brace stripped and the -% key-macro called. Else branch into the error handling provided by -% |\ekv@set@pair|. -% \begin{macrocode} -\ekv@if@lastnamedcs - {% - \long\def\ekv@set@pair#1\ekv@mark#2\ekv@nil#3% - {% - \ifcsname #3{#1}\endcsname\ekv@set@pair@a\fi\@secondoftwo - {#2}% - {% - \ifcsname #3{}u\endcsname\ekv@set@pair@a\fi\@secondoftwo - {#2}% - {% - \ekv@ifdefined{#3{#1}N}% - \ekv@err@noarg - \ekv@err@unknown - #3% - }% - {#1}% - }% - \ekv@set@other#3% - } - \def\ekv@set@pair@a\fi\@secondoftwo - {\fi\expandafter\ekv@set@pair@b\lastnamedcs} - } - {% - \long\def\ekv@set@pair#1\ekv@mark#2\ekv@nil#3% - {% - \ifcsname #3{#1}\endcsname - \ekv@set@pair@a\fi\ekv@set@pair@c#3{#1}\endcsname - {#2}% - {% - \ifcsname #3{}u\endcsname - \ekv@set@pair@a\fi\ekv@set@pair@c#3{}u\endcsname - {#2}% - {% - \ekv@ifdefined{#3{#1}N}% - \ekv@err@noarg - \ekv@err@unknown - #3% - }% - {#1}% - }% - \ekv@set@other#3% - } - \def\ekv@set@pair@a\fi\ekv@set@pair@c{\fi\expandafter\ekv@set@pair@b\csname} - \long\def\ekv@set@pair@c#1\endcsname#2#3{#3} - } -\long\def\ekv@set@pair@b#1% - {% - \ifx#1\relax - \ekv@set@pair@e - \fi - \ekv@set@pair@d#1% - } -\ekv@exparg{\long\def\ekv@set@pair@d#1#2#3}{\ekv@strip{#2}#1} -\long\def\ekv@set@pair@e\fi\ekv@set@pair@d#1#2#3{\fi#3} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% {\ekv@set@key,\ekv@set@key@a,\ekv@set@key@b,\ekv@set@key@c} -% Analogous to |\ekv@set@pair|, |\ekv@set@key| builds the |NoVal| key-macro and -% provides an error-branch. |\ekv@set@key@| will test whether the key-macro is -% defined and if so call it, else the errors are thrown. -% \begin{macrocode} -\ekv@if@lastnamedcs - {% - \long\def\ekv@set@key#1\ekv@mark#2% - {% - \ifcsname #2{#1}N\endcsname\ekv@set@key@a\fi\@firstofone - {% - \ifcsname #2{}uN\endcsname\ekv@set@key@a\fi\@firstofone - {% - \ekv@ifdefined{#2{#1}}% - \ekv@err@reqval - \ekv@err@unknown - #2% - }% - {#1}% - }% - \ekv@set@other#2% - } - \def\ekv@set@key@a\fi\@firstofone{\fi\expandafter\ekv@set@key@b\lastnamedcs} - } - {% - \long\def\ekv@set@key#1\ekv@mark#2% - {% - \ifcsname #2{#1}N\endcsname - \ekv@set@key@a\fi\ekv@set@key@c#2{#1}N\endcsname - {% - \ifcsname #2{}uN\endcsname - \ekv@set@key@a\fi\ekv@set@key@c#2{}uN\endcsname - {% - \ekv@ifdefined{#2{#1}}% - \ekv@err@reqval - \ekv@err@unknown - #2% - }% - {#1}% - }% - \ekv@set@other#2% - } - \def\ekv@set@key@a\fi\ekv@set@key@c{\fi\expandafter\ekv@set@key@b\csname} - \long\def\ekv@set@key@c#1N\endcsname#2{#2} - } -\long\def\ekv@set@key@b#1% - {% - \ifx#1\relax - \ekv@fi@secondoftwo - \fi - \@firstoftwo#1% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvsetdef} -% Provide a macro to define a shorthand to use |\ekvset| on a specified \set. -% To gain the maximum speed |\ekvset| is expanded twice by -% |\ekv@exparg| so that during runtime the macro storing the set name -% is already built and one |\expandafter| doesn't have to be used. -% \begin{macrocode} -\ekv@expargtwice{\protected\def\ekvsetdef#1#2}% - {% - \romannumeral - \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1}}% - {\ekvset{#2}{##1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvsetSneakeddef,\ekvsetdefSneaked} -% And do the same for |\ekvsetSneaked| in the two possible ways, with a fixed -% sneaked argument and with a flexible one. -% \begin{macrocode} -\ekv@expargtwice{\protected\def\ekvsetSneakeddef#1#2}% - {% - \romannumeral - \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1##2}}% - {\ekvsetSneaked{#2}{##1}{##2}}% - } -\ekv@expargtwice{\protected\def\ekvsetdefSneaked#1#2#3}% - {% - \romannumeral - \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1}}% - {\ekvsetSneaked{#2}{#3}{##1}}% - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@alignsafe,\ekv@endalignsafe} -% These macros protect the usage of ampersands inside of alignment contexts. -% \begin{macrocode} -\begingroup -\catcode`\^^@=2 -\@firstofone{\endgroup - \def\ekv@alignsafe{\romannumeral\iffalse{\fi`^^@ } -} -\def\ekv@endalignsafe{\ifnum`{=\ekv@zero}\fi} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekvoptarg,\ekvoptargTF} -% Provide macros to expandably collect an optional argument in brackets. The -% macros here are pretty simple in nature compared to \pkg{xparse}'s -% possibilities (they don't care for nested bracket levels). -% -% We start with a temporary definition to pre-expand |\ekv@alignsafe| (will be -% |#1|) and |\ekv@endalignsafe| (will be |#2|). As |\ekv@alignsafe| starts with -% a |\romannumeral| we use that to also control the number of steps needed -% instead of adding another |\romannumeral|. For this we have to remove the -% space token from the end of |\ekv@alignsafe|'s definition. -% \begin{macrocode} -\begingroup -\def\ekvoptarg#1#2{% -\endgroup -% \end{macrocode} -% The real definition starts an expansion context and afterwards grabs the -% arguments. |#1| will be the next step, |#2| the default value, and |#3| might -% be an opening bracket, or the mandatory argument. We check for the opening -% bracket, if it is found grab the optional argument, else leave |#1{#2}| in the -% input stream after ending the expansion context. -% \begin{macrocode} -\def\ekvoptarg{#1\ekv@optarg@a} -\long\def\ekv@optarg@a##1##2##3% - {% - \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optarg@b\ekv@mark[\ekv@mark - #2% - \@firstofone{ ##1}{##2}{##3}% - }% -% \end{macrocode} -% The other variant of this will do roughly the same. Here, |#1| will be the -% next step if an optional argument is found, |#2| the next step else, and |#3| -% might be the opening bracket or mandatory argument. -% \begin{macrocode} -\def\ekvoptargTF{#1\ekv@optargTF@a} -\long\def\ekv@optargTF@a##1##2##3% - {% - \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optargTF@b{##1}\ekv@mark[\ekv@mark - #2% - \@firstofone{ ##2}{##3}% - } -% \end{macrocode} -% The two macros to grab the optional argument have to remove the remainder of -% the test and the wrong next step as well as grabbing the argument. -% \begin{macrocode} -\long\def\ekv@optarg@b\ekv@mark[\ekv@mark\ifnum`##1\fi\@firstofone##2##3##4##5]% - {#2##2{##5}} -\long\def\ekv@optargTF@b - ##1\ekv@mark[\ekv@mark\ifnum`##2\fi\@firstofone##3##4##5]% - {#2 ##1{##5}} -} -% \end{macrocode} -% Do the definitions and add the test macro. We use |\ekv@strip| to remove the -% trailing space from the definition of |\ekv@alignsafe|. -% \begin{macrocode} -\ekv@exparg - {% - \expandafter\ekv@strip\expandafter - {\expandafter\ekv@mark\ekv@alignsafe}% - \ekvoptarg - }% - \ekv@endalignsafe -\long\def\ekv@optarg@if#1\ekv@mark[\ekv@mark{} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ekverr} -% \begin{macro}[internal]{\ekv@err@collect,\ekv@err@cleanup} -% Since |\ekvset| is fully expandable as long as the code of the keys is (which -% is unlikely) we want to somehow throw expandable errors, in our case via -% a runaway argument (to my knowledge the first version of this method was -% implemented by Jean-François Burnol, many thanks to him). The first step is to -% ensure that the second argument (which might contain user input) doesn't -% contain tokens we use as delimiters (in this case |\par|), this will be done -% by the front facing macro |\ekverr|. But first we set some other things up. -% -% We use a temporary definition for |\ekverr| to get multiple consecutive -% spaces. Then we set up the macro that will collect the error and the macro -% that will throw the error. The latter will have an unreasonable long name. -% This way we can convey more information. Though the information in the macro -% name is static and has to be somewhat general to fit every occurence. The -% important bit is that the long named macro has a delimited argument and is -% short which will throw the error at the |\par| at the end of -% |\ekv@err@collect|. This macro has the drawback that it will only print nicely -% if the |\newlinechar| is |^^J|. -% \begin{macrocode} -\def\ekv@err@cleanup\par{} -\def\ekv@err@collect#1% - {% - \def\ekv@err@collect##1\par##2% - {% - \expandafter - \ekv@err@cleanup - #1! ##2 Error: ##1\par - }% - \def#1##1\thanks@jfbu{}% - } -\def\ekverr{ } -\expandafter\ekv@err@collect\csname <an-expandable-macro>^^J% - completed due to above exception. \ekverr If the error^^J% - summary is \ekverr not comprehensible \ekverr see the package^^J% - documentation.^^J% - I will try to recover now. \ekverr If you're in inter-^^J% - active mode hit <return> \ekverr at the ? prompt and I^^J% - continue hoping recovery\endcsname -% \end{macrocode} -% \begin{macrocode} -\long\def\ekverr#1#2{\expandafter\ekv@err@collect\detokenize{#2}\par{#1}} -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@err} -% We define a shorthand to throw errors in \expkv. -% \begin{macrocode} -\ekv@exparg{\long\def\ekv@err#1}{\ekverr{expkv}{#1}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal] -% { -% \ekv@err@common,\ekv@err@common@, -% \ekv@err@unknown,\ekv@err@noarg,\ekv@err@reqval -% } -% Now we can use |\ekv@err| to set up some error messages so that we can later -% use those instead of the full strings. -% \begin{macrocode} -\long\def\ekv@err@common #1#2{\expandafter\ekv@err@common@\string#2{#1}} -\ekv@exparg{\long\def\ekv@err@common@#1`#2' #3.#4#5}% - {\ekv@err{#4 `#5' in set `#2'}} -\ekv@exparg{\long\def\ekv@err@unknown#1}{\ekv@err@common{unknown key}{#1}} -\ekv@exparg{\long\def\ekv@err@noarg #1} - {\ekv@err@common{unwanted value for}{#1}} -\ekv@exparg{\long\def\ekv@err@reqval #1}{\ekv@err@common{missing value for}{#1}} -\ekv@exparg{\long\def\ekv@err@redirect@kv@notfound#1#2#3\ekv@stop}% - {\ekv@err{no key `#2' in sets #3}} -\ekv@exparg{\def\ekv@err@redirect@k@notfound#1#2\ekv@stop}% - {\ekv@err{no NoVal key `#1' in sets #2}} -% \end{macrocode} -% \end{macro} -% -% Now everything that's left is to reset the category code of |@|. -% \begin{macrocode} -\catcode`\@=\ekv@tmp -% \end{macrocode} -% -% \gobbledocstriptag -%</tex> -%^^A=<< -% -%^^A=<< -% -% \end{implementation}^^A=<< -% -% \clearpage -% \PrintIndex -% -\endinput -% -^^A vim: ft=tex fdm=marker fmr=>>=,=<< diff --git a/macros/generic/expkv/expkv.pdf b/macros/generic/expkv/expkv.pdf Binary files differdeleted file mode 100644 index 2da6f6dada..0000000000 --- a/macros/generic/expkv/expkv.pdf +++ /dev/null |