1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
|
% \iffalse meta-comment
%
% File: expkv-pop.dtx Copyright (C) 2020-2023 Jonathan P. Spratte
%
% This work may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this license or
% (at your option) any later version. The latest version of this license is in
% the file:
%
% http://www.latex-project.org/lppl.txt
%
% ------------------------------------------------------------------------------
%
%<*driver>^^A>>=
\def\expkvDocNoGenerate{}
\input expkv-bundle.ins
\generate{\file{expkv-pop.sty}{\from{expkv-pop.dtx}{pkg}}}
\generate{\file{expkv-pop.tex}{\from{expkv-pop.dtx}{tex}}}
\generate{\file{t-expkv-pop.tex}{\from{expkv-pop.dtx}{ctx}}}
\endbatchfile
%</driver>^^A=<<
% \fi
%
% \section{\expkvp}
%^^A the LaTeX package >>=
% \subsection{The \LaTeX\ Package}
% Set up the \LaTeX\ package.
% \gobbledocstriptag
%<*pkg>
% \begin{macrocode}
\RequirePackage{expkv}
\def\ekvp@tmp
{%
\ProvidesFile{expkv-pop.tex}%
[\ekvpDate\space v\ekvpVersion\space a prefix oriented parser]%
}
\input{expkv-pop.tex}
\ProvidesPackage{expkv-pop}%
[\ekvpDate\space v\ekvpVersion\space a prefix oriented parser]
% \end{macrocode}
% \gobbledocstriptag
%</pkg>
%^^A=<<
%^^A the ConTeXt module >>=
% \subsection{The \ConTeXt\ module}
% Set up the \ConTeXt\ module.
% \gobbledocstriptag
%<*ctx>
% \begin{macrocode}
\writestatus{loading}{ConTeXt User Module / expkv-pop}
\usemodule[expkv]
\unprotect
\input expkv-pop.tex
\writestatus{loading}
{ConTeXt User Module / expkv-pop / Version \ekvpVersion\space loaded}
\protect\endinput
% \end{macrocode}
% \gobbledocstriptag
%</ctx>
%^^A=<<
%^^A main file >>=
% \subsection{The Generic Code}
% And another generic code package we need to set up.
% \gobbledocstriptag
%<*tex>
%
% Loading the generic \expkv\ package if it wasn't already loaded, utilizing
% that \expkv\ prevents itself from loading multiple times.
% \begin{macrocode}
\input expkv
% \end{macrocode}
%
% Introduce an own guard against being loaded multiple times:
% \begin{macrocode}
\expandafter\ifx\csname ekvpVersion\endcsname\relax
\else
\expandafter\endinput
\fi
% \end{macrocode}
%
% \begin{macro}{\ekvpVersion,\ekvpDate}
% Specify the own version and date
% \begin{macrocode}
\def\ekvpVersion{1.0}
\def\ekvpDate{2023-01-23}
% \end{macrocode}
% \end{macro}
%
% Reporting back who we are for \LaTeX\ (the package will have set up things for
% us).
% \begin{macrocode}
\csname ekvp@tmp\endcsname
% \end{macrocode}
%
% Package internal category code setup (stored to restore it at the end -- we
% have to be careful to not lose this definition of |\ekvp@tmp| inside the
% package).
% \begin{macrocode}
\expandafter\chardef\csname ekvp@tmp\endcsname=\catcode`\@
\catcode`\@=11
% \end{macrocode}
%
% \subsubsection{Parsing}^^A>>=
%
% \begin{macro}{\ekvpParse}
% \begin{macro}[internal]
% {
% \ekvpParse@a,\ekvpParse@b,\ekvpParse@c,
% \ekvpParse@unsafe,\ekvpParse@unsafe@auto
% }
% Parsing should be done in two steps of expansion, hence we put
% |\unexpanded\expanded| around it. Next we check whether the parser is
% defined, and afterwards run |\ekvparse|. For that we expand it once and
% remove the |\unexpanded| which it uses itself.
% \begin{macrocode}
\long\def\ekvpParse#1#2%
{%
\ekv@unexpanded\ekv@expanded
{{\expandafter\ekvpParse@a\detokenize{#1}\ekv@mark{#2}}}%
}
\def\ekvpParse@a#1\ekv@mark
{%
\ekv@ifdefined{ekvp@@p@#1}%
{\expandafter\ekvpParse@b\csname ekvp@@p@#1\endcsname}%
{\ekvp@err@unknownparser{#1}\@gobble}%
}
\def\ekvpParse@b#1%
{%
\ekv@ifdefined{#1{ppa}}%
{%
\expandafter\ekvpParse@c\expandafter
{\expandafter#1\csname#1{ppa}\endcsname}%
}%
{\ekvpParse@c{#1{}}}%
}
\def\ekvpParse@c#1#2#3{#2#3}
\ekv@expargtwice{\long\def\ekvpParse@c#1#2}%
{\expandafter\ekvpParse@c\ekvparse{\ekvp@k#1}{\ekvp@p#1}{#2}}
\long\def\ekvpParse@unsafe#1#2#3{\ekv@unexpanded\ekv@expanded{{#2#3}}}
\ekv@expargtwice{\long\def\ekvpParse@unsafe@auto#1#2#3}
{\expandafter\ekvpParse@unsafe\ekvparse{\ekvp@k#1#2}{\ekvp@p#1#2}{#3}}
\ekv@expargtwice{\long\def\ekvpParse@unsafe#1#2}%
{\expandafter\ekvpParse@unsafe\ekvparse{\ekvp@k#1{}}{\ekvp@p#1{}}{#2}}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@k}
% For |NoVal| we use the parser specific rule.
% \begin{macrocode}
\def\ekvp@k#1{\csname #1k\endcsname#1}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@ifspace,\ekvp@ifspace@}
% This checks whether a space is inside its argument (gobbling up to the first
% space and checking for an empty remainder).
% \begin{macrocode}
\long\def\ekvp@ifspace#1%
{%
\ekvp@ifspace@#1 \ekv@ifempty@B\ekv@ifempty@false
\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
}
\long\def\ekvp@ifspace@#1 % keep this space
{\ekv@ifempty@\ekv@ifempty@A}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@p}
% \begin{macro}[internal]{\ekvp@prefix}
% \begin{macro}[internal]{\ekvp@prefix@a,\ekvp@prefix@b}
% If there should be a prefix there needs to be a space, so we check for one.
% Then we split the first prefix of the rest.
% \begin{macrocode}
\ekv@exparg{\long\def\ekvp@prefix#1}%
{%
\ekvp@ifspace{#1}%
{\ekvp@prefix@a#1\ekv@stop}%
{\ekvp@noprefix{#1}}%
}
% \end{macrocode}
% A prefix is parsed with a few additional logical groups. The input |#1| is
% the parser's name macro, |#2| is the full item which should be parsed. And
% we place an additional group in which the prefix macros will be collected.
% \begin{macrocode}
\ekv@exparg{\long\def\ekvp@p#1#2#3}{\ekvp@prefix{#3}#1{#2}{#3}}
% \end{macrocode}
% The prefix is |\detokenize|d and forwarded. Then we check whether it's a
% defined type or prefix.
% \begin{macrocode}
\long\def\ekvp@prefix@a#1 % keep this space
{\expandafter\ekvp@prefix@b\detokenize{#1}\ekv@mark{#1}}
\ekv@exparg{\long\def\ekvp@prefix@b#1\ekv@mark#2#3\ekv@stop#4}%
{%
\ekv@ifdefined{#4{pt}@#1}
{\expandafter\ekvp@prefix@pt\csname #4{pt}@#1\endcsname{#1}}%
{%
\ekv@ifdefined{#4{pp}@#1}%
{\expandafter\ekvp@prefix@pp\csname #4{pp}@#1\endcsname}%
{\@firstoftwo{\ekvp@noprefix{#2 #3}}}%
}%
{#3}#4%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@prefix@pt,\ekvp@prefix@pt@}
% A type ends the prefix parsing (every item can only have one type). The
% argument |#1| is that type macro, and in |#2| is the types name. |#3| then
% contains the name of the element, |#4| is the parser's name macro (which is
% no longer needed here), |#5| is the list of prefix macros, |#6| the entire
% unchanged item, and |#7| is the value provided to the current item. The
% prefix macro list will be stepped through, each macro will get the type
% name, element name, and complete unprocessed item, whereas the type macro
% will not receive the type string (but instead the value), which is why we
% put a |\@firstoftwo| there to remove the type. |\ekvp@prefix@pt@| is a
% helper to fetch the first prefix macro (or the type should |#5| be empty).
% \begin{macrocode}
\long\def\ekvp@prefix@pt#1#2#3#4#5#6#7%
{%
\ekvp@prefix@pt@{#2}{#3}{#6}#5{\@firstoftwo#1}{#7}\ekvpEOT
\ekv@unexpanded{\ekvpEOA{#6}}%
}
\long\def\ekvp@prefix@pt@#1#2#3#4{#4{#1}{#2}{#3}}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@prefix@pp}
% A prefix macro is added to the list of prefixes (argument |#4|) and the next
% prefix is searched.
% \begin{macrocode}
\ekv@exparg{\long\def\ekvp@prefix@pp#1#2#3#4}{\ekvp@prefix{#2}#3{#4#1}}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@noprefix}
% If no prefix was found (either because there was no space, or the first
% space delimited thing wasn't a valid prefix) we check if there is a notype
% rule or throw an error.
% \begin{macrocode}
\long\def\ekvp@noprefix#1#2%
{%
\ekv@ifdefined{#2{pn}}
{\expandafter\ekvp@prefix@pt\csname #2{pn}\endcsname{}{#1}}
\ekvp@err@missingtype
#2%
}
% \end{macrocode}
% \end{macro}
%
%^^A=<<
%
% \subsubsection{Defining Parsers}^^A>>=
%
% \begin{macro}{\ekvpNewParser}
% \begin{macro}[internal]{\ekvpNewParser@}
% To define a new parser we need to assert that it doesn't already exists (if
% so we throw an error) and define the parser name. A parser name is defined
% as the macro \cs[no-index]{ekvp@@p@\meta{name}}, that macro will take one
% argument and build the name of a prefix or type. Additionally a new parser
% gets its |NoVal| rule set up as the default rule.
% \begin{macrocode}
\protected\long\def\ekvpNewParser#1%
{\expandafter\ekvpNewParser@\detokenize{#1}\ekv@mark}
\protected\def\ekvpNewParser@#1\ekv@mark
{%
\ekv@ifdefined{ekvp@@p@#1}%
{\ekvp@errm{Parser `#1' already defined}}%
{%
\expandafter\def\csname ekvp@@p@#1\endcsname##1{ekvp@@##1@#1}
\expandafter
\let\csname\csname ekvp@@p@#1\endcsname k\endcsname\ekvp@k@default
}%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpValueAlwaysRequired}
% \begin{macro}[internal]{\ekvpValueAlwaysRequired@}
% This just changes the default |NoVal| rule to throw an error.
% \begin{macrocode}
\protected\long\def\ekvpValueAlwaysRequired#1%
{\ekvp@parser@def{#1}\ekvpValueAlwaysRequired@{}}
\protected\def\ekvpValueAlwaysRequired@#1%
{\expandafter\let\csname#1k\endcsname\ekvp@err@noval}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpDefNoValue}
% \begin{macro}[internal]{\ekvpDefNoValue@}
% \begin{macrocode}
\protected\long\def\ekvpDefNoValue#1%
{\ekvp@parser@def{#1}\ekvpDefNoValue@\@gobble}
\protected\long\def\ekvpDefNoValue@#1#2%
{%
\def\ekvp@tmp##1{\ekv@unexpanded{#2}}%
\ekv@exparg{\long\expandafter\def\csname#1k\endcsname##1##2##3}%
{\ekvp@tmp{##3}}%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpUseNoValueMarker}
% \begin{macro}[internal]{\ekvpUseNoValueMarker@}
%
% \begin{macrocode}
\protected\long\def\ekvpUseNoValueMarker#1%
{\ekvp@parser@def{#1}\ekvpUseNoValueMarker@\@gobble}
\protected\long\def\ekvpUseNoValueMarker@#1#2%
{%
\long\expandafter\edef\csname#1k\endcsname##1##2##3%
{\ekv@unexpanded{\ekvp@p}##1{##2}{##3}{\ekv@unexpanded{#2}}}%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpDefNoValuePrefix}
% \begin{macro}[internal]{\ekvpDefNoValuePrefix@,\ekvpDefNoValuePrefix@@}
% This might be the most complicated of the |NoVal|-behaviour changes. The
% first two steps should be clear.
% \begin{macrocode}
\protected\long\def\ekvpDefNoValuePrefix#1%
{\ekvp@parser@def{#1}\ekvpDefNoValuePrefix@\@gobbletwo}
\protected\def\ekvpDefNoValuePrefix@#1%
{\expandafter\ekvpDefNoValuePrefix@@\csname#1{ppn}\endcsname#1}
% \end{macrocode}
% This step is the complicated one combining the effects of \cs{ekvpDefPrefix}
% with the standard |NoVal|-rule (but for which the no-value marker might have
% been changed), so here's what happens:
% We set up a temporary meaning for the control sequence name
% forwarded as |#1| to extract the no-value marker of the current parser. Then
% we set up the |NoVal|-macro of the current parser to expand to the normal
% parsing route, there we inject |#1| as the first prefix macro in the
% corresponding first argument of \cs[no-index]{ekvp@p}, and we extract the
% no-value marker with the |\expandafter#1|-chain. This last step might
% actually fail badly if \cs{ekvpValueAlwaysRequired} or \cs{ekvpDefNoValue}
% was used.
% \begin{macrocode}
\protected\long\def\ekvpDefNoValuePrefix@@#1#2%
{%
\long\def#1\ekvp@p##1##2##3%
{%
\ekv@unexpanded{\ekvp@p}%
####1{####2\ekv@unexpanded{#1}}{####3}{\ekv@unexpanded{##3}}%
}%
\long\expandafter\edef\csname#2k\endcsname##1##2##3%
{\expandafter\expandafter\expandafter#1\csname#2k\endcsname{}{}{}}%
\ekvp@parser@def@prefix#1%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpDefAutoPrefix}
% \begin{macro}[internal]{\ekvpDefAutoPrefix@}
% \begin{macrocode}
\protected\long\def\ekvpDefAutoPrefix#1%
{\ekvp@parser@def{#1}\ekvpDefAutoPrefix@\@gobbletwo}
\protected\long\def\ekvpDefAutoPrefix@#1%
{\expandafter\ekvp@parser@def@prefix\csname#1{ppa}\endcsname}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[internal]{\ekvp@parser@def,\ekvp@parser@def@}
% This just makes sure that the parser is defined and builds the parser name.
% |#1| should be the user-level parser name, |#2| the macro that gets the
% code-level parser forwarded, and |#3| should gobble all additional arguments
% needed by whichever frontend macro this is used for in case of an error.
% \begin{macrocode}
\long\def\ekvp@parser@def#1%
{\expandafter\ekvp@parser@def@\detokenize{#1}\ekv@mark}
\def\ekvp@parser@def@#1\ekv@mark#2#3%
{%
\ekv@ifdefined{ekvp@@p@#1}%
{\ekv@exparg{#2}{\csname ekvp@@p@#1\endcsname}}%
{\ekvp@errm{Parser `#1' not defined}#3}%
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ekvpDefPrefix}
% \begin{macro}[internal]{\ekvpDefPrefix@,\ekvp@parser@def@prefix}
% A prefix is stored as a macro. At first we define a temporary meaning just
% to make sure that the user uses only the first three arguments.
% The real macro is a bit more complicated. It places the user provided before
% action (|#3|) where it currently is, the after action (|#4|) after
% |\ekvpEOT| (so after the type action). It also fetches the next prefix or
% the type macro and forwards the arguments to it.
% \begin{macrocode}
\protected\long\def\ekvpDefPrefix#1%
{\ekvp@parser@def{#1}\ekvpDefPrefix@\@gobblethree}
\protected\long\def\ekvpDefPrefix@#1#2%
{\expandafter\ekvp@parser@def@prefix\csname#1{pp}@\detokenize{#2}\endcsname}
\protected\long\def\ekvp@parser@def@prefix#1#2#3%
{%
\def#1##1##2##3{#2#3}%
\long\def#1##1##2##3##4##5\ekvpEOT
{\ekv@unexpanded{#2}##4{##1}{##2}{##3}##5\ekvpEOT\ekv@unexpanded{#3}}%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpDefPrefixLet,\ekvpDefPrefixStore}
% These are just special cases of |\ekvpDefPrefix|, so nothing complicated
% here.
% \begin{macrocode}
\ekv@exparg{\protected\long\def\ekvpDefPrefixLet#1#2#3#4#5}%
{\ekvpDefPrefix{#1}{#2}{\ekvpProtect{\let#3= #4}}{\ekvpProtect{\let#3= #5}}}
\ekv@exparg{\protected\long\def\ekvpDefPrefixStore#1#2#3#4#5}%
{%
\ekvpDefPrefix{#1}{#2}%
{\ekvpProtect{\edef#3{\ekv@unexpanded{#4}}}}%
{\ekvpProtect{\edef#3{\ekv@unexpanded{#5}}}}%
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ekvpDefType,\ekvpDefNoType}
% \begin{macro}[internal]{\ekvpDefType@,\ekvpDefNoType@,\ekvpDefType@NoType}
% A type macro and a notype-macro are pretty similar, the whole difference is
% the naming scheme. The macro leaves the user definition in the input and
% places the |\ekvpEOP| and |\ekvpEOT| markers.
% \begin{macrocode}
\protected\long\def\ekvpDefType#1{\ekvp@parser@def{#1}\ekvpDefType@\@gobbletwo}
\protected\long\def\ekvpDefType@#1#2%
{\ekvpDefType@NoType{#1{pt}@\detokenize{#2}}}%
\protected\long\def\ekvpDefNoType#1{\ekvp@parser@def{#1}\ekvpDefNoType@\@gobble}
\protected\long\def\ekvpDefNoType@#1{\ekvpDefType@NoType{#1{pn}}}
\protected\long\def\ekvpDefType@NoType#1#2%
{%
\long\expandafter\def\csname#1\endcsname##1##2##3\ekvpEOT
{\ekv@unexpanded{\ekvpEOP{##2}#2\ekvpEOT{##2}}}%
}%
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpLet}
% \begin{macro}[internal]{\ekvpLet@a,\ekvpLet@b,\ekvpLet@c,\ekvpLet@d}
% Letting has an optional argument, so we can't use |\ekvp@parser@def|
% directly here, first we need to convert the optional argument to a normal
% one so that we can gobble it normally. Then we check that the two parsers
% are defined, and afterwards that the copy macro is. If all this is correct
% we do the |\let|. Since arguments are shuffled around here, I'll list them
% each time they get reordered (some might be curried).
%
% |#1| is the parser name, |#2| the type, |#3| the new \prefix/\type\ name,
% |#4| the optional other parser name, |#5| the already existing
% \prefix/\type\ which should be copied.
% \begin{macrocode}
\protected\long\def\ekvpLet#1#2#3{\ekvoptarg{\ekvpLet@a{#1}{#2}{#3}}{#1}}
\protected\long\def\ekvpLet@a#1%
{\ekvp@parser@def{#1}\ekvpLet@b{\@firstoftwo\@gobblethree}}
\protected\long\def\ekvpLet@b#1#2#3#4%
{\ekvp@parser@def{#4}\ekvpLet@c{\@firstoftwo\@gobblethree}#1{#2}{#3}}
% \end{macrocode}
% |#1| the other parser's macro, |#2| the parser's macro, |#3| the type, |#4|
% the new \prefix/\type, |#5| the already existing \prefix/\type.
% \begin{macrocode}
\protected\long\def\ekvpLet@c#1#2#3%
{%
\ekv@ifdefined{ekvpLet@@\detokenize{#3}}%
{\expandafter\ekvpLet@d\csname ekvpLet@@\detokenize{#3}\endcsname{#1}{#2}}%
{\ekvp@errm{unknown type \detokenize{#3}}\@gobbletwo}%
}
% \end{macrocode}
% |#1| the type's macro, |#2| the other parser's macro, |#3| the parser's
% macro, |#4| the new \prefix/\type, |#5| the already existing \prefix/\type.
% \begin{macrocode}
\protected\long\def\ekvpLet@d#1#2#3#4#5%
{%
\ekv@ifdefined{#2{#1}@\detokenize{#5}}%
{%
\expandafter\let
\csname#3{#1}@\detokenize{#4}\expandafter\endcsname
\csname#2{#1}@\detokenize{#5}\endcsname
}%
{\ekvp@errm{Undefined prefix/type \detokenize{#5} can't be copied}}%
}
\def\ekvpLet@@prefix{pp}
\def\ekvpLet@@type{pt}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
%^^A=<<
%
% \subsubsection{\texttt{NoVal} Handling}^^A>>=
%
% The idea of |NoVal| handling is taken from \pkg{expl3}. We define a marker
% (or directly use \pkg{expl3}'s). The defined test is not as robust as
% \pkg{expl3}'s, but pretty fast (and based on |\ekv@ifempty|).
%
% \begin{macro}{\ekvpIfNoVal}
% \begin{macro}[internal]{\ekvp@novalflag,\ekvp@ifnoval}
% \begin{macro}[internal]{\ekvp@k@default}
% \begin{macrocode}
\ekv@ifdefined{c_novalue_tl}
{\expandafter\let\expandafter\ekvp@novalflag\csname c_novalue_tl\endcsname}
{%
\begingroup
\lccode`\Z=`\-
\lccode`\:=`\-
\lccode`\N=\z@
\lccode`\V=\z@
\lowercase{\endgroup\def\ekvp@novalflag{ZNoValue:}}%
}
\def\ekvpIfNoVal#1%
{%
\long\def\ekvpIfNoVal##1%
{%
\ekvp@ifnoval{}##1{}?!#1??!\ekv@ifempty@B\ekv@ifempty@true
\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
}%
\long\def\ekvp@ifnoval##1#1##2?##3?!{\ekv@ifempty@\ekv@ifempty@A##1##2}%
\long\def\ekvp@k@default##1##2##3{\ekvp@p##1{##2}{##3}{#1}}%
}
\expandafter\ekvpIfNoVal\expandafter{\ekvp@novalflag}
\let\ekvp@novalflag\ekvp@undefined
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%^^A=<<
%
% \subsubsection{Assertions}^^A>>=
%
% Assertions are just wrappers around user provided code to throw errors. Each
% of them
%
% \begin{macro}{\ekvpAssertIf,\ekvpAssertIfNot}
% \begin{macro}[internal]{\ekvpAssertIf@,\ekvpAssertIf@good,\ekvpAssert@notgood}
% \begin{macrocode}
\def\ekvpAssertIf{\romannumeral\ekv@alignsafe\ekvoptarg{\ekvpAssertIf@{}}{EOT}}
\def\ekvpAssertIfNot
{\romannumeral\ekv@alignsafe\ekvoptarg{\ekvpAssertIf@\else}{EOT}}
\long\def\ekvpAssertIf@#1#2#3%
{%
#3#1\ekvpAssertIf@good\fi
\ekvpAssert@notgood{#2}%
}
\long\def\ekvpAssertIf@good\fi\ekvpAssert@notgood#1#2%
{\fi\ekv@endalignsafe\ekv@zero}
\long\def\ekvpAssert@notgood#1%
{%
\ekv@ifdefined{ekvpAssert@@#1}%
{\csname ekvpAssert@@#1\endcsname}%
{\ekvp@err@unknownmarker{#1}\ekvpAssert@@EOA}%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ekvpAssertTF,\ekvpAssertTFNot}
% \begin{macro}[internal]{\ekvpAssertTF@}
% \begin{macrocode}
\def\ekvpAssertTF{\ekvoptarg{\ekvpAssertTF@{}}{EOT}}
\def\ekvpAssertTFNot{\ekvoptarg{\ekvpAssertTF@{\@secondoftwo\@firstoftwo}}{EOT}}
\long\def\ekvpAssertTF@#1#2#3%
{%
\ekv@alignsafe
#3#1%
{\ekv@endalignsafe\@gobble}%
{\romannumeral\ekvpAssert@notgood{#2}}%
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}
% {
% \ekvpAssertValue,\ekvpAssertNoValue,\ekvpAssertOneValue,
% \ekvpAssertTwoValues
% }
% \begin{macro}[internal]{\ekvpAssert@further}
% \begin{macro}[internal]{\ekvp@assert@num@args}
% These here are special cases of assertions. First we define a helper, the
% following might look like a recursive call, but we'll redefine the helper
% later and need this definition only temporary.
% \begin{macrocode}
\ekv@exparg{\def\ekvpAssert@further#1#2#3}%
{\ekvoptarg{\ekvpAssert@further{#1}{#2}{#3}}{EOT}}
% \end{macrocode}
% Now the definitions, we just need to set up the tests and corresponding
% error messages. All this passes through the \cs{ekvpAssertTF} check (which
% we shortcut to its internal auxiliary function here).
% \begin{macrocode}
\ekv@exparg{\def\ekvpAssertValue}%
{\ekvpAssert@further{\@secondoftwo\@firstoftwo}{\ekvpIfNoVal}{missing value}}
\ekv@exparg{\def\ekvpAssertNoValue}%
{\ekvpAssert@further{}{\ekvpIfNoVal}{superfluous value}}
\ekv@exparg{\def\ekvpAssertOneValue}%
{\ekvpAssert@further{}{\ekvp@assert@num@args\@gobble}{argument count != 1}}
\ekv@exparg{\def\ekvpAssertTwoValues}%
{\ekvpAssert@further{}{\ekvp@assert@num@args\@gobbletwo}{argument count != 2}}
\long\def\ekvp@assert@num@args#1#2%
{%
\expandafter\ekv@ifempty@\expandafter\ekv@ifempty@A#1#2\ekv@ifempty@B
\ekv@ifempty@true\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
}
% \end{macrocode}
% And finally redefine our auxiliary that just does a bit of argument
% reordering for us.
% \begin{macrocode}
\long\def\ekvpAssert@further#1#2#3#4#5{\ekvpAssertTF@{#1}{#4}{#2{#5}}{#3}}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[internal]{\ekvpAssert@@EOP,\ekvpAssert@@EOT,\ekvpAssert@@EOA}
% \begin{macrocode}
\def\ekvpAssert@@EOA#1#2%
{\long\def#1##1##2#2##3{\ekvp@err{##1 at `##3'}\ekv@endalignsafe\ekv@zero}}
\ekvpAssert@@EOA\ekvpAssert@@EOP\ekvpEOP
\ekvpAssert@@EOA\ekvpAssert@@EOT\ekvpEOT
\ekvpAssert@@EOA\ekvpAssert@@EOA\ekvpEOA
% \end{macrocode}
% \end{macro}
%
%^^A=<<
%
% \subsubsection{Markers}^^A>>=
%
% \begin{macro}
% {\ekvpGobbleP,\ekvpGobbleT,\ekvpGobbleA,\ekvpEOP,\ekvpEOT,\ekvpEOA}
% Since after the |\ekvpEOT|, \emph{etc.} markers the full element is placed,
% they are defined as |\@gobble|, and the functions gobbling up to them gobble
% that argument as well.
% \begin{macrocode}
\long\def\ekvpGobbleP#1\ekvpEOP#2{}
\long\def\ekvpGobbleT#1\ekvpEOT#2{}
\long\def\ekvpGobbleA#1\ekvpEOA#2{}
\let\ekvpEOP\@gobble
\let\ekvpEOT\@gobble
\let\ekvpEOA\@gobble
% \end{macrocode}
% \end{macro}
%
%^^A=<<
%
% \subsubsection{Miscellaneous Auxiliaries}^^A>>=
%
% \begin{macro}{\ekvpProtect}
% \begin{macro}[internal]{\ekvpProtect@,\ekvpProtect@@}
% The idea of this macro is pretty simple. In contexts in which a |\protected|
% macro isn't expanded the |\ekvpProtect@@| expansion will just expand to
% itself and in the process protect the argument using |\unexpanded|. If a
% |\protected| macro would be expanded the |\ekvpProtect@| will remove
% |\ekvpProtect@@| and unpack the argument.
% \begin{macrocode}
\def\ekvpProtect{\ekvpProtect@\ekvpProtect@@}
\protected\long\def\ekvpProtect@\ekvpProtect@@#1{#1}
\long\def\ekvpProtect@@#1{\ekv@unexpanded{\ekvpProtect@@{#1}}}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
%^^A=<<
%
% \subsubsection{Error messages}^^A>>=
%
% \begin{macro}[internal]
% {
% \ekvp@err,\ekvp@err@unknownparser,\ekvp@err@noval,\ekvp@err@missingtype,
% \ekvp@errm
% }
% These should be pretty straight forward. We use |\ekverr| to throw
% expandable errors.
% \begin{macrocode}
\protected\long\def\ekvp@errm#1{\errmessage{expkv-pop Error: #1}}
\ekv@exparg{\long\def\ekvp@err#1}{\ekverr{expkv-pop}{#1}}
\def\ekvp@err@unknownparser#1{\ekvp@err{unknown parser `#1'}}
\long\def\ekvp@err@noval#1#2#3{\ekvp@err{missing value for `#3'}}
\long\def\ekvp@err@missingtype#1#2#3#4{\ekvp@err{missing type in `#3'}}
\long\def\ekvp@err@unknownmarker#1{\ekvp@err{unknown marker `#1'}}
% \end{macrocode}
% \end{macro}
%
%^^A=<<
%
% Undefine the no longer needed |\ekvp@prefix| and restore the category code of
% |@|:
% \begin{macrocode}
\let\ekvp@prefix\ekvp@undefined
\catcode`\@=\ekvp@tmp
% \end{macrocode}
%
% \gobbledocstriptag
%</tex>
%^^A=<<
|