-
-
Notifications
You must be signed in to change notification settings - Fork 249
/
ltcmd.dtx
4815 lines (4814 loc) · 168 KB
/
ltcmd.dtx
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 meta-comment
%
% Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle
% Copyright (C) 2004-2008 Frank Mittelbach, The LaTeX3 Project
% Copyright (C) 2009-2022
% The LaTeX Project and any individual authors listed elsewhere
% in this file.
%
% This file is part of the LaTeX base system.
% -------------------------------------------
%
% It 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 file has the LPPL maintenance status "maintained".
%
% The list of all files belonging to the LaTeX base distribution is
% given in the file `manifest.txt'. See also `legal.txt' for additional
% information.
%
% The list of derived (unpacked) files belonging to the distribution
% and covered by LPPL is defined by the unpacking scripts (with
% extension .ins) which are part of the distribution.
%
% \fi
%
% \iffalse
%
%%% From File: ltcmd.dtx
%
% \begin{macrocode}
\def\ltcmdversion{v1.0l}
\def\ltcmddate{2022-03-18}
% \end{macrocode}
%
%<*driver>
% \fi
\ProvidesFile{ltcmd.dtx}
[\ltcmddate\space \ltcmdversion\space
LaTeX Kernel (Document commands)]
% \iffalse
\documentclass{l3doc}
\GetFileInfo{ltcmd.dtx}
\title{\filename}
\date{\filedate}
\author{Frank Mittelbach, Chris Rowley, David Carlisle, \LaTeX{} Project Team}
\begin{document}
\maketitle
\DocInput{ltcmd.dtx}
\end{document}
%</driver>
% \fi
%
% \section{Creating document commands}
%
% \changes{v1.0a}{2020/11/20}{Initial version derived from \texttt{xparse.dtx}}
%
% Document commands should be created using the tools provided by this module:
% \cs{NewDocumentCommand}, etc.\@, in almost all cases. This allows clean
% separation of document-level syntax from code-level interfaces. Users have
% a need to create new document commands, and as such a significant amount of
% documentation for \pkg{ltcmd} is provided as part of \texttt{usrguide3}. Here,
% additional material aimed at programmers is provided
%
% \MaybeStop{}
%
% \begin{macrocode}
%<@@=cmd>
% \end{macrocode}
%
% \begin{macrocode}
%<*2ekernel>
\message{document commands,}
%</2ekernel>
% \end{macrocode}
%
% \changes{v1.0b}{2021/03/18}{Use \cs{NewModuleRelease}.}
% \changes{v1.0e}{2021/05/24}{Use \cs{msg_...} instead of \cs{__kernel_msg...}}
%
%
% \pkg{ltcmd} code contains an |^^@| character, which usually has
% catcode~15, so \cs{IncludeInRelease} will break when this code is
% being skipped, so we'll save the catcode of |^^@| to restore later:
% \begin{macrocode}
%<*2ekernel|latexrelease>
%<latexrelease>\edef\@latexrelease@catcode@null{\the\catcode`\^^@ }
%<latexrelease>\catcode`\^^@=12
\ExplSyntaxOn
%<latexrelease>\NewModuleRelease{2020/10/01}{ltcmd}
%<latexrelease> {Document~command~parser}%
% \end{macrocode}
%
% \subsection{Variables and constants}
%
% \begin{variable}{\l_@@_arg_spec_tl}
% Holds the argument specification after normalization of shorthands.
% \begin{macrocode}
\tl_new:N \l_@@_arg_spec_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_args_tl}
% Token list variable for grabbed arguments.
% \begin{macrocode}
\tl_new:N \l_@@_args_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_args_i_tl, \l_@@_args_ii_tl}
% Hold the modified arguments when dealing with default values or
% processors.
% \begin{macrocode}
\tl_new:N \l_@@_args_i_tl
\tl_new:N \l_@@_args_ii_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_current_arg_int}
% The number of the current argument being set up: this is used to
% make sure there are at most 9 arguments, then for creating the
% expandable auxiliary functions and knowing how many arguments the
% code function should take.
% \begin{macrocode}
\int_new:N \l_@@_current_arg_int
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_defaults_bool, \l_@@_defaults_tl}
% The boolean indicates whether there are any argument with default
% value other than |-NoValue-|; the token list holds the code to
% determine these default values in terms of other arguments.
% \begin{macrocode}
\bool_new:N \l_@@_defaults_bool
\tl_new:N \l_@@_defaults_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_environment_bool}
% Generating environments uses the same mechanism as generating functions.
% However, full processing of arguments is always needed for environments,
% and so the function-generating code needs to know this.
% This variable is also used at run time to give correct error messages.
% \begin{macrocode}
\bool_new:N \l_@@_environment_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_environment_str}
% Name of the environment, used at definition time and at run time.
% \begin{macrocode}
\str_new:N \l_@@_environment_str
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_expandable_bool}
% Used to indicate if an expandable command is begin generated, as this
% affects both the acceptable argument types and how they are implemented.
% \begin{macrocode}
\bool_new:N \l_@@_expandable_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_expandable_aux_name_tl}
% Used to create pretty-printing names for the auxiliaries: although the
% immediate definition does not vary, the full expansion does and so it
% does not count as a constant.
% \begin{macrocode}
\tl_new:N \l_@@_expandable_aux_name_tl
\tl_set:Nn \l_@@_expandable_aux_name_tl
{
\l_@@_function_tl \c_space_tl
( arg~ \int_use:N \l_@@_current_arg_int )
}
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_grabber_int}
% Used (in exceptional cases) to get unique names for grabbers used by
% expandable commands.
% \begin{macrocode}
\int_new:N \g_@@_grabber_int
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_fn_tl}
% For passing the pre-formed name of the auxiliary to be used as the
% parsing function.
% \begin{macrocode}
\tl_new:N \l_@@_fn_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_fn_code_tl}
% For passing the pre-formed name of the auxiliary that contains the
% actual code.
% \begin{macrocode}
\tl_new:N \l_@@_fn_code_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_function_tl}
% Holds the control sequence name of the function currently being
% defined: used to avoid passing this as an argument and to avoid repeated
% use of \cs{cs_to_str:N}.
% \begin{macrocode}
\tl_new:N \l_@@_function_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_grab_expandably_bool}
% When defining a non-expandable command, indicates whether the
% arguments can all safely be grabbed by expandable grabbers. This is
% to support abuses of \pkg{xparse} that use protected functions
% inside csname constructions.
% \begin{macrocode}
\bool_new:N \l_@@_grab_expandably_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_obey_spaces_bool}
% For trailing optionals.
% \begin{macrocode}
\bool_new:N \l_@@_obey_spaces_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_last_delimiters_tl}
% Holds the delimiters (first tokens) of all optional arguments since
% the previous mandatory argument, to warn about cases where it would
% be impossible to omit optional arguments completely because the
% following mandatory argument has the same delimiter as one of the
% optional arguments.
% \begin{macrocode}
\tl_new:N \l_@@_last_delimiters_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_long_bool}
% Used to indicate that an argument is long, on a per-argument basis.
% \begin{macrocode}
\bool_new:N \l_@@_long_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_m_args_int}
% The number of \texttt{m} arguments: if this is the same as the total
% number of arguments, then a short-cut can be taken in the creation of
% the grabber code.
% \begin{macrocode}
\int_new:N \l_@@_m_args_int
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_prefixed_bool}
% When preparing the signature of non-expandable commands, indicates
% that the current argument is affected by a processor or by |+|
% (namely is long).
% \begin{macrocode}
\bool_new:N \l_@@_prefixed_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_process_all_tl, \l_@@_process_one_tl, \l_@@_process_some_bool}
% When preparing the signature, the processors that will be applied to
% a given argument are collected in \cs{l_@@_process_one_tl}, while
% \cs{l_@@_process_all_tl} contains processors for all arguments. The
% boolean indicates whether there are any processors (to bypass the
% whole endeavour otherwise).
% \begin{macrocode}
\tl_new:N \l_@@_process_all_tl
\tl_new:N \l_@@_process_one_tl
\bool_new:N \l_@@_process_some_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_saved_args_tl}
% Stores \cs{l_@@_args_tl} to deal with space-trimming of
% \texttt{b}-type arguments.
% \begin{macrocode}
\tl_new:N \l_@@_saved_args_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_signature_tl}
% Used when constructing the signature (code for argument grabbing) to
% hold what will become the implementation of the main function.
% When arguments are grabbed (at point of use of the command/environment),
% it also stores the code for grabbing the remaining arguments.
% \begin{macrocode}
\tl_new:N \l_@@_signature_tl
% \end{macrocode}
% \end{variable}
%
% \begin{variable}
% {\l_@@_some_obey_spaces_bool, \l_@@_some_long_bool, \l_@@_some_short_bool}
% These flags are set while normalizing the argument specification.
% The \texttt{obey_spaces} one is used to detect when |!| is used on
% an argument that is not a trailing optional argument.
% The other two are used to check whether all short arguments appear
% before long arguments: this is needed to grab arguments expandably.
% As soon as the first long argument is seen (other than
% \texttt{t}-type, whose long status is ignored) the
% \texttt{some_long} flag is set. The \texttt{some_short} flag is
% used for expandable commands, to know whether to define a short
% auxiliary too.
% \begin{macrocode}
\bool_new:N \l_@@_some_obey_spaces_bool
\bool_new:N \l_@@_some_long_bool
\bool_new:N \l_@@_some_short_bool
% \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_tmp_prop, \l_@@_tmpa_tl, \l_@@_tmpb_tl}
% \begin{macro}{\@@_tmp:w}
% Scratch space.
% \begin{macrocode}
\prop_new:N \l_@@_tmp_prop
\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\cs_new_eq:NN \@@_tmp:w ?
% \end{macrocode}
% \end{macro}
% \end{variable}
%
% With \pkg{xparse}, information about commands being (re)defined was
% switched off by default, unless the |log-declarations| package
% option was used, so here we'll switch that off as well.
% \begin{macrocode}
\msg_redirect_module:nnn { cmd } { info } { none }
% \end{macrocode}
%
% Also add \pkg{cmd} to the \pkg{LaTeX} messages.
% \begin{macrocode}
\prop_gput:Nnn \g_msg_module_type_prop { cmd } { LaTeX }
% \end{macrocode}
%
% \subsection{Declaring commands and environments}
%
% \begin{macro}{\@@_declare_cmd:Nnn, \@@_declare_expandable_cmd:Nnn}
% \begin{macro}{\@@_declare_cmd_aux:Nnn}
% \begin{macro}{\@@_declare_cmd_internal:Nnnn}
% The main functions for creating commands set the appropriate flag then
% use the same internal code to do the definition.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_cmd:Nnn
{
\bool_set_false:N \l_@@_expandable_bool
\@@_declare_cmd_aux:Nnn
}
\cs_new_protected:Npn \@@_declare_expandable_cmd:Nnn
{
\bool_set_true:N \l_@@_expandable_bool
\@@_declare_cmd_aux:Nnn
}
% \end{macrocode}
% The first stage is to log information, both for the user in the log and
% for programmatic use in a property list of all declared commands.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_cmd_aux:Nnn #1#2#3
{
\cs_if_exist:NTF #1
{
\msg_info:nnxx { cmd } { redefine }
{ \token_to_str:N #1 } { \tl_to_str:n {#2} }
}
{
\bool_lazy_or:nnT
{ \cs_if_exist_p:c { \cs_to_str:N #1 ~ code } }
{ \cs_if_exist_p:c { \cs_to_str:N #1 ~ defaults } }
{
\msg_warning:nnx { cmd } { unsupported-let }
{ \token_to_str:N #1 }
}
\msg_info:nnxx { cmd } { define-command }
{ \token_to_str:N #1 } { \tl_to_str:n {#2} }
}
\bool_set_false:N \l_@@_environment_bool
\@@_declare_cmd_internal:Nnnn #1 {#2} {#3} { }
}
% \end{macrocode}
% At definition time, the variable \cs{l_@@_fn_tl} is only used for error messages.
% The real business of defining a document command starts with setting up
% the appropriate name, then normalizing the argument specification to get rid of
% shorthands.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_cmd_internal:Nnnn #1#2#3#4
{
\tl_set:Nx \l_@@_function_tl { \cs_to_str:N #1 }
\tl_set:Nx \l_@@_fn_tl
{ \exp_not:c { \l_@@_function_tl \c_space_tl } }
\@@_normalize_arg_spec:n {#2}
\exp_args:No \@@_prepare_signature:n \l_@@_arg_spec_tl
\@@_declare_cmd_code:Nnn #1 {#2} {#3}
#4
\@@_break_point:n {#2}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_break_point:n}
% A marker used to escape from creating a definition if necessary.
% \begin{macrocode}
\cs_new_eq:NN \@@_break_point:n \use_none:n
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_cmd_code:Nnn}
% \begin{macro}
% {\@@_declare_cmd_code_aux:Nnn, \@@_declare_cmd_code_expandable:Nnn}
% The appropriate auxiliary is called.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_cmd_code:Nnn
{
\bool_if:NTF \l_@@_grab_expandably_bool
{ \@@_declare_cmd_code_expandable:Nnn }
{ \@@_declare_cmd_code_aux:Nnn }
}
% \end{macrocode}
% Standard functions call \cs{@@_start:nNNnnn}, which receives the
% argument specification, an auxiliary used for
% grabbing arguments, an auxiliary containing the code, and then the
% signature, default arguments, and processors.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_cmd_code_aux:Nnn #1#2#3
{
\cs_generate_from_arg_count:cNnn
{ \l_@@_function_tl \c_space_tl code }
\cs_set_protected:Npn \l_@@_current_arg_int {#3}
\cs_set_protected_nopar:Npx #1
{
\bool_if:NTF \l_@@_environment_bool
{
\@@_start_env:nnnnn { \exp_not:n {#2} }
{ \l_@@_environment_str }
}
{
\@@_start:nNNnnn { \exp_not:n {#2} }
\exp_not:c { \l_@@_function_tl \c_space_tl }
\exp_not:c { \l_@@_function_tl \c_space_tl code }
}
{ \exp_not:o \l_@@_signature_tl }
{
\bool_if:NT \l_@@_defaults_bool
{ \exp_not:o \l_@@_defaults_tl }
}
{
\bool_if:NT \l_@@_process_some_bool
{ \exp_not:o \l_@@_process_all_tl }
}
}
}
% \end{macrocode}
% Expandable functions and functions whose arguments can be grabbed
% expandably call \cs{@@_start_expandable:nNNNNn}, which receives the
% argument specification, four auxiliaries (two for grabbing arguments, one for
% the code, and one for default arguments), and finally the signature.
% Non-expandable functions that take this branch should nevertheless
% be protected, as well as their \texttt{code} function. They will
% only be expanded in contexts such as constructing a csname.
% The two grabbers (named after the function with one or two spaces)
% are needed when there are both short and long arguments; otherwise
% the same grabber is included twice in the definition. If all
% arguments are long or all are short the (only) grabber is defined
% correspondingly to be long/short. Otherwise two grabbers are
% defined, one long, one short.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_cmd_code_expandable:Nnn #1#2#3
{
\exp_args:Ncc \cs_generate_from_arg_count:NNnn
{ \l_@@_function_tl \c_space_tl code }
{ cs_set \bool_if:NF \l_@@_expandable_bool { _protected } :Npn }
\l_@@_current_arg_int {#3}
\bool_if:NT \l_@@_defaults_bool
{
\use:x
{
\cs_generate_from_arg_count:cNnn
{ \l_@@_function_tl \c_space_tl defaults }
\cs_set:Npn \l_@@_current_arg_int
{ \exp_not:o \l_@@_defaults_tl }
}
}
\bool_if:NTF \l_@@_expandable_bool
{ \cs_set_nopar:Npx } { \cs_set_protected_nopar:Npx } #1
{
\exp_not:N \@@_start_expandable:nNNNNn
{ \exp_not:n {#2} }
\exp_not:c { \l_@@_function_tl \c_space_tl }
\exp_not:c
{
\l_@@_function_tl \c_space_tl
\bool_if:NT \l_@@_some_short_bool
{ \bool_if:NT \l_@@_some_long_bool { \c_space_tl } }
}
\exp_not:c { \l_@@_function_tl \c_space_tl code }
\bool_if:NTF \l_@@_defaults_bool
{ \exp_not:c { \l_@@_function_tl \c_space_tl defaults } }
{ ? }
{ \exp_not:o \l_@@_signature_tl }
}
\bool_if:NTF \l_@@_some_long_bool
{
\bool_if:NT \l_@@_some_short_bool
{
\cs_set_nopar:cpx { \l_@@_function_tl \c_space_tl \c_space_tl }
##1##2 { ##1 {##2} }
}
\cs_set:cpx
}
{ \cs_set_nopar:cpx }
{ \l_@@_function_tl \c_space_tl } ##1##2 { ##1 {##2} }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_declare_env:nnnn}
% \begin{macro}{\@@_declare_env_internal:nnnn}
% The lead-off to creating an environment is much the same as that for
% creating a command: issue the appropriate message, store the argument
% specification then hand off to an internal function.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_env:nnnn #1#2
{
\str_set:Nx \l_@@_environment_str {#1}
\str_set:Nx \l_@@_environment_str
{ \tl_trim_spaces:o { \l_@@_environment_str } }
\cs_if_exist:cTF { \l_@@_environment_str }
{
\msg_info:nnxx { cmd } { redefine-env }
{ \l_@@_environment_str } { \tl_to_str:n {#2} }
}
{
\msg_info:nnxx { cmd } { define-env }
{ \l_@@_environment_str } { \tl_to_str:n {#2} }
}
\bool_set_false:N \l_@@_expandable_bool
\bool_set_true:N \l_@@_environment_bool
\exp_args:NV \@@_declare_env_internal:nnnn
\l_@@_environment_str {#2}
}
% \end{macrocode}
% Creating a document environment requires a few more steps than creating
% a single command. In order to pass the arguments of the command to the
% end of the function, it is necessary to store the grabbed arguments.
% To do that, the function used at the end of the environment has to be
% redefined to contain the appropriate information. To minimize the amount
% of expansion at point of use, the code here is expanded now as well as
% when used.
% The last argument of \cs{@@_declare_cmd_internal:Nnnn} is only run
% if the definition succeeded. In package mode this ensures that the
% original definition of the environment is not changed if the
% definition fails for any reason. This also avoids an error when
% defining the \verb*|end aux | function when the user asks for more
% than $9$ arguments.
% \begin{macrocode}
\cs_new_protected:Npn \@@_declare_env_internal:nnnn #1#2#3#4
{
\exp_args:Nc \@@_declare_cmd_internal:Nnnn { environment~ #1 } {#2}
{#3}
{
\cs_set_nopar:cpx { environment~ #1 ~end }
{ \exp_not:c { environment~ #1 ~end~aux } }
\cs_generate_from_arg_count:cNnn
{ environment~ #1 ~end~aux~ } \cs_set:Npn
\l_@@_current_arg_int {#4}
\cs_set_eq:cc {#1} { environment~ #1 }
\cs_set_eq:cc { end #1 } { environment~ #1 ~end }
}
}
\cs_new_protected:Npn \@@_set_environment_end:n #1
{
\cs_set_nopar:cpx { environment~ #1 ~end~aux }
{
\exp_not:c { environment~ #1 ~end~aux~ }
\exp_not:o \l_@@_args_tl
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Structure of \pkg{xparse} commands}
%
% \begin{macro}{\@@_start_env:nnnnn, \@@_start:nNNnnn}
% For error messages that occur during run-time when getting arguments
% of environments it is necessary to keep track of the environment
% name. We begin non-expandable commands with a token equal to
% \cs{scan_stop:}, whose name gives a reasonable error message if the
% command is used inside a csname and protects against
% \texttt{f}-expansion. This is useless for environments since
% \cs{begin} is already not expandable. Both the command and
% environment codes start with \cs{group_align_safe_begin:}, then
% \cs{@@_run_code:} (used by both) does \cs{group_align_safe_end:}, so
% that delimited arguments may be grabbed in alignments if they
% contain and alignment tab token (see latex3/latex3/issues/839).
% \begin{macrocode}
\cs_new_protected:Npn \@@_start_env:nnnnn #1#2
{
\conditionally@traceoff
\group_align_safe_begin:
\str_set:Nn \l_@@_environment_str {#2}
\bool_set_true:N \l_@@_environment_bool
\@@_start_aux:ccnnnn
{ environment~ \l_@@_environment_str \c_space_tl }
{ environment~ \l_@@_environment_str \c_space_tl code }
{#1}
}
\cs_new_protected:Npx \@@_start:nNNnnn #1#2#3
{
\exp_not:c { xparse~function~is~not~expandable }
\exp_not:N \conditionally@traceoff
\exp_not:N \group_align_safe_begin:
\exp_not:n { \bool_set_false:N \l_@@_environment_bool }
\exp_not:N \@@_start_aux:NNnnnn
#2 #3 {#1}
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_start_aux:NNnnnn, \@@_start_aux:ccnnnn}
% \changes{v1.0i}{2021/12/02}
% {Correct defaults for optional arguments in end-of-environment code (gh/712)}
% This sets up a few variables to minimize the boilerplate code
% included in all \pkg{xparse}-defined commands. It then runs the
% grabbers~|#4|. Again, the argument specification |#1| is only for
% diagnostics.
% \begin{macrocode}
\cs_new_protected:Npn \@@_start_aux:NNnnnn #1#2#3#4#5#6
{
\tl_clear:N \l_@@_args_tl
\tl_set:Nn \l_@@_fn_tl {#1}
\tl_set:Nn \l_@@_fn_code_tl {#2}
\tl_set:Nn \l_@@_defaults_tl {#5}
\tl_set:Nn \l_@@_process_all_tl {#6}
#4
\@@_run_code:
}
\cs_generate_variant:Nn \@@_start_aux:NNnnnn { cc }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_run_code:}
% \changes{v1.0i}{2021/12/02}
% {Correct defaults for optional arguments in end-of-environment code (gh/712)}
% After arguments are grabbed, this function is responsible for
% inserting default values, running processors, and finally doing
% \cs{group_align_safe_end:} as promised, and running the code.
% \begin{macrocode}
\cs_new_protected:Npn \@@_run_code:
{
\tl_if_empty:NF \l_@@_defaults_tl { \@@_defaults: }
\tl_if_empty:NF \l_@@_process_all_tl { \@@_args_process: }
\bool_if:NT \l_@@_environment_bool
{ \exp_args:No \@@_set_environment_end:n \l_@@_environment_str }
\group_align_safe_end:
\conditionally@traceon
\exp_after:wN \l_@@_fn_code_tl \l_@@_args_tl
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_defaults:}
% \begin{macro}{\@@_defaults_def:, \@@_defaults_def:nn, \@@_defaults_def:nnn}
% \begin{macro}{\@@_defaults_aux:, \@@_defaults_error:w}
% First construct \cs{@@_tmp:w} (see below) that will receive
% the arguments found so far and determine default values for any
% missing argument. Then call it repeatedly until the set of
% arguments stabilizes. Since that could lead to an infinite loop we
% only call it up to nine times, the maximal number needed for
% stabilization if there is a chain of arguments that depend on each
% other. If that fails to stabilize raise an error.
% \begin{macrocode}
\cs_new_protected:Npn \@@_defaults:
{
\@@_defaults_def:
\tl_set_eq:NN \l_@@_args_i_tl \l_@@_args_tl
\@@_defaults_aux: \@@_defaults_aux: \@@_defaults_aux:
\@@_defaults_aux: \@@_defaults_aux: \@@_defaults_aux:
\@@_defaults_aux: \@@_defaults_aux: \@@_defaults_aux:
\@@_defaults_error:w
\q_recursion_stop
\tl_set_eq:NN \l_@@_args_tl \l_@@_args_i_tl
}
\cs_new_protected:Npn \@@_defaults_aux:
{
\tl_set:Nx \l_@@_args_ii_tl
{ \exp_after:wN \@@_tmp:w \l_@@_args_i_tl }
\tl_if_eq:NNT \l_@@_args_ii_tl \l_@@_args_i_tl
{ \use_none_delimit_by_q_recursion_stop:w }
\tl_set_eq:NN \l_@@_args_i_tl \l_@@_args_ii_tl
}
\cs_new_protected:Npn \@@_defaults_error:w \q_recursion_stop
{
\msg_error:nnx { cmd } { default-loop }
{ \@@_environment_or_command: }
}
% \end{macrocode}
% To construct \cs{@@_tmp:w}, first go through the arguments
% found and the corresponding defaults, building a token list with
% |{#|\meta{arg number}|}| for arguments found in the input (whose
% default will not be used) and otherwise
% |{|\cs{exp_not:n}\Arg{default}|}| for arguments whose default will
% be used.
% \begin{macrocode}
\cs_new_protected:Npn \@@_defaults_def:
{
\tl_clear:N \l_@@_tmpa_tl
\int_zero:N \l_@@_current_arg_int
\@@_tl_mapthread_function:NNN \l_@@_args_tl \l_@@_defaults_tl
\@@_defaults_def:nn
\cs_generate_from_arg_count:NNVo \@@_tmp:w \cs_set:Npn
\l_@@_current_arg_int \l_@@_tmpa_tl
}
\cs_generate_variant:Nn \cs_generate_from_arg_count:NNnn { NNVo }
\cs_new_protected:Npn \@@_defaults_def:nn
{
\int_incr:N \l_@@_current_arg_int
\exp_args:NV \@@_defaults_def:nnn \l_@@_current_arg_int
}
\cs_new_protected:Npn \@@_defaults_def:nnn #1#2#3
{
\tl_put_right:Nx \l_@@_tmpa_tl
{
{
\exp_not:N \exp_not:n
{
\tl_if_novalue:nTF {#2}
{ \exp_not:o {#3} }
{ \exp_not:n { ## #1 } }
}
}
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_args_process:}
% \begin{macro}{\@@_args_process_loop:nn, \@@_args_process_aux:n}
% Loop through arguments (stored in \cs{l_@@_args_tl}) and the
% corresponding processors (in \cs{l_@@_process_all_tl})
% simultaneously, apply all processors for each argument and store the
% result back into \cs{l_@@_args_tl}. To allow processors to depend
% on other arguments, for every processor define a temporary auxiliary
% that receives all arguments \cs{l_@@_args_tl}.
% \begin{macrocode}
\cs_new_protected:Npn \@@_args_process:
{
\tl_clear:N \l_@@_args_ii_tl
\@@_tl_mapthread_function:NNN
\l_@@_args_tl
\l_@@_process_all_tl
\@@_args_process_loop:nn
\tl_set_eq:NN \l_@@_args_tl \l_@@_args_ii_tl
}
\cs_new_protected:Npn \@@_args_process_loop:nn #1#2
{
\tl_set:Nn \ProcessedArgument {#1}
\tl_if_novalue:nF {#1}
{ \tl_map_function:nN {#2} \@@_args_process_aux:n }
\tl_put_right:No \l_@@_args_ii_tl
{ \exp_after:wN { \ProcessedArgument } }
}
\cs_new_protected:Npn \@@_args_process_aux:n #1
{
\cs_generate_from_arg_count:NNnn \@@_tmp:w \cs_set:Npn
{ \tl_count:N \l_@@_args_tl } {#1}
\exp_args:NNNo \exp_after:wN \@@_tmp:w \l_@@_args_tl
{ \ProcessedArgument }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_start_expandable:nNNNNn}
% This is called for all expandable commands. |#6| is the signature,
% responsible for grabbing arguments. |#5| is used to determine
% default values (or is |?| if there are none). |#4| is the code to run.
% |#2|~and~|#3| are functions (named after the command) that grab a single
% argument in the input stream (|#3|~is~short). The argument specification |#1| is
% only used by diagnostic functions. Same as for the non-expandable
% version, this starts with \cs{group_align_safe_begin:}, which
% expands to nothing, so may be safely used in an expandable context.
% \begin{macrocode}
\cs_new:Npn \@@_start_expandable:nNNNNn #1#2#3#4#5#6
{
\group_align_safe_begin:
#6 \@@_end_expandable:NNw #5 #4 \q_@@ #2#3
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_end_expandable:NNw}
% \begin{macro}[EXP]{\@@_end_expandable_aux:w}
% \begin{macro}[EXP]{\@@_end_expandable_aux:nNNNN}
% \begin{macro}[EXP]{\@@_end_expandable_defaults:nnnNNn}
% \begin{macro}[EXP]{\@@_end_expandable_defaults:nnw}
% \begin{macro}[EXP]{\@@_end_expandable_defaults:nw}
% Followed by a function |#1| to determine default values (or |?| if
% there are no defaults), the code
% |#2|, arguments that have been grabbed, then \cs{q_@@} and two generic
% grabbers. The idea to find default values is similar to the
% non-expandable case but we cannot define an auxiliary function, so
% at every step in the loop we need to go through all arguments
% searching for which ones started out as |-NoValue-| and replacing
% these by the newly computed values. In fact we need to keep track
% of three versions of all arguments: the original version, the
% previous version with default values, and the currently built
% version (first argument of \cs{@@_end_expandable_defaults:nnnNNn}).
% \begin{macrocode}
\cs_new:Npn \@@_end_expandable:NNw #1#2
{ \@@_end_expandable_aux:w #1#2 \prg_do_nothing: }
\cs_new:Npn \@@_end_expandable_aux:w #1#2#3 \q_@@
{ \exp_args:No \@@_end_expandable_aux:nNNNN {#3} #1 #2 }
\cs_new:Npn \@@_end_expandable_aux:nNNNN #1#2#3#4#5
{
\token_if_eq_charcode:NNT ? #2 { \exp_after:wN \use_iv:nnnn }
\@@_end_expandable_defaults:nnnNNn {#1} { } {#1} #2#3
{ } { } { } { } { } { } { } { } { } { }
{
\msg_expandable_error:nnf { cmd } { default-loop }
{ \exp_args:Nf \tl_trim_spaces:n { \token_to_str:N #4 } }
\use_iv:nnnn
}
\q_stop
}
\cs_new:Npn \@@_end_expandable_defaults:nnnNNn #1#2#3#4#5#6
{
#6
\str_if_eq:nnTF {#1} {#2}
{ \use_i_delimit_by_q_stop:nw { \group_align_safe_end: #5 #1 } }
{
\exp_args:No \@@_tl_mapthread_function:nnN
{ #4 #1 } {#3}
\@@_end_expandable_defaults:nnw
\@@_end_expandable_defaults:nnnNNn { } {#1} {#3} #4 #5
}
}
\cs_new:Npn \@@_end_expandable_defaults:nnw #1#2
{
\tl_if_novalue:nTF {#2}
{ \exp_args:No \@@_end_expandable_defaults:nw {#1} }
{ \@@_end_expandable_defaults:nw {#2} }
}
\cs_new:Npn \@@_end_expandable_defaults:nw
#1#2 \@@_end_expandable_defaults:nnnNNn #3
{ #2 \@@_end_expandable_defaults:nnnNNn { #3 {#1} } }
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Normalizing the argument specifications}
%
% The goal here is to expand aliases and check that the argument
% specification is valid before the main parsing run. If it is not
% valid the entire set up is abandoned to avoid any strange internal
% errors. A function is provided for each argument type that will grab
% any extra data items and call the loop function after performing the
% following checks and tasks.
% \begin{itemize}
% \item Check that each argument has the correct number of data items
% associated with it, and that where a single character is required,
% one has actually been supplied.
% \item Check that processors and the markers~|+| and~|!| are followed
% by an argument for which they make sense, and are not redundant.
% \item Check the absence of forbidden types for expandable commands,
% namely \texttt{G}/\texttt{v} always, and \texttt{l}/\texttt{u}
% after optional arguments (\pkg{xparse} may have inserted braces
% due to a failed search for an optional argument).
% \item Check that no optional argument is followed by a mandatory
% argument with the same delimiter, as otherwise the optional
% argument could never be omitted.
% \item Keep track in \cs{l_@@_some_long_bool} and
% \cs{l_@@_some_short_bool} of whether the command has some
% long/short arguments.
% \item Keep track in \cs{l_@@_grab_expandably_bool} of whether all
% arguments are \texttt{m}/\texttt{l}/\texttt{u} type and short
% arguments appear before long ones, in which case they can be
% grabbed expandably just as safely as they could be grabbed
% nonexpandably. Regardless of that, arguments of expandable
% commands will be grabbed expandably and arguments of environments
% will not (because the list of arguments built by non-expandable
% grabbing is used to pass them to the end-environment code).
% \end{itemize}
% Further checks happen at the end of the loop:
% \begin{itemize}
% \item that there are at most $9$ arguments;
% \item that an expandable command does not end with an optional
% argument (this case is detected by using the fact that
% \cs{l_@@_last_delimiters_tl} is cleared by every mandatory argument
% and filled by every optional argument).
% \end{itemize}
%
% \begin{macro}{\@@_normalize_arg_spec:n}
% \begin{macro}{\@@_normalize_arg_spec_loop:n}
% Loop through the argument specification, calling an auxiliary
% specific to each argument type. If any argument is unknown stop the
% definition.
% \begin{macrocode}
\cs_new_protected:Npn \@@_normalize_arg_spec:n #1
{
\int_zero:N \l_@@_current_arg_int
\tl_clear:N \l_@@_last_delimiters_tl
\tl_clear:N \l_@@_arg_spec_tl
\bool_set_true:N \l_@@_grab_expandably_bool
\bool_set_false:N \l_@@_obey_spaces_bool
\bool_set_false:N \l_@@_long_bool
\bool_set_false:N \l_@@_some_obey_spaces_bool
\bool_set_false:N \l_@@_some_long_bool
\bool_set_false:N \l_@@_some_short_bool
\@@_normalize_arg_spec_loop:n #1
\q_recursion_tail \q_recursion_tail \q_recursion_tail \q_recursion_stop
\int_compare:nNnT \l_@@_current_arg_int > 9
{
\msg_error:nnxx { cmd } { too-many-args }
{ \@@_environment_or_command: } { \tl_to_str:n {#1} }
\@@_bad_def:wn
}
\bool_if:NT \l_@@_expandable_bool
{
\tl_if_empty:NF \l_@@_last_delimiters_tl
{
\msg_error:nnxx { cmd } { expandable-ending-optional }
{ \iow_char:N \\ \l_@@_function_tl } { \tl_to_str:n {#1} }
\@@_bad_def:wn
}
}
\bool_if:NT \l_@@_expandable_bool
{ \bool_set_true:N \l_@@_grab_expandably_bool }
\bool_if:NT \l_@@_environment_bool
{ \bool_set_false:N \l_@@_grab_expandably_bool }
}
\cs_new_protected:Npn \@@_normalize_arg_spec_loop:n #1
{
\quark_if_recursion_tail_stop:n {#1}
\int_incr:N \l_@@_current_arg_int
\cs_if_exist_use:cF { @@_normalize_type_ \tl_to_str:n {#1} :w }
{
\bool_lazy_any:nTF
{
{ \str_if_eq_p:nn {#1} { G } }
{ \str_if_eq_p:nn {#1} { g } }
{ \str_if_eq_p:nn {#1} { l } }
{ \str_if_eq_p:nn {#1} { u } }
}
{
\msg_error:nnxx { cmd } { xparse-arg-type }
{ \@@_environment_or_command: } { \tl_to_str:n {#1} }
}
{
\msg_error:nnxx { cmd } { unknown-argument-type }
{ \@@_environment_or_command: } { \tl_to_str:n {#1} }
}
\@@_bad_def:wn
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}
% {
% \@@_normalize_type_d:w,
% \@@_normalize_type_e:w,
% \@@_normalize_type_o:w,
% \@@_normalize_type_O:w,
% \@@_normalize_type_r:w,
% \@@_normalize_type_s:w,
% }
% These argument types are aliases of more general ones, for example
% with the default argument |-NoValue-|. To easily insert that marker
% expanded in the definitions we call \cs{@@_tmp:w} with the argument
% |-NoValue-|. For argument types that need additional data, check
% that the data is present (not \cs{q_recursion_tail}) before
% proceeding.
% \begin{macrocode}
\cs_set_protected:Npn \@@_tmp:w #1
{
\cs_new_protected:Npn \@@_normalize_type_d:w ##1##2
{
\quark_if_recursion_tail_stop_do:nn {##2} { \@@_bad_arg_spec:wn }
\@@_normalize_type_D:w {##1} {##2} {#1}
}
\cs_new_protected:Npn \@@_normalize_type_e:w ##1
{
\quark_if_recursion_tail_stop_do:nn {##1} { \@@_bad_arg_spec:wn }
\@@_normalize_type_E:w {##1} { }