chiark / gitweb /
.mdw-build.conf: Don't try `vpath' builds with this package.
[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 % \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