mdwtab.dtx: Add a sneaky kern after `\multicolumn'.
[mdwtools] / mdwkey.dtx
CommitLineData
281c40ee 1% \begin{meta-comment} <general public licence>
2%%
3%% mdwkey package -- yet another key/value parser
4%% Copyright (c) 2003 Mark Wooding
5%<*package>
6%%
3d509049 7%% This file is part of the `mdwtools' LaTeX package collection.
281c40ee 8%%
3d509049
MW
9%% `mdwtools' is free software: you can redistribute it and/or modify it
10%% under the terms of the GNU General Public License as published by the
11%% Free Software Foundation; either version 2 of the License, or (at your
12%% option) any later version.
13%%
14%% `mdwtools' is distributed in the hope that it will be useful, but
15%% WITHOUT ANY WARRANTY; without even the implied warranty of
16%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17%% General Public License for more details.
281c40ee 18%%
19%% You should have received a copy of the GNU General Public License
3d509049
MW
20%% along with `mdwtools'. If not, write to the Free Software Foundation,
21%% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
281c40ee 22%</package>
23%%
24% \end{meta-comment}
25%
26% \begin{meta-comment} <Package preamble>
27%<+package&!plain>\NeedsTeXFormat{LaTeX2e}
28%<+package&!plain>\ProvidesPackage{mdwkey}
af8af7eb 29%<+package&!plain> [2020/09/06 1.14.0 Key/value parser]
281c40ee 30% \end{meta-comment}
31%
32% \CheckSum{316}
33%\iffalse
34%<*package>
35%\fi
36%% \CharacterTable
37%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
38%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
39%% Digits \0\1\2\3\4\5\6\7\8\9
40%% Exclamation \! Double quote \" Hash (number) \#
41%% Dollar \$ Percent \% Ampersand \&
42%% Acute accent \' Left paren \( Right paren \)
43%% Asterisk \* Plus \+ Comma \,
44%% Minus \- Point \. Solidus \/
45%% Colon \: Semicolon \; Less than \<
46%% Equals \= Greater than \> Question mark \?
47%% Commercial at \@ Left bracket \[ Backslash \\
48%% Right bracket \] Circumflex \^ Underscore \_
49%% Grave accent \` Left brace \{ Vertical bar \|
50%% Right brace \} Tilde \~}
51%%
52%\iffalse
53%</package>
54%\fi
55%
56% \begin{meta-comment}
57%
58%<*driver>
59\input{mdwtools}
60\describespackage{mdwkey}
61\mdwdoc
62%</driver>
63%
64% \end{meta-comment}
65%
66%^^A-------------------------------------------------------------------------
67% \section{User's guide}
68%
69% This is a key/value-pair parser, rather like the one in David Carlisle's
70% \package{keyval} package but a little more powerful. There's no problem
71% with having both in the same program.
72%
73% \subsection{Terminology}
74%
75% A \emph{key-value pair} is a pair \syntax{<key> `=' <value>}, where the
76% \lit{=} appears at the topmost bracing level. A \emph{tag} is just a
77% single \syntax{<key>}. A \emph{list} is a sequence of key-value pairs and
78% tags separated by commas \lit{,} at the topmost bracing level. A \<key> or
79% \<value> has a leading and/or trailing space removed, if there are any, and
80% if the whole thing is enclosed in braces, then the braces are removed.
81% Examples:
82% \begin{itemize} \synshorts
83% \item "foo = bar" is a key-value pair. The key is `foo' and the value is
e8e9e5d8 84% `bar'.
281c40ee 85% \item `{foo = bar}' is a tag. The key is `foo = bar'.
86% \item `foo = { bar }' is a key-value pair. The key is `foo' and
87% the value is ` bar ' (with the leading and trailing spaces).
88% \item `foo = { bar }x' is a key-value pair. The key is `foo' and
89% the value is `{ bar }x'.
90% \item `one, two' is a list of two tags, `one' and `two'.
91% \item `one, {two, three}' is a list of two tags, `one' and `two, three'.
92% \end{itemize}
93% There is no way to get an unmatched brace into a \<key> or \<value> without
94% stupid catcode tricks.
95%
96% \subsection{Using the system}
97%
98% \DescribeMacro\mkdef
99% New key names, and what to do when they're encountered, is all defined
100% using |\mkdef|. Keys are gathered into \emph{groups}, so that lots of
101% people can use the system without treading on their toes. I recommend that
102% people use \syntax{<package-name>":"<label>} for their group names.
103%
104% There's a lot which can be done using |\mkdef|.
105% \begin{itemize} \synshorts
106% \item "\\mkdef{"<group>"}{"<key>"}{"<stuff>"}" will perform <stuff> when
107% <key> is given a value: the value is available as "#1" in <stuff>.
108% \item "\\mkdef{"<group>"}{"<key>"}["<default>"]{"<stuff>"}" is the same,
109% but additionally if <key> is found as a tag, then treat it as if we found
e8e9e5d8 110% <key>"={"<default>"}" instead.
281c40ee 111% \item "\\mkdef{"<group>"}{"<key>"}*{"<stuff>"}" will perform <stuff> when
e8e9e5d8 112% <key> is found as a tag.
281c40ee 113% \item "\\mkdef*{"<group>"}{"<stuff>"}" will perform <stuff> when an unknown
114% key (one for which there is no specific definition in this group) is
115% given a value: the key is available as "#1" and the value as "#2" in
e8e9e5d8 116% <stuff>.
281c40ee 117% \item "\\mkdef*{"<group>"}["<default>"]{"<stuff>"}" is the same, but
e8e9e5d8 118% additionally if an unknown key is found as a tag then treat it as if
281c40ee 119% it had been assigned the value <default> instead.
120% \item "\\mkdef*{"<group>"}*{"<stuff>"}" will perform <stuff> when an
121% unknown key is found as a tag: the key is available as "#1" in <stuff>.
122% \end{itemize}
123%
124% \DescribeMacro\mkparse
125% All that remains now is to learn how to use the thing. Once you have a
126% list, you can say \syntax{"\\mkparse{"<group>"}{"<list>"}"} to perform all
127% the appropriate actions. (This will mess up |\toks0| and |\next@| and some
128% other standard scratch macros.)
129%
130%^^A-------------------------------------------------------------------------
131% \implementation
132% \section{Implementation}
133%
134% \begin{macrocode}
135%<*package|macro>
136% \end{macrocode}
137%
138% \subsection{Provide bits of \LaTeX\ for plain \TeX}
139%
140% This lot is the infrastructure needed to make the macros work under Plain
141% \TeX.
142%
143% \begin{macrocode}
144%<*plain>
145\edef\done{\catcode`\noexpand\@=\the\catcode`\@}
146\catcode`\@=11
147\def\@gobble#1{}
148\def\@firstoftwo#1#2{#1}
149\def\@firstofthree#1#2#3{#1}
150\def\@secondoftwo#1#2{#2}
151\def\@ifnextchar#1#2#3{%
152 \def\next@{%
153 \ifx\char@#1\expandafter\@firstoftwo%
154 \else\expandafter\@secondoftwo\fi{#2}{#3}%
155 }%
156 \@ifn@i%
157}
158\def\@ifn@i{\futurelet\char@\@ifn@ii}
159\def\@ifn@ii{%
160 \ifx\char@\@sptoken\expandafter\@ifn@i\else%
161 \expandafter\next@\fi%
162}
163\def\@ifstar#1#2{%
164 \def\next@{%
165 \ifx\char@*\expandafter\@firstofthree%
166 \else\expandafter\@secondoftwo\fi{#1}{#2}%
167 }%
168 \futurelet\char@\next@%
169}
170\def\@namedef#1{\expandafter\def\csname#1\endcsname}
171\def\PackageError#1#2#3{\errhelp{#3}\errmessage{#1 error: #2}\errhelp{}}
172%</plain>
173% \end{macrocode}
174%
175% \subsection{Removing spaces}
176%
177% \begin{macro}{\withoutspaces}
178%
179% Saying \syntax{"\\withoutspaces{"<macro>"}{"<stuff>"}"} calls \<macro>,
180% passing it the argument which is \<stuff>, shorn of (a) a single leading
181% and/or space token, and (b) a single layer of |{|\ldots|}| grouping, if
182% present. This improves over \package{keyval}'s attempt by being a little
183% simpler and only stripping off one layer of braces.
184%
185% \begin{macrocode}
186\def\q@delim{\q@delim}
187\def\next@#1{%
188\let\@sptoken=#1
189\def\withoutspaces##1##2{%
190 \def\next@{##1}\futurelet\char@\wsp@i##2%
191 \q@delim#1\q@delim\q@delim\relax%
192}
193\def\wsp@i{%
194 \ifx\char@\@sptoken\expandafter\wsp@ii%
195 \else\expandafter\wsp@iii\fi%
196}
197\def\wsp@ii#1{\wsp@iii}
198\def\wsp@iii##1#1\q@delim##2\relax{\wsp@iv##1\q@delim\relax}
199\def\wsp@iv##1\q@delim##2\relax{\next@{##1}}
200}\next@{ }
201% \end{macrocode}
202%
203% \end{macro}
204%
205% \begin{macro}{\withoutspacesdef}
206%
207% As a trivial but useful application of the above,
208% \syntax{"\\withoutspacesdef{"<name>"}{"<stuff>"}"} defines \<name> as a
209% macro containing \<stuff> with a leading and trailing space deleted and a
210% level of bracing removed.
211%
212% \begin{macrocode}
213\def\withoutspacesdef#1#2{\withspaces\toks@{#2}\edef#1{\the\toks@}}
214% \end{macrocode}
215%
216% \end{macro}
217%
218% \subsection{Parsing key/value lists}
219%
220% \begin{macro}{\mkparse}
221%
222% The main parser macro. Stash some information away and then start on the
223% main loop.
224%
225% \begin{macrocode}
226\def\mkparse#1#2{%
227 \def\mk@group{#1}%
228 \def\mk@{mk$#1$}%
229 \mk@loop!#2,\q@delim,\relax%
230}
231% \end{macrocode}
232%
233% And already the subtlety begins. Note that there's a leading \lit{!} at
234% the front of the token list. This prevents our delimited argument from
235% being entirely brace-enclosed, which in turn stops \TeX\ from removing it
236% until we're good and ready.
237%
238% This doesn't trap empty items -- that happens later.
239%
240% \begin{macrocode}
241\def\mk@loop#1,{%
242 \expandafter\def\expandafter\next@\expandafter{\@gobble#1}%
243 \ifx\next@\q@delim\expandafter\mk@x%
244 \else\mk@i#1=\q@delim\expandafter\mk@loop\expandafter!\fi%
245}
246\def\mk@x#1\relax{\relax}
247% \end{macrocode}
248%
249% Now we have to split an entry into a key and a value. If we have
250% \<key>|=|\<value> then |#1| = |!|\<key> and |#2| = \<value>|=|; if we have
251% only \<key>, then |#1| = |!|\<key> as before, and |#2| is empty. The first
252% thing to do is strip the |!| and spaces from |#1|. If |#2| is empty then
253% we're done with this stage and can just call |\mk@k| with what we've got;
254% otherwise we swap the trailing |=| on |#2| for a leading |!| and strip that
255% off, and then call |\mk@kv| with the answer.
256%
257% \begin{macrocode}
258\def\mk@i#1=#2\q@delim{%
259 \expandafter\withoutspaces\expandafter%
260 \mk@ii\expandafter{\@gobble#1}{#2}%
261}
262\def\mk@ii#1#2{%
263 \ifx\q@delim#2\q@delim\mk@k{#1}%
264 \else\mk@iii{#1}!#2\q@delim\fi%
265}
266\def\mk@iii#1#2=\q@delim{%
267 \expandafter\withoutspaces\expandafter%
268 \mk@iv\expandafter{\@gobble#2}{#1}%
269}
270\def\mk@iv#1#2{\mk@kv{#2}{#1}}
271% \end{macrocode}
272%
273% We just have \<key>, shorn of spaces and outer braces. If it's empty then
274% the whole entry was empty and we should ignore it. Otherwise, if there's a
275% defined command for handling the token then we use that; if not, then we
276% look for a general unknown-key command. If nothing works, we raise an
277% error.
278%
279% \begin{macrocode}
280\def\mk@k#1{%
281 \ifx\q@delim#1\q@delim\else%
282 \expandafter\let\expandafter\next@\csname\mk@!#1\endcsname%
283 \ifx\next@\relax%
284 \expandafter\let\expandafter\next@\csname\mk@*!\endcsname%
285 \ifx\next@\relax\mk@err{#1}%
286 \else\next@{#1}\fi%
287 \else\next@\fi%
288 \fi%
289 }
290% \end{macrocode}
291%
292% We have a \<key> and a \<value>, both stripped of spaces and braces. If
293% there's a command for this key, then give it the value; otherwise look for
294% a general unknown-key-with-value command. If nothing works, raise an
295% error.
296%
297% \begin{macrocode}
298\def\mk@kv#1#2{%
299 \expandafter\let\expandafter\next@\csname\mk@=#1\endcsname%
300 \ifx\next@\relax%
301 \expandafter\let\expandafter\next@\csname\mk@*=\endcsname%
302 \ifx\next@\relax\mk@err{#1}%
303 \else\next@{#1}{#2}\fi%
304 \else\next@{#2}\fi%
305}
306% \end{macrocode}
307%
308% How to raise an error. Not so difficult.
309%
310% \begin{macrocode}
311\def\mk@err#1{%
312 \PackageError{mdwkey}{Key `#1' not found in group `\mk@group'}{%
313 I've never heard of the key you tried to set. I'm going to ignore it.
314 }
315}
316% \end{macrocode}
317%
318% \end{macro}
319%
320% \subsection{Defining keys}
321%
322% \begin{macro}{\mkdefkey}
323%
324% This is all quite dull, really. I tried to merge the two cases, but it
325% failed because I can't pass around macro parameter names through
326% |\@ifnextchar| and their friends. If anyone has any bright ideas, I'd be
327% delighted.
328%
329% \begin{macrocode}
330\def\mkdef{\@ifstar\mkdef@star@\mkdef@}
331\def\mkdef@#1#2{%
332 \@ifstar%
333 {\mkdef@ii{#1}{#2}}%
334 {\@ifnextchar[%
335 {\mkdef@iii{#1}{#2}}%
336 {\mkdef@i{#1}{#2}}}%
337}
338\def\mkdef@i#1#2{\@namedef{mk$#1$=#2}##1}
339\def\mkdef@ii#1#2{\@namedef{mk$#1$!#2}}
340\def\mkdef@iii#1#2[#3]{%
341 \toks@{#3}%
342 \expandafter\edef\csname mk$#1$!#2\endcsname%
343 {\expandafter\noexpand\csname mk$#1$=#2\endcsname{\the\toks@}}%
344 \@namedef{mk$#1$=#2}##1%
345}
346\def\mkdef@star@#1{%
347 \@ifstar%
348 {\mkdef@star@ii{#1}}%
349 {\@ifnextchar[%
350 {\mkdef@star@iii{#1}}%
351 {\mkdef@star@i{#1}}}%
352}
353\def\mkdef@star@i#1{\@namedef{mk$#1$*=}##1##2}
354\def\mkdef@star@ii#1{\@namedef{mk$#1$*!}##1}
355\def\mkdef@star@iii#1[#2]{%
356 \toks@{#2}%
357 \expandafter\edef\csname mk$#1$*!\endcsname##1%
358 {\expandafter\noexpand\csname mk$#1$*=\endcsname{##1}{\the\toks@}}%
359 \@namedef{mk$#1$*=}##1##2%
360}
361% \end{macrocode}
362%
363% \end{macro}
364%
365% And with that, we're done.
366%
367% \begin{macrocode}
368%<+plain>\done
369%</package|macro>
370% \end{macrocode}
371%
372% \hfill Mark Wooding, \today
373%
374% \Finale
375%
376\endinput