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