-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathcollargs.edtx
2962 lines (2962 loc) · 107 KB
/
collargs.edtx
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
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% \iffalse
% collargs.edtx (this is not a .dtx file; to produce a .dtx, process it with edtx2dtx)
%%
%% This file is a part of CollArgs, a TeX package providing a command which can
%% determine the argument scope of any command whose argument structure
%% conforms to xparse's argument specification, available at
%% https://ctan.org/pkg/advice and https://github.com/sasozivanovic/advice.
%%
%% Copyright (c) 2023- Saso Zivanovic <saso.zivanovic@guest.arnes.si>
%% (Sa\v{s}o \v{Z}ivanovi\'{c})
%%
%% This work may be distributed and/or modified under the conditions of the
%% LaTeX Project Public License, either version 1.3c of this license or (at
%% your option) any later version. The latest version of this license is in
%% https://www.latex-project.org/lppl.txt and version 1.3c or later is part of
%% all distributions of LaTeX version 2008 or later.
%%
%% This work has the LPPL maintenance status `maintained'.
%% The Current Maintainer of this work is Saso Zivanovic.
%%
%% The files belonging to this work and covered by LPPL are listed in
%% (<texmf>/doc/generic/collargs/)FILES.
%
% \fi
%
% \begin{macrocode}
%
% Package CollArgs provides commands |\CollectArguments| and
% |\CollectArgumentsRaw|, which (what a surprise!) collect the arguments
% conforming to the given (slightly extended) |xparse| argument specification.
% The package was developed to help out with automemoization (see
% section~\ref{sec:code:automemoization}). It started out as a few lines of
% code, but had grown once I realized I want automemoization to work for
% verbatim environments as well --- the environment-collecting code is based on
% Bruno Le Floch's package |cprotect| --- and had then grown some more once I
% decided to support the |xparse| argument specification in full detail, and to
% make the verbatim mode flexible enough to deal with a variety of situations.
%
% The implementation of this package does not depend on |xparse|. Perhaps this
% is a mistake, especially as the |xparse| code is now included in the base
% \hologo{LaTeX}, but the idea was to have a light-weight package (not sure
% this is the case anymore, given all the bells and whistles), to have its
% functionality available in plain \hologo{TeX} and \hologo{ConTeXt} as well
% (same as Memoize), and, perhaps most importantly, to have the ability to
% collect the arguments verbatim.
%
%
% \paragraph{Identification}
%<latex>\ProvidesPackage{collargs}[2024/03/15 v1.2.0 Collect arguments of any command]
%<context>%D \module[
%<context>%D file=t-collargs.tex,
%<context>%D version=1.2.0,
%<context>%D title=CollArgs,
%<context>%D subtitle=Collect arguments of any command,
%<context>%D author=Saso Zivanovic,
%<context>%D date=2024-03-15,
%<context>%D copyright=Saso Zivanovic,
%<context>%D license=LPPL,
%<context>%D ]
%<context>\writestatus{loading}{ConTeXt User Module / collargs}
%<context>\unprotect
%<context>\startmodule[collargs]
% \paragraph{Required packages}
%<latex>\RequirePackage{pgfkeys}
%<plain>\input pgfkeys
%<context>\input t-pgfkey
%<latex>\RequirePackage{etoolbox}
%<plain,context>\input etoolbox-generic
%<plain>\edef\resetatcatcode{\catcode`\noexpand\@\the\catcode`\@\relax}
%<plain>\catcode`\@11\relax
%
% \begin{macro}{\toksapp,\gtoksapp,\etoksapp,\xtoksapp}
% Macros for appending to a token register. We don't have to define them in
% \hologo{LuaTeX}, where they exist as primitives. Same as these primitives,
% out macros accept either a register number or a |\toksdef|fed control
% sequence as the (unbraced) |#1|; |#2| is the text to append.
\ifdefined\luatexversion
\else
\def\toksapp{\toks@cs@or@num\@toksapp}
\def\gtoksapp{\toks@cs@or@num\@gtoksapp}
\def\etoksapp{\toks@cs@or@num\@etoksapp}
\def\xtoksapp{\toks@cs@or@num\@xtoksapp}
\def\toks@cs@or@num#1#2#{%
% Test whether |#2| (the original |#1|) is a number or a control sequence.
\ifnum-2>-1#2
% It is a number. |\toks@cs@or@num@num| will gobble |\toks@cs@or@num@cs|
% below.
\expandafter\toks@cs@or@num@num
% The register control sequence in |#2| is skipped over in the false
% branch.
\fi
\toks@cs@or@num@cs{#1}{#2}%
}
% |#1| is one of |\@toksapp| and friends. The second macro prefixes the
% register number by |\toks|.
\def\toks@cs@or@num@cs#1#2{#1{#2}}
\def\toks@cs@or@num@num\toks@cs@or@num@cs#1#2{#1{\toks#2 }}
% Having either |\tokscs| or |\toks<number>| in |#1|, we can finally do the
% real job.
\long\def\@toksapp#1#2{#1\expandafter{\the#1#2}}%
\long\def\@etoksapp#1#2{#1\expandafter{\expanded{\the#1#2}}}%
\long\def\@gtoksapp#1#2{\global#1\expandafter{\the#1#2}}%
\long\def\@xtoksapp#1#2{\global#1\expandafter{\expanded{\the#1#2}}}%
\fi
% \end{macro}
%
% \begin{macro}{\CollectArguments,\CollectArgumentsRaw}
% |\CollectArguments| takes three arguments: the optional |#1| is the option
% list, processed by |pgfkeys| (given the grouping structure, these options
% will apply to all arguments); the mandatory |#2| is the |xparse|-style
% argument specification; the mandatory |#3| is the ``next'' command (or a
% sequence of commands). The argument list is expected to start immediately
% after the final argument; |\CollectArguments| parses it, effectively
% figuring out its extent, and then passes the entire argument list to the
% ``next'' command (as a single argument).
%
% |\CollectArgumentsRaw| differs only in how it takes and processes the
% options. For one, these should be given as a mandatory argument.
% Furthermore, they do not take the form of a keylist, but should deploy the
% ``programmer's interface.'' |#1| should thus be a sequence of invocations
% of the macro counterparts of the keys defined in
% section~\ref{sec:code:collargs:keys}, which can be recognized as starting
% with |\collargs| followed by a capital letter, e.g.\ |\collargsCaller|.
% Note that |\collargsSet| may also be used in |#1|. (The ``optional,''
% i.e.\ bracketed, argument of |\CollectArgumentsRaw| is in fact mandatory.)
\protected\def\CollectArguments{%
\pgf@keys@utilifnextchar[\CollectArguments@i{\CollectArgumentsRaw{}}%]
}
\def\CollectArguments@i[#1]{\CollectArgumentsRaw{\collargsSet{#1}}}
\protected\def\CollectArgumentsRaw#1#2#3{%
% This group will be closed by |\collargs@.| once we grinded through the
% argument specification.
\begingroup
% Initialize category code fixing; see section~\ref{sec:code:collargs:fix}
% for details. We have to do this before applying the settings, so that
% |\collargsFixFromNoVerbatim| et al can take effect.
\global\let\ifcollargs@last@verbatim\ifcollargs@verbatim
\global\let\ifcollargs@last@verbatimbraces\ifcollargs@verbatimbraces
\global\collargs@double@fixfalse
% Apply the settings.
\collargs@verbatim@wrap{#1}%
% Initialize the space-grabber.
\collargs@init@grabspaces
% Remember the code to execute after collection.
\def\collargs@next{#3}%
% Initialize the token register holding the collected arguments.
\ifcollargsClearArgs
\global\collargsArgs{}%
\fi
% Execute the central loop macro, which expects the argument specification
% |#2| to be delimited from the following argument tokens by a dot.
\collargs@#2.%
}
% \end{macro}
%
% \begin{macro}{\collargsSet}
% This macro processes the given keys in the |/collargs| keypath. When it is
% used to process options given by the end user (the optional argument to
% |\CollectArguments|, and the options given within the argument
% specification, using the new modifier |&|), its invocation should be
% wrapped in |\collargs@verbatim@wrap| to correctly deal with the changes of
% the verbatim mode.
\def\collargsSet#1{\pgfqkeys{/collargs}{#1}}
% \end{macro}
%
% \subsubsection{The keys}
% \label{sec:code:collargs:keys}
%
% \begin{macro}{\collargs@cs@cases}
% If the first argument of this auxiliary macro is a single control sequence,
% then the second argument is executed. If the first argument starts with a
% control sequence but this control sequence does not form the entire
% argument, the third argument is executed. Otherwise, the fourth argument
% is executed.
%
% This macro is defined in package CollArgs because we use it in key |caller|
% below, but it is really useful in package Auto, where having it we don't
% have to bother the end-user with a separate keys for commands and
% environments, but automatically detect whether the argument of |auto| and
% (|de|)|activate| is a command or an environment.
\def\collargs@cs@cases#1{\collargs@cs@cases@i#1\collargs@cs@cases@end}
\let\collargs@cs@cases@end\relax
\def\collargs@cs@cases@i{\futurelet\collargs@temp\collargs@cs@cases@ii}
\def\collargs@cs@cases@ii#1#2\collargs@cs@cases@end{%
\ifcat\noexpand\collargs@temp\relax
\ifx\relax#2\relax
\expandafter\expandafter\expandafter\@firstofthree
\else
\expandafter\expandafter\expandafter\@secondofthree
\fi
\else
\expandafter\@thirdofthree
\fi
}
\def\@firstofthree#1#2#3{#1}
\def\@secondofthree#1#2#3{#2}
\def\@thirdofthree#1#2#3{#3}
% \end{macro}
%
% \begin{collargskey}{caller}
% \begin{macro}{\collargsCaller}
% Every macro which grabs a part of the argument list will be accessed
% through the ``caller'' control sequence, so that \hologo{TeX}'s reports
% of any errors in the argument structure can contain a command name
% familiar to the author.\footnote{The idea is borrowed from package
% |environ|, which is in turn based on code from |amsmath|.} For example,
% if the argument list ``originally'' belonged to command |\foo| with
% argument structure |r()|, but no parentheses follow in the input, we want
% \hologo{TeX} to complain that |Use of \foo doesn't match its definition|.
% This can be achieved by setting |caller=\foo|; the default is
% |caller=\CollectArguments|, which is still better than seeing an error
% involving some random internal control sequence. It is also ok to set an
% environment name as the caller, see below.
%
% The key and macro defined below store the caller control sequence into
% |\collargs@caller|, e.g.\ when we say |caller=\foo|, we effectively
% execute |\def\collargs@caller{\foo}|.
\collargsSet{
caller/.code={\collargsCaller{#1}},
}
\def\collargsCaller#1{%
\collargs@cs@cases{#1}{%
\let\collargs@temp\collargs@caller@cs
}{%
\let\collargs@temp\collargs@caller@csandmore
}{%
\let\collargs@temp\collargs@caller@env
}%
\collargs@temp{#1}%
}
\def\collargs@caller@cs#1{%
% If |#1| is a single control sequence, just use that as the caller.
\def\collargs@caller{#1}%
}
\def\collargs@caller@csandmore#1{%
% If |#1| starts with a control sequence, we don't complain, but convert the
% entire |#1| into a control sequence.
\begingroup
\escapechar -1
\expandafter\endgroup
\expandafter\def\expandafter\collargs@caller\expandafter{%
\csname\string#1\endcsname
}%
}
\def\collargs@caller@env#1{%
% If |#1| does not start with a control sequence, we assume that is an
% environment name, so we prepend |start| in \hologo{ConTeXt}, and dress it
% up into |\begin{#1}| in \hologo{LaTeX}.
\expandafter\def\expandafter\collargs@caller\expandafter{%
\csname
%<context>start%
%<latex>begin{%
#1%
%<latex>}%
\endcsname
}%
}
\collargsCaller\CollectArguments
% \end{macro}
% \end{collargskey}
%
% \begin{macro}{\ifcollargs@verbatim,\ifcollargs@verbatimbraces}
% The first of these conditional
% signals that we're collecting the arguments in one of the verbatim
% modes; the second one signals the |verb| mode in particular.
\newif\ifcollargs@verbatim
\newif\ifcollargs@verbatimbraces
% \end{macro}
%
% \begin{collargskey}{verbatim, verb, no verbatim}
% \begin{macro}{\collargs@verbatim@wrap}
% These keys set the verbatim mode macro which will be executed by
% |\collargsSet| after processing all keys.
% The verbatim mode macros |\collargsVerbatim|, |\collargsVerb| and
% |\collargsNoVerbatim| are somewhat complex; we postpone their definition
% until section~\ref{sec:code:collargs:verbatim}. Their main effect is to
% set conditionals |\ifcollargs@verbatim| and |\ifcollargs@verbatimbraces|,
% which are be inspected by the argument type handlers --- and to make
% the requested category code changes, of course.
%
% Here, note that the verbatim-selection code is not executed while the
% keylist is being processed. Rather, the verbatim keys simply set the macro
% which will be executed \emph{after} the keylist is processed, and this is
% why processing of a keylist given by the user must be always wrapped in
% |\collargs@verbatim@wrap|.
\collargsSet{
verbatim/.code={\let\collargs@apply@verbatim\collargsVerbatim},
verb/.code={\let\collargs@apply@verbatim\collargsVerb},
no verbatim/.code={\let\collargs@apply@verbatim\collargsNoVerbatim},
}
\def\collargs@verbatim@wrap#1{%
\let\collargs@apply@verbatim\relax
#1%
\collargs@apply@verbatim
}
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{fix from verbatim, fix from verb, fix from no verbatim}
% \begin{macro}{\collargsFixFromVerbatim,\collargsFixFromVerb,\collargsFixFromNoVerbatim}
% These keys and macros should be used to request a category code fix, when
% the offending tokenization took place prior to invoking
% |\CollectArguments|; see section~\ref{sec:code:collargs:fix} for details.
% While I assume that only |\collargsFixFromNoVerbatim| will ever be used
% (and it is used by |\mmz|), we provide macros for all three transitions,
% for completeness.\indentmacrocode
\collargsSet{
fix from verbatim/.code={\collargsFixFromVerbatim},
fix from verb/.code={\collargsFixFromVerb},
fix from no verbatim/.code={\collargsFixFromNoVerbatim},
}
% \noindentmacrocode
\def\collargsFixFromNoVerbatim{%
\global\collargs@fix@requestedtrue
\global\let\ifcollargs@last@verbatim\iffalse
}
\def\collargsFixFromVerbatim{%
\global\collargs@fix@requestedtrue
\global\let\ifcollargs@last@verbatim\iftrue
\global\let\ifcollargs@last@verbatimbraces\iftrue
}
\def\collargsFixFromVerb{%
\global\collargs@fix@requestedtrue
\global\let\ifcollargs@last@verbatim\iftrue
\global\let\ifcollargs@last@verbatimbraces\iffalse
}
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{braces}
% Set the characters which are used as the grouping characters in the full
% verbatim mode. The user is only required to do this when multiple
% character pairs serve as the grouping characters. The underlying macro,
% |\collargsBraces|, will be defined in
% section~\ref{sec:code:collargs:verbatim}.
\collargsSet{
braces/.code={\collargsBraces{#1}}%
}
% \end{collargskey}
%
% \begin{collargskey}{environment}
% \begin{macro}{\collargsEnvironment}
% Set the environment name.
% \indentmacrocode
\collargsSet{
environment/.estore in=\collargs@b@envname
}
\def\collargsEnvironment#1{\edef\collargs@b@envname{#1}}
\collargsEnvironment{}
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{begin tag, end tag, tags}
% \begin{macro}{\ifcollargsBeginTag,\ifcollargsEndTag,\ifcollargsAddTags}
% When |begin tag|\slash|end tag| is in effect, the begin\slash end-tag will
% be will be prepended/appended to the environment body. |tags| is a
% shortcut for setting |begin tag| and |end tag| simultaneously.
% \indentmacrocode
\collargsSet{
begin tag/.is if=collargsBeginTag,
end tag/.is if=collargsEndTag,
tags/.style={begin tag=#1, end tag=#1},
tags/.default=true,
}
\newif\ifcollargsBeginTag
\newif\ifcollargsEndTag
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{ignore nesting}
% \begin{macro}{\ifcollargsIgnoreNesting}
% When this key is in effect, we will
% ignore any \cs{begin}\marg{name}s and simply grab everything up to
% the first \cs{end}\marg{name} (again, the markers are automatically
% adapted to the format).
\collargsSet{
ignore nesting/.is if=collargsIgnoreNesting,
}
\newif\ifcollargsIgnoreNesting
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{ignore other tags}
% \begin{macro}{\ifcollargsIgnoreOtherTags}
% This key is only relevant in the
% non-verbatim and partial verbatim modes in \hologo{LaTeX}. When it
% is in effect, CollArgs checks the environment name following each
% |\begin| and |\end|, ignoring the tags with an environment name other
% than |\collargs@b@envname|.
\collargsSet{
ignore other tags/.is if=collargsIgnoreOtherTags,
}
\newif\ifcollargsIgnoreOtherTags
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}[noindex]{(append/prepend) (pre/post)processor}
% \begin{macro}[noindex]{\collargs(Append/Prepend)(Pre/Post)processor}
% \begin{collargskey}[noprint]{append preprocessor, prepend preprocessor,
% append postprocessor, prepend postprocessor}
% \begin{macro}[noprint]{\collargsAppendPreprocessor,\collargsPrependPreprocessor,\collargsAppendPostprocessor,\collargsPrependPostprocessor}
% These keys and
% macros populate the list of preprocessors,
% |\collargs@preprocess@arg|, and the list of post-processors,
% |\collargs@postprocess@arg|, executed in |\collargs@appendarg|.
\collargsSet{
append preprocessor/.code={\collargsAppendPreprocessor{#1}},
prepend preprocessor/.code={\collargsPrependPreprocessor{#1}},
append postprocessor/.code={\collargsAppendPostprocessor{#1}},
prepend postprocessor/.code={\collargsPrependPostprocessor{#1}},
}
\def\collargsAppendPreprocessor#1{\appto\collargs@preprocess@arg{#1}}
\def\collargsPrependPreprocessor#1{\preto\collargs@preprocess@arg{#1}}
\def\collargsAppendPostprocessor#1{\appto\collargs@postprocess@arg{#1}}
\def\collargsPrependPostprocessor#1{\preto\collargs@postprocess@arg{#1}}
% \end{macro}
% \end{collargskey}
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}[noindex]{clear (pre/post)processors}
% \begin{macro}[noindex]{\collargsClear(Pre/Post)processors}
% \begin{collargskey}[noprint]{clear preprocessors, clear postprocessors}
% \begin{macro}[noprint]{\collargsClearPreprocessors,\collargsClearPostprocessors}
% These keys and macros
% clear the pre- and post-processor lists, which are initially empty as
% well.
\def\collargs@preprocess@arg{}
\def\collargs@postprocess@arg{}
\collargsSet{
clear preprocessors/.code={\collargsClearPreprocessors},
clear postprocessors/.code={\collargsClearPostprocessors},
}
\def\collargsClearPreprocessors{\def\collargs@preprocess@arg{}}%
\def\collargsClearPostprocessors{\def\collargs@postprocess@arg{}}%
% \end{macro}
% \end{collargskey}
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}[noindex]{(append/prepend) expandable (pre/post)processor}
% \begin{macro}[noindex]{\collargs(Append/Prepend)Expandable(Pre/Post)processor}
% \begin{collargskey}[noprint]{append expandable preprocessor, prepend expandable preprocessor, append expandable postprocessor, prepend expandable postprocessor}
% \begin{macro}[noprint]{\collargsAppendExpandablePreprocessor,\collargsPrependExpandablePreprocessor,\collargsAppendExpandablePostprocessor,\collargsPrependExpandablePostprocessor}
% These keys and macros simplify the definition of expandable
% processors. Note that expandable processors are added to the same
% list as non-expandable processors.
\collargsSet{
append expandable preprocessor/.code={\collargsAppendExpandablePreprocessor{#1}},
prepend expandable preprocessor/.code={\collargsPrependExpandablePreprocessor{#1}},
append expandable postprocessor/.code={\collargsAppendExpandablePostprocessor{#1}},
prepend expandable postprocessor/.code={\collargsPrependExpandablePostprocessor{#1}},
}
\def\collargsAppendExpandablePreprocessor#1{%
\appto\collargs@preprocess@arg{%
\collargsArg\expandafter{\expanded{#1}}%
}%
}
\def\collargsPrependExpandablePreprocessor#1{%
\preto\collargs@preprocess@arg{%
\collargsArg\expandafter{\expanded{#1}}%
}%
}
\def\collargsAppendExpandablePostprocessor#1{%
\appto\collargs@postprocess@arg{%
\collargsArg\expandafter{\expanded{#1}}%
}%
}
\def\collargsPrependExpandablePostprocessor#1{%
\preto\collargs@postprocess@arg{%
\collargsArg\expandafter{\expanded{#1}}%
}%
}
% \end{macro}
% \end{collargskey}
% \end{macro}
% \end{collargskey}
%
%
% \begin{collargskey}{no delimiters}
% \begin{macro}{\ifcollargsNoDelimiters}
% When this conditional is in effect, the
% delimiter wrappers set by |\collargs@wrap| are ignored by
% |\collargs@appendarg|.
\collargsSet{%
no delimiters/.is if=collargsNoDelimiters,
}
\newif\ifcollargsNoDelimiters
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{clear args}
% \begin{macro}{\ifcollargsClearArgs}
% When this conditional is set to |false|, the global token register
% |\collargsArgs| receiving the collected arguments is not cleared prior
% to argument collection.
\collargsSet{%
clear args/.is if=collargsClearArgs,
}
\newif\ifcollargsClearArgs
\collargsClearArgstrue
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{return}
% \begin{macro}{\collargsReturn}
% Exiting |\CollectArguments|, should the next-command be followed by the
% braced collected arguments, collected arguments as they are, or nothing?
\collargsSet{%
return/.is choice,
return/braced/.code=\collargsReturnBraced,
return/plain/.code=\collargsReturnPlain,
return/no/.code=\collargsReturnNo,
}
\def\collargsReturnBraced{\def\collargsReturn{0}}
\def\collargsReturnPlain{\def\collargsReturn{1}}
\def\collargsReturnNo{\def\collargsReturn{2}}
\collargsReturnBraced
% \end{macro}
% \end{collargskey}
%
% \begin{collargskey}{alias}
% \begin{macro}{\collargsAlias}
%
\collargsSet{%
alias/.code 2 args=\collargsAlias{#1}{#2}%
}
\def\collargsAlias#1#2{%
\csdef{collargs@#1}{\collargs@@@#2}%
}
% \end{macro}
% \end{collargskey}
%
%
%
% \subsubsection{The central loop}
% \label{sec:code:collargs:central-loop}
%
% The central loop is where we grab the next \meta{token} from the argument
% specification and execute the corresponding argument type or modifier
% handler, |\collargs@|\meta{token}. The central loop consumes the argument
% type \meta{token}; the handler will see the remainder of the argument
% specification (which starts with the arguments to the argument type, if any,
% e.g.\ by |()| of |d()|), followed by a dot, and then the tokens list from
% which the arguments are to be collected. It is the responsibility of handler
% to preserve the rest of the argument specification and reexecute the central
% loop once it is finished.
%
% \begin{macro}{\collargs@}
% Each argument is processed in a group to allow for local settings. This
% group is closed by |\collargs@appendarg|.
\def\collargs@{%
\begingroup
\collargs@@@
}
% \end{macro}
%
% \begin{macro}{\collargs@@@}
% This macro is where modifier handlers reenter the central loop --- we don't
% want modifiers to open a group, because their settings should remain in
% effect until the next argument. Furthermore, modifiers do not trigger
% category code fixes.
\def\collargs@@@#1{%
\collargs@in@{#1}{&+!>.}%
\ifcollargs@in@
\expandafter\collargs@@@iii
\else
\expandafter\collargs@@@i
\fi
#1%
}
\def\collargs@@@i#1.{%
% Fix the category code of the next argument token, if necessary, and then
% proceed with the main loop.
\collargs@fix{\collargs@@@ii#1.}%
}
% Reset the fix request and set the last verbatim conditionals to the current
% state.
\def\collargs@@@ii{%
\global\collargs@fix@requestedfalse
\global\let\ifcollargs@last@verbatim\ifcollargs@verbatim
\global\let\ifcollargs@last@verbatimbraces\ifcollargs@verbatimbraces
\collargs@@@iii
}
% Call the modifier or argument type handler denoted by the first token of the
% remainder of the argument specification.
\def\collargs@@@iii#1{%
\ifcsname collargs@#1\endcsname
\csname collargs@#1\expandafter\endcsname
\else
% We throw an error if the token refers to no argument type or modifier.
\collargs@error@badtype{#1}%
\fi
}
% Throwing an error stops the processing of the argument specification, and
% closes the group opened in |\collargs@i|.
\def\collargs@error@badtype#1#2.{%
\PackageError{collargs}{Unknown xparse argument type or modifier "#1"
for "\expandafter\string\collargs@caller\space"}{}%
\endgroup
}
% \end{macro}
%
% {\catcode`\&=11
% \begin{macro}{\collargs@&}
% We extend the |xparse| syntax with modifier |&|, which applies the given
% options to the following (and only the following) argument. If |&| is
% followed by another |&|, the options are expected to occur in the raw
% format, like the options given to |\CollectArgumentsRaw|. Otherwise, the
% options should take the form of a keylist, which will be processed by
% |\collargsSet|. In any case, the options should be given within the
% argument specification, immediately following the (single or double) |&|.
\csdef{collargs@&}{%
\futurelet\collargs@temp\collargs@amp@i
}
\def\collargs@amp@i{%
% In \hologo{ConTeXt}, |&| has character code ``other'' in the text.
%<!context>\ifx\collargs@temp&%
%<context>\expandafter\ifx\detokenize{&}\collargs@temp
\expandafter\collargs@amp@raw
\else
\expandafter\collargs@amp@set
\fi
}
\def\collargs@amp@raw#1#2{%
\collargs@verbatim@wrap{#2}%
\collargs@@@
}
\def\collargs@amp@set#1{%
\collargs@verbatim@wrap{\collargsSet{#1}}%
\collargs@@@
}
% \end{macro}}
%
% \begin{macro}[noindex]{\collargs@+}
% \MyIndex{collargs@+}{\texttt{\textbackslash collargs@+}}{main}
% This modifier makes the next argument long, i.e.\ accept paragraph tokens.
\csdef{collargs@+}{%
\collargs@longtrue
\collargs@@@
}
\newif\ifcollargs@long
% \end{macro}
%
% \begin{macro}{\collargs@>}
% We can simply ignore the processor modifier. (This, |xparse|'s processor,
% should not be confused with CollArgs's processors, which are set using
% keys |append preprocessor| etc.)
\csdef{collargs@>}#1{\collargs@@@}
% \end{macro}
%
% \begin{macro}[noindex]{\collargs@!}
% \MyIndex{collargs@!}{\texttt{\textbackslash collargs@\exclamationmark}}{main}
% Should we accept spaces before an optional argument following a mandatory
% argument (\pkg{xparse} manual, \S1.1)? By default, yes. This modifier is
% only applicable to types |d| and |t|, and derived types, but, unlike
% |xparse|, we don't bother to enforce this; when used with other types, |!|
% simply has no effect.
\csdef{collargs@!}{%
\collargs@grabspacesfalse
\collargs@@@
}
% \end{macro}
%
% \begin{macro}{\collargsArgs}
% This token register is where we store the collected argument tokens. All
% assignments to this register are global, because it needs to survive the
% groups opened for individual arguments.
\newtoks\collargsArgs
% \end{macro}
%
% \begin{macro}{\collargsArg}
% An auxiliary, but publicly available token register, used for processing
% the argument, and by some argument type handlers.
\newtoks\collargsArg
% \end{macro}
%
% \begin{macro}{\collargs@.}
% This fake argument type is used to signal the end of the argument list.
% Note that this really counts as an extension of the |xparse| argument
% specification.
\csdef{collargs@.}{%
% Close the group opened in |\collargs@|.
\endgroup
% Close the main |\CollectArguments| group, fix the category code of the next
% token if necessary, and execute the next-code, followed by the collected
% arguments in braces. Any over-grabbed spaces are reinserted into the input
% stream, non-verbatim.
\expanded{%
\endgroup
\noexpand\collargs@fix{%
\expandonce\collargs@next
\ifcase\collargsReturn\space
{\the\collargsArgs}%
\or
\the\collargsArgs
\fi
\collargs@spaces
}%
}%
}
% \end{macro}
%
% \subsubsection{Auxiliary macros}
% \label{sec:code:collargs:aux}
%
% \begin{macro}{\collargs@appendarg}
% This macro is used by the argument type
% handlers to append the collected argument to the storage
% (|\collargsArgs|).
\long\def\collargs@appendarg#1{%
% Temporarily store the collected argument into a token register. The
% processors will manipulate the contents of this register.
\collargsArg={#1}%
% This will clear the double-fix conditional, and potentially request a
% normal, single fix. We can do this here because this macro is only called
% when something is actually collected. For details, see
% section~\ref{sec:code:collargs:fix}.
\ifcollargs@double@fix
\collargs@cancel@double@fix
\fi
% Process the argument with user-definable preprocessors, the wrapper defined
% by the argument type, and user-definable postprocessors.
\collargs@preprocess@arg
\ifcollargsNoDelimiters
\else
\collargs@process@arg
\fi
\collargs@postprocess@arg
% Append the processed argument, preceded by any grabbed spaces (in the
% correct mode), to the storage.
\xtoksapp\collargsArgs{\collargs@grabbed@spaces\the\collargsArg}%
% Initialize the space-grabber.
\collargs@init@grabspaces
% Once the argument was appended to the list, we can close its group, opened
% by |\collargs@|.
\endgroup
}
% \end{macro}
%
% \begin{macro}{\collargs@wrap}
% This macro is used by argument type handlers to declare their delimiter
% wrap, like square brackets around the optional argument of type |o|. It
% uses |\collargs@addwrap|, defined in section~\ref{sec:code:collargs:keys},
% but adds to |\collargs@process@arg|, which holds the delimiter wrapper
% defined by the argument type handler. Note that this macro \emph{appends}
% a wrapper, so multiple wrappers are allowed --- this is used by type |e|
% handler.
\def\collargs@wrap#1{%
\appto\collargs@process@arg{%
\long\def\collargs@temp##1{#1}%
\expandafter\expandafter\expandafter\collargsArg
\expandafter\expandafter\expandafter{%
\expandafter\collargs@temp\expandafter{\the\collargsArg}%
}%
}%
}
\def\collargs@process@arg{}
% \end{macro}
%
% \begin{macro}{\collargs@defcollector,\collargs@defusecollector,\collargs@letusecollector}
% These macros streamline the usage of
% the ``caller'' control sequence. They are like a |\def|, but should not
% be given the control sequence to define, as they will automatically
% define the control sequence residing in |\collargs@caller|; the usage is
% thus |\collargs@defcollector<parameters>{<definition>}|. For example, if
% |\collargs@caller| holds |\foo|, |\collargs@defcollector#1{(#1)}| is
% equivalent to |\def\foo#1{(#1)}|. Macro |\collargs@defcollector| will
% only define the caller control sequence to be the collector, while
% |\collargs@defusecollector| will also immediately execute it.
\def\collargs@defcollector#1#{%
\ifcollargs@long\long\fi
\expandafter\def\collargs@caller#1%
}
\def\collargs@defusecollector#1#{%
\afterassignment\collargs@caller
\ifcollargs@long\long\fi
\expandafter\def\collargs@caller#1%
}
\def\collargs@letusecollector#1{%
\expandafter\let\collargs@caller#1%
\collargs@caller
}
\newif\ifcollargs@grabspaces
\collargs@grabspacestrue
% \end{macro}
%
% \begin{macro}{\collargs@init@grabspaces}
% The space-grabber macro
% |\collargs@grabspaces| should be initialized by executing this macro. If
% |\collargs@grabspaces| is called twice without an intermediate
% initialization, it will assume it is in the same position in the input
% stream and simply bail out.
\def\collargs@init@grabspaces{%
\gdef\collargs@gs@state{0}%
\gdef\collargs@spaces{}%
\gdef\collargs@otherspaces{}%
}
% \end{macro}
%
% \begin{macro}{\collargs@grabspaces}
% This auxiliary macro grabs any following
% spaces, and then executes the next-code given as the sole argument. The
% spaces will be stored into two macros, |\collargs@spaces| and
% |\collargs@otherspaces|, which store the spaces in the non-verbatim and the
% verbatim form. With the double storage, we can grab the spaces in the
% verbatim mode and use them non-verbatim, or vice versa. The macro takes a
% single argument, the code to execute after maybe grabbing the spaces.
%
\def\collargs@grabspaces#1{%
\edef\collargs@gs@next{\unexpanded{#1}}%
\ifnum\collargs@gs@state=0
\gdef\collargs@gs@state{1}%
\expandafter\collargs@gs@i
\else
\expandafter\collargs@gs@next
\fi
}
\def\collargs@gs@i{%
\futurelet\collargs@temp\collargs@gs@g
}
% We check for grouping characters even in the verbatim mode, because we might
% be in the partial verbatim.
\def\collargs@gs@g{%
\ifcat\noexpand\collargs@temp\bgroup
\expandafter\collargs@gs@next
\else
\ifcat\noexpand\collargs@temp\egroup
\expandafter\expandafter\expandafter\collargs@gs@next
\else
\expandafter\expandafter\expandafter\collargs@gs@ii
\fi
\fi
}
\def\collargs@gs@ii{%
\ifcollargs@verbatim
\expandafter\collargs@gos@iii
\else
\expandafter\collargs@gs@iii
\fi
}
% This works because the character code of a space token is always 32.
\def\collargs@gs@iii{%
\expandafter\ifx\space\collargs@temp
\expandafter\collargs@gs@iv
\else
\expandafter\collargs@gs@next
\fi
}
\expandafter\def\expandafter\collargs@gs@iv\space{%
\gappto\collargs@spaces{ }%
\xappto\collargs@otherspaces{\collargs@otherspace}%
\collargs@gs@i
}
% We need the space of category 12 above.
\begingroup\catcode`\ =12\relax\gdef\collargs@otherspace{ }\endgroup
\def\collargs@gos@iii#1{%
% Macro |\collargs@cc| recalls the ``outside'' category code of character
% |#1|; see section~\ref{sec:code:collargs:verbatim}.
\ifnum\collargs@cc{#1}=10
% We have a space.
\expandafter\collargs@gos@iv
\else
\ifnum\collargs@cc{#1}=5
% We have a newline.
\expandafter\expandafter\expandafter\collargs@gos@v
\else
\expandafter\expandafter\expandafter\collargs@gs@next
\fi
\fi
#1%
}
\def\collargs@gos@iv#1{%
\gappto\collargs@otherspaces{#1}%
% No matter how many verbatim spaces we collect, they equal a single
% non-verbatim space.
\gdef\collargs@spaces{ }%
\collargs@gs@i
}
\def\collargs@gos@v{%
% Only add the first newline.
\ifnum\collargs@gs@state=2
\expandafter\collargs@gs@next
\else
\expandafter\collargs@gs@vi
\fi
}
\def\collargs@gs@vi#1{%
\gdef\collargs@gs@state{2}%
\gappto\collargs@otherspaces{#1}%
\gdef\collargs@spaces{ }%
\collargs@gs@i
}
% \end{macro}
%
% \begin{macro}{\collargs@maybegrabspaces}
% This macro grabs any following spaces, but it will do so only when
% conditional |\ifcollargs@grabspaces|, which can be \emph{un}set by modifier
% |!|, is in effect. The macro is used by handlers for types |d| and |t|.
\def\collargs@maybegrabspaces{%
\ifcollargs@grabspaces
\expandafter\collargs@grabspaces
\else
\expandafter\@firstofone
\fi
}
% \end{macro}
%
% \begin{macro}{\collargs@grabbed@spaces}
% This macro expands to either the verbatim
% or the non-verbatim variant of the grabbed spaces, depending on the
% verbatim mode in effect at the time of expansion.
\def\collargs@grabbed@spaces{%
\ifcollargs@verbatim
\collargs@otherspaces
\else
\collargs@spaces
\fi
}
% \end{macro}
%
% \begin{macro}{\collargs@reinsert@spaces}
% Inserts the grabbed spaces back into the
% input stream, but with the category code appropriate for the verbatim mode
% then in effect. After the insertion, the space-grabber is initialized and
% the given next-code is executed in front of the inserted spaces.
\def\collargs@reinsert@spaces#1{%
\expanded{%
\unexpanded{%
\collargs@init@grabspaces
#1%
}%
\collargs@grabbed@spaces
}%
}
% \end{macro}
%
% \begin{macro}{\collargs@ifnextcat}
% An adaptation of |\pgf@keys@utilifnextchar|
% which checks whether the \emph{category} code of the next non-space
% character matches the category code of |#1|.
\long\def\collargs@ifnextcat#1#2#3{%
\let\pgf@keys@utilreserved@d=#1%
\def\pgf@keys@utilreserved@a{#2}%
\def\pgf@keys@utilreserved@b{#3}%
\futurelet\pgf@keys@utillet@token\collargs@ifncat}
\def\collargs@ifncat{%
\ifx\pgf@keys@utillet@token\pgf@keys@utilsptoken
\let\pgf@keys@utilreserved@c\collargsxifnch
\else
\ifcat\noexpand\pgf@keys@utillet@token\pgf@keys@utilreserved@d
\let\pgf@keys@utilreserved@c\pgf@keys@utilreserved@a
\else
\let\pgf@keys@utilreserved@c\pgf@keys@utilreserved@b
\fi
\fi
\pgf@keys@utilreserved@c}
{%
\def\:{\collargs@xifncat}
\expandafter\gdef\: {\futurelet\pgf@keys@utillet@token\collargs@ifncat}
}
% \end{macro}
%
% \begin{macro}{\collargs@forrange}
% This macro executes macro |\collargs@do| for every integer from |#1| and
% |#2|, both inclusive. |\collargs@do| should take a single parameter, the
% current number.
\def\collargs@forrange#1#2{%
\expanded{%
\noexpand\collargs@forrange@i{\number#1}{\number#2}%
}%
}
\def\collargs@forrange@i#1#2{%
\ifnum#1>#2 %
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\collargs@do{#1}%
\expandafter\collargs@forrange@i\expandafter{\number\numexpr#1+1\relax}{#2}%
}%
}
% \end{macro}
%
% \begin{macro}{\collargs@forranges}
% This macro executes macro |\collargs@do| for every integer falling into the
% ranges specified in |#1|. The ranges should be given as a comma-separated
% list of |from-to| items, e.g.\ |1-5,10-11|.
\def\collargs@forranges{\forcsvlist\collarg@forrange@i}
\def\collarg@forrange@i#1{\collarg@forrange@ii#1-}
\def\collarg@forrange@ii#1-#2-{\collargs@forrange{#1}{#2}}
% \end{macro}
%
% \begin{macro}{\collargs@percentchar}
% This macro holds the percent character of category 12.
\begingroup
\catcode`\%=12
\gdef\collargs@percentchar{%}
\endgroup