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