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