diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3tl.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3tl.dtx | 860 |
1 files changed, 512 insertions, 348 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx b/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx index 2ef110b3838..b942e97703c 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-05} +% \date{Released 2020-10-27} % % \maketitle % @@ -1269,6 +1269,15 @@ % % \subsection{Functions} % +% \begin{macro}{\__kernel_tl_set:Nx,\__kernel_tl_gset:Nx} +% These two are supplied to get better performance for macros which would +% otherwise use \cs{tl_set:Nx} or \cs{tl_gset:Nx} internally. +% \begin{macrocode} +\cs_new_eq:NN \__kernel_tl_set:Nx \cs_set_nopar:Npx +\cs_new_eq:NN \__kernel_tl_gset:Nx \cs_gset_nopar:Npx +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\tl_new:N, \tl_new:c} % Creating new token list variables is a case of checking for an % existing definition and doing the definition. @@ -1283,12 +1292,14 @@ % \end{macro} % % \begin{macro}{\tl_const:Nn, \tl_const:Nx, \tl_const:cn, \tl_const:cx} -% Constants are also easy to generate. +% Constants are also easy to generate. They use \cs{cs_gset_nopar:Npx} instead +% of \cs{__kernel_tl_gset:Nx} so that the correct scope checking is applied if +% \pkg{l3debug} is used. % \begin{macrocode} \cs_new_protected:Npn \tl_const:Nn #1#2 { \__kernel_chk_if_free_cs:N #1 - \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } + \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w {#2} } } \cs_new_protected:Npn \tl_const:Nx #1#2 { @@ -1350,9 +1361,21 @@ % \cs{scan_stop:} would lead to problems later on. % \begin{macrocode} \cs_new_protected:Npn \tl_concat:NNN #1#2#3 - { \tl_set:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } } + { + \__kernel_tl_set:Nx #1 + { + \__kernel_exp_not:w \exp_after:wN {#2} + \__kernel_exp_not:w \exp_after:wN {#3} + } + } \cs_new_protected:Npn \tl_gconcat:NNN #1#2#3 - { \tl_gset:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } } + { + \__kernel_tl_gset:Nx #1 + { + \__kernel_exp_not:w \exp_after:wN {#2} + \__kernel_exp_not:w \exp_after:wN {#3} + } + } \cs_generate_variant:Nn \tl_concat:NNN { ccc } \cs_generate_variant:Nn \tl_gconcat:NNN { ccc } % \end{macrocode} @@ -1419,17 +1442,17 @@ % \enquote{by hand} as it is used quite a lot. % \begin{macrocode} \cs_new_protected:Npn \tl_set:Nn #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:n {#2} } } + { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w {#2} } } \cs_new_protected:Npn \tl_set:No #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:o {#2} } } + { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } } \cs_new_protected:Npn \tl_set:Nx #1#2 - { \cs_set_nopar:Npx #1 {#2} } + { \__kernel_tl_set:Nx #1 {#2} } \cs_new_protected:Npn \tl_gset:Nn #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } } + { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w {#2} } } \cs_new_protected:Npn \tl_gset:No #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} } } + { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } } \cs_new_protected:Npn \tl_gset:Nx #1#2 - { \cs_gset_nopar:Npx #1 {#2} } + { \__kernel_tl_gset:Nx #1 {#2} } \cs_generate_variant:Nn \tl_set:Nn { NV , Nv , Nf } \cs_generate_variant:Nn \tl_set:Nx { c } \cs_generate_variant:Nn \tl_set:Nn { c, co , cV , cv , cf } @@ -1453,21 +1476,45 @@ % Adding to the left is done directly to gain a little performance. % \begin{macrocode} \cs_new_protected:Npn \tl_put_left:Nn #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } } + { + \__kernel_tl_set:Nx #1 + { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} } + } \cs_new_protected:Npn \tl_put_left:NV #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } } + { + \__kernel_tl_set:Nx #1 + { \exp_not:V #2 \__kernel_exp_not:w \exp_after:wN {#1} } + } \cs_new_protected:Npn \tl_put_left:No #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } } + { + \__kernel_tl_set:Nx #1 + { + \__kernel_exp_not:w \exp_after:wN {#2} + \__kernel_exp_not:w \exp_after:wN {#1} + } + } \cs_new_protected:Npn \tl_put_left:Nx #1#2 - { \cs_set_nopar:Npx #1 { #2 \exp_not:o #1 } } + { \__kernel_tl_set:Nx #1 { #2 \__kernel_exp_not:w \exp_after:wN {#1} } } \cs_new_protected:Npn \tl_gput_left:Nn #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } } + { + \__kernel_tl_gset:Nx #1 + { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} } + } \cs_new_protected:Npn \tl_gput_left:NV #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } } + { + \__kernel_tl_gset:Nx #1 + { \exp_not:V #2 \__kernel_exp_not:w \exp_after:wN {#1} } + } \cs_new_protected:Npn \tl_gput_left:No #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } } + { + \__kernel_tl_gset:Nx #1 + { + \__kernel_exp_not:w \exp_after:wN {#2} + \__kernel_exp_not:w \exp_after:wN {#1} + } + } \cs_new_protected:Npn \tl_gput_left:Nx #1#2 - { \cs_gset_nopar:Npx #1 { #2 \exp_not:o {#1} } } + { \__kernel_tl_gset:Nx #1 { #2 \__kernel_exp_not:w \exp_after:wN {#1} } } \cs_generate_variant:Nn \tl_put_left:Nn { c } \cs_generate_variant:Nn \tl_put_left:NV { c } \cs_generate_variant:Nn \tl_put_left:No { c } @@ -1495,21 +1542,39 @@ % The same on the right. % \begin{macrocode} \cs_new_protected:Npn \tl_put_right:Nn #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } } + { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } } \cs_new_protected:Npn \tl_put_right:NV #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } } + { + \__kernel_tl_set:Nx #1 + { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:V #2 } + } \cs_new_protected:Npn \tl_put_right:No #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } } + { + \__kernel_tl_set:Nx #1 + { + \__kernel_exp_not:w \exp_after:wN {#1} + \__kernel_exp_not:w \exp_after:wN {#2} + } + } \cs_new_protected:Npn \tl_put_right:Nx #1#2 - { \cs_set_nopar:Npx #1 { \exp_not:o #1 #2 } } + { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#1} #2 } } \cs_new_protected:Npn \tl_gput_right:Nn #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } } + { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } } \cs_new_protected:Npn \tl_gput_right:NV #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } } + { + \__kernel_tl_gset:Nx #1 + { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:V #2 } + } \cs_new_protected:Npn \tl_gput_right:No #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } } + { + \__kernel_tl_gset:Nx #1 + { + \__kernel_exp_not:w \exp_after:wN {#1} + \__kernel_exp_not:w \exp_after:wN {#2} + } + } \cs_new_protected:Npn \tl_gput_right:Nx #1#2 - { \cs_gset_nopar:Npx #1 { \exp_not:o {#1} #2 } } + { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#1} #2 } } \cs_generate_variant:Nn \tl_put_right:Nn { c } \cs_generate_variant:Nn \tl_put_right:NV { c } \cs_generate_variant:Nn \tl_put_right:No { c } @@ -1574,6 +1639,7 @@ % \tl_gset_rescan:cnn, \tl_gset_rescan:cno, \tl_gset_rescan:cnx % } % \begin{macro}{\tl_rescan:nn} +% \begin{macro}{\@@_rescan_aux:} % \begin{macro}{\@@_set_rescan:NNnn, \@@_set_rescan_multi:nNN} % \begin{macro}[EXP]{\@@_rescan:NNw} % In a group, after some initial setup explained below and the user @@ -1632,9 +1698,11 @@ \cs_new_protected:Npn \tl_rescan:nn #1#2 { \tl_set_rescan:Nnn \l_@@_internal_a_tl {#1} {#2} - \exp_after:wN \tl_clear:N \exp_after:wN \l_@@_internal_a_tl + \exp_after:wN \@@_rescan_aux: \l_@@_internal_a_tl } +\exp_args:NNo \cs_new_protected:Npn \@@_rescan_aux: + { \tl_clear:N \l_@@_internal_a_tl } \cs_new_protected:Npn \tl_set_rescan:Nnn { \@@_set_rescan:NNnn \tl_set:No } \cs_new_protected:Npn \tl_gset_rescan:Nnn @@ -1653,7 +1721,7 @@ } \cs_new_protected:Npn \@@_set_rescan_multi:nNN #1#2#3 { - \exp_args:No \tex_everyeof:D { \c_@@_rescan_marker_tl } + \tex_everyeof:D \exp_after:wN { \c_@@_rescan_marker_tl } \exp_after:wN \@@_rescan:NNw \exp_after:wN #2 \exp_after:wN #3 @@ -1676,6 +1744,7 @@ % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}{\@@_set_rescan:nNN} % \begin{macro}{\@@_set_rescan_single:nnNN, \@@_set_rescan_single_aux:nnnNN} @@ -1802,13 +1871,13 @@ % the \meta{tl~var} by \meta{token list}. % \begin{macrocode} \cs_new_protected:Npn \tl_replace_once:Nnn - { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_wrap:w \tl_set:Nx } + { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_wrap:w \__kernel_tl_set:Nx } \cs_new_protected:Npn \tl_greplace_once:Nnn - { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_wrap:w \tl_gset:Nx } + { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_wrap:w \__kernel_tl_gset:Nx } \cs_new_protected:Npn \tl_replace_all:Nnn - { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_next:w \tl_set:Nx } + { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_next:w \__kernel_tl_set:Nx } \cs_new_protected:Npn \tl_greplace_all:Nnn - { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_next:w \tl_gset:Nx } + { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_next:w \__kernel_tl_gset:Nx } \cs_generate_variant:Nn \tl_replace_once:Nnn { c } \cs_generate_variant:Nn \tl_greplace_once:Nnn { c } \cs_generate_variant:Nn \tl_replace_all:Nnn { c } @@ -1825,6 +1894,7 @@ % \@@_replace_auxi:NnnNNNnn, % \@@_replace_auxii:nNNNnn, % \@@_replace_next:w, +% \@@_replace_next_aux:w, % \@@_replace_wrap:w, % } % To implement the actual replacement auxiliary @@ -1969,7 +2039,7 @@ { \group_align_safe_begin: \cs_set:Npn \@@_replace_wrap:w ##1 #1 ##2 - { \exp_not:o { \use_none:nn ##1 } ##2 } + { \__kernel_exp_not:w \exp_after:wN { \use_none:nn ##1 } ##2 } \cs_set:Npx \@@_replace_next:w ##1 #5 { \exp_not:N \@@_replace_wrap:w ##1 @@ -1979,9 +2049,7 @@ } #3 #4 { - \exp_after:wN \@@_replace_next:w - \exp_after:wN { \exp_after:wN } - \exp_after:wN { \exp_after:wN } + \exp_after:wN \@@_replace_next_aux:w #4 #1 { @@ -1992,6 +2060,7 @@ } \group_align_safe_end: } +\cs_new:Npn \@@_replace_next_aux:w { \@@_replace_next:w { } { } } \cs_new_eq:NN \@@_replace_wrap:w ? \cs_new_eq:NN \@@_replace_next:w ? % \end{macrocode} @@ -2027,29 +2096,6 @@ % % \subsection{Token list conditionals} % -% \begin{macro}[pTF]{\tl_if_blank:n, \tl_if_blank:V, \tl_if_blank:o} -% \begin{macro}{\@@_if_blank_p:NNw} -% \TeX{} skips spaces when reading a non-delimited arguments. Thus, -% a \meta{token list} is blank if and only if \cs{use_none:n} -% \meta{token list} |?| is empty after one expansion. The auxiliary -% \cs{@@_if_empty_if:o} is a fast emptyness test, converting its -% argument to a string (after one expansion) and using the test -% \cs{if_meaning:w} \cs{q_@@_nil} |...| \cs{q_@@_nil}. -% \begin{macrocode} -\prg_new_conditional:Npnn \tl_if_blank:n #1 { p , T , F , TF } - { - \@@_if_empty_if:o { \use_none:n #1 ? } - \prg_return_true: - \else: - \prg_return_false: - \fi: - } -\prg_generate_conditional_variant:Nnn \tl_if_blank:n - { e , V , o } { p , T , F , TF } -% \end{macrocode} -% \end{macro} -% \end{macro} -% % \begin{macro}[pTF]{\tl_if_empty:N, \tl_if_empty:c} % These functions check whether the token list in the argument is % empty and execute the proper code from their argument(s). @@ -2068,23 +2114,17 @@ % \end{macro} % % \begin{macro}[pTF]{\tl_if_empty:n, \tl_if_empty:V} -% Convert the argument to a string: this is empty if and only if -% the argument is. Then |\if_meaning:w \q_@@_nil ... \q_@@_nil| is -% \texttt{true} if and only if the string |...| is empty. -% It could be tempting to use |\if_meaning:w \q_@@_nil #1 \q_@@_nil| directly. -% This fails on a token -% list starting with \cs{q_@@_nil} of course but more troubling is the -% case where argument is a complete conditional such as \cs{if_true:} -% a \cs{else:} b \cs{fi:} because then \cs{if_true:} is used by -% \cs{if_meaning:w}, the test turns out \texttt{false}, the \cs{else:} -% executes the \texttt{false} branch, the \cs{fi:} ends it and the -% \cs{q_@@_nil} at the end -% starts executing\dots{} +% The \cs{if:w} triggers the expansion of \cs{tl_to_str:n} which converts the +% argument to a string: this is empty if and only if the argument is. Then +% |\if:w \scan_stop: ... \scan_stop:| is \texttt{true} if and only if the +% string |...| is empty. +% It could be tempting to use |\if:w \scan_stop: #1 \scan_stop:| directly. +% But this fails on a token list expanding to anything starting with +% \cs{scan_stop:} leaving everything that follows in the input stream. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_empty:n #1 { p , TF , T , F } { - \exp_after:wN \if_meaning:w \exp_after:wN \q_@@_nil - \tl_to_str:n {#1} \q_@@_nil + \if:w \scan_stop: \tl_to_str:n {#1} \scan_stop: \prg_return_true: \else: \prg_return_false: @@ -2107,13 +2147,15 @@ % We don't put \cs{prg_return_true:} and so on in the definition of % the auxiliary, because that would prevent an optimization applied to % conditionals that end with this code. +% Also the |\@@_if_empty_if:o| is expanded once in |\tl_if_empty:oTF| for +% efficiency as well (and to reduce code doubling). % \begin{macrocode} \cs_new:Npn \@@_if_empty_if:o #1 { - \exp_after:wN \if_meaning:w \exp_after:wN \q_@@_nil - \__kernel_tl_to_str:w \exp_after:wN {#1} \q_@@_nil + \if:w \scan_stop: \__kernel_tl_to_str:w \exp_after:wN {#1} \scan_stop: } -\prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F } +\exp_args:Nno \use:n + { \prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F } } { \@@_if_empty_if:o {#1} \prg_return_true: @@ -2125,6 +2167,30 @@ % \end{macro} % \end{macro} % +% \begin{macro}[pTF]{\tl_if_blank:n, \tl_if_blank:V, \tl_if_blank:o} +% \begin{macro}{\@@_if_blank_p:NNw} +% \TeX{} skips spaces when reading a non-delimited arguments. Thus, +% a \meta{token list} is blank if and only if \cs{use_none:n} +% \meta{token list} |?| is empty after one expansion. The auxiliary +% \cs{@@_if_empty_if:o} is a fast emptyness test, converting its +% argument to a string (after one expansion) and using the test +% \cs{if:w} \cs{scan_stop:} |...| \cs{scan_stop:}. +% \begin{macrocode} +\exp_args:Nno \use:n + { \prg_new_conditional:Npnn \tl_if_blank:n #1 { p , T , F , TF } } + { + \@@_if_empty_if:o { \use_none:n #1 ? } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \tl_if_blank:n + { e , V , o } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \begin{macro}[pTF]{\tl_if_eq:NN, \tl_if_eq:Nc, \tl_if_eq:cN, \tl_if_eq:cc} % Returns \cs{c_true_bool} if and only if the two token list variables are % equal. @@ -2269,28 +2335,28 @@ % \end{macro} % % \begin{macro}[EXP,pTF]{\tl_if_single:n} -% \begin{macro}[EXP,pTF]{\@@_if_single:n} +% \begin{macro}[EXP]{\@@_if_single:nnw} % This test is similar to \cs{tl_if_empty:nTF}. Expanding % \cs{use_none:nn} |#1| |??| once yields an empty result if |#1| is % blank, a single~|?| if |#1| has a single item, and otherwise yields -% some tokens ending with |??|. Then, \cs{tl_to_str:n} makes sure +% some tokens ending with |??|. Then, \cs{__kernel_tl_to_str:w} makes sure % there are no odd category codes. An earlier version would compare % the result to a single~|?| using string comparison, but the Lua call % is slow in \LuaTeX{}. Instead, \cs{@@_if_single:nnw} picks the % second token in front of it. If |#1| is empty, this token is -% the trailing~|?| and the catcode test yields \texttt{false}. If -% |#1| has a single item, the token is~|^| and the catcode test +% the trailing~|?| and the |\if:w| test yields \texttt{false}. If +% |#1| has a single item, the token is~|\scan_stop:| and the |\if:w| test % yields \texttt{true}. Otherwise, it is one of the characters -% resulting from \cs{tl_to_str:n}, and the catcode test yields -% \texttt{false}. Note that \cs{if_catcode:w} and +% resulting from \cs{tl_to_str:n}, and the |\if:w| test yields +% \texttt{false}. Note that \cs{if:w} and % \cs{__kernel_tl_to_str:w} are primitives that take care of % expansion. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF } { - \if_catcode:w ^ \exp_after:wN \@@_if_single:nnw + \if:w \scan_stop: \exp_after:wN \@@_if_single:nnw \__kernel_tl_to_str:w - \exp_after:wN { \use_none:nn #1 ?? } ^ ? \s_@@_stop + \exp_after:wN { \use_none:nn #1 ?? } \scan_stop: ? \s_@@_stop \prg_return_true: \else: \prg_return_false: @@ -2587,18 +2653,16 @@ { \int_eval:n { - \@@_act:NNNnn - \@@_act_count_normal:nN - \@@_act_count_group:nn - \@@_act_count_space:n - { } + \@@_act:NNNn + \@@_act_count_normal:N + \@@_act_count_group:n + \@@_act_count_space: {#1} } } -\cs_new:Npn \@@_act_count_normal:nN #1 #2 { 1 + } -\cs_new:Npn \@@_act_count_space:n #1 { 1 + } -\cs_new:Npn \@@_act_count_group:nn #1 #2 - { 2 + \tl_count_tokens:n {#2} + } +\cs_new:Npn \@@_act_count_normal:N #1 { 1 + } +\cs_new:Npn \@@_act_count_space: { 1 + } +\cs_new:Npn \@@_act_count_group:n #1 { 2 + \tl_count_tokens:n {#1} + } % \end{macrocode} % \end{macro} % \end{macro} @@ -2624,7 +2688,7 @@ \s_@@_stop { {#1} #5 } } \cs_new:Npn \@@_reverse_items:wn #1 \s_@@_stop #2 - { \exp_not:o { \use_none:nn #2 } } + { \__kernel_exp_not:w \exp_after:wN { \use_none:nn #2 } } % \end{macrocode} % \end{macro} % \end{macro} @@ -2639,22 +2703,28 @@ % } % Trimming spaces from around the input is deferred to an internal % function whose first argument is the token list to trim, augmented -% by an initial \cs{s_@@_mark}, and whose second argument is a +% by an initial \cs{@@_trim_mark:}, and whose second argument is a % \meta{continuation}, which receives as a braced argument -% \cs{use_none:n} \cs{s_@@_mark} \meta{trimmed token list}. In the case -% at hand, we take \cs{exp_not:o} as our continuation, so that space -% trimming behaves correctly within an \texttt{x}-type expansion. +% \cs{@@_trim_mark:} \meta{trimmed token list}. The control sequence +% \cs{@@_trim_mark:} expands to nothing in a single expansion. In the case +% at hand, we take \cs{__kernel_exp_not:w} \cs{exp_after:wN} as our +% continuation, so that space trimming behaves correctly within an +% \texttt{x}-type expansion. % \begin{macrocode} \cs_new:Npn \tl_trim_spaces:n #1 - { \@@_trim_spaces:nn { \s_@@_mark #1 } \exp_not:o } + { + \@@_trim_spaces:nn + { \@@_trim_mark: #1 } + { \__kernel_exp_not:w \exp_after:wN } + } \cs_generate_variant:Nn \tl_trim_spaces:n { o } \cs_new:Npn \tl_trim_spaces_apply:nN #1#2 - { \@@_trim_spaces:nn { \s_@@_mark #1 } { \exp_args:No #2 } } + { \@@_trim_spaces:nn { \@@_trim_mark: #1 } { \exp_args:No #2 } } \cs_generate_variant:Nn \tl_trim_spaces_apply:nN { o } \cs_new_protected:Npn \tl_trim_spaces:N #1 - { \tl_set:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } } + { \__kernel_tl_set:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } } \cs_new_protected:Npn \tl_gtrim_spaces:N #1 - { \tl_gset:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } } + { \__kernel_tl_gset:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } } \cs_generate_variant:Nn \tl_trim_spaces:N { c } \cs_generate_variant:Nn \tl_gtrim_spaces:N { c } % \end{macrocode} @@ -2667,45 +2737,48 @@ % \@@_trim_spaces_auxi:w, \@@_trim_spaces_auxii:w, % \@@_trim_spaces_auxiii:w, \@@_trim_spaces_auxiv:w % } +% \begin{macro}{\@@_trim_mark:} % Trimming spaces from around the input is done using delimited % arguments and quarks, and to get spaces at odd places in the % definitions, we nest those in \cs{@@_tmp:w}, which then receives % a single space as its argument: |#1| is \verb*+ +. % Removing leading spaces is done with \cs{@@_trim_spaces_auxi:w}, -% which loops until \cs{s_@@_mark}\verb*+ + matches the end of the token +% which loops until \cs{@@_trim_mark:}\verb*+ + matches the end of the token % list: then |##1| is the token list and |##3| is % \cs{@@_trim_spaces_auxii:w}. This hands the relevant tokens to the % loop \cs{@@_trim_spaces_auxiii:w}, responsible for trimming % trailing spaces. The end is reached when \verb*+ + \cs{s_@@_nil} -% matches the one present in the definition of \cs{tl_trim_spacs:n}. +% matches the one present in the definition of \cs{tl_trim_spaces:n}. % Then \cs{@@_trim_spaces_auxiv:w} puts the token list into a group, -% with \cs{use_none:n} placed there to gobble a lingering \cs{s_@@_mark}, -% and feeds this to the \meta{continuation}. +% with a lingering \cs{@@_trim_mark:} at the start (which will expand to +% nothing in one step of expansion), and feeds this to the +% \meta{continuation}. % \begin{macrocode} -\cs_set:Npn \@@_tmp:w #1 +\cs_set_protected:Npn \@@_tmp:w #1 { \cs_new:Npn \@@_trim_spaces:nn ##1 { \@@_trim_spaces_auxi:w ##1 \s_@@_nil - \s_@@_mark #1 { } - \s_@@_mark \@@_trim_spaces_auxii:w + \@@_trim_mark: #1 { } + \@@_trim_mark: \@@_trim_spaces_auxii:w \@@_trim_spaces_auxiii:w #1 \s_@@_nil \@@_trim_spaces_auxiv:w \s_@@_stop } - \cs_new:Npn \@@_trim_spaces_auxi:w ##1 \s_@@_mark #1 ##2 \s_@@_mark ##3 + \cs_new:Npn + \@@_trim_spaces_auxi:w ##1 \@@_trim_mark: #1 ##2 \@@_trim_mark: ##3 { ##3 \@@_trim_spaces_auxi:w - \s_@@_mark + \@@_trim_mark: ##2 - \s_@@_mark #1 {##1} + \@@_trim_mark: #1 {##1} } \cs_new:Npn \@@_trim_spaces_auxii:w - \@@_trim_spaces_auxi:w \s_@@_mark \s_@@_mark ##1 + \@@_trim_spaces_auxi:w \@@_trim_mark: \@@_trim_mark: ##1 { \@@_trim_spaces_auxiii:w ##1 @@ -2717,172 +2790,21 @@ \@@_trim_spaces_auxiii:w } \cs_new:Npn \@@_trim_spaces_auxiv:w ##1 \s_@@_nil ##2 \s_@@_stop ##3 - { ##3 { \use_none:n ##1 } } + { ##3 { ##1 } } + \cs_new:Npn \@@_trim_mark: {} } \@@_tmp:w { ~ } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro} % {\tl_sort:Nn, \tl_sort:cn, \tl_gsort:Nn, \tl_gsort:cn, \tl_sort:nN} % Implemented in \pkg{l3sort}. % \end{macro} % -% \subsection{Token by token changes} -% -% \begin{variable}{\q_@@_act_mark, \q_@@_act_stop} -% The \cs[no-index]{@@_act_\ldots{}} functions may be applied to any token list. -% Hence, we use two private quarks, to allow any token, even quarks, -% in the token list. -% Only \cs{q_@@_act_mark} and \cs{q_@@_act_stop} may not appear -% in the token lists manipulated by \cs{@@_act:NNNnn} functions. -% \begin{macrocode} -\quark_new:N \q_@@_act_mark -\quark_new:N \q_@@_act_stop -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[EXP]{\@@_act:NNNnn} -% \begin{macro}[EXP]{\@@_act_output:n, \@@_act_reverse_output:n} -% \begin{macro}[EXP]{\@@_act_loop:w} -% \begin{macro}[EXP]{\@@_act_normal:NwnNNN} -% \begin{macro}[EXP]{\@@_act_group:nwnNNN} -% \begin{macro}[EXP]{\@@_act_space:wwnNNN} -% \begin{macro}[EXP]{\@@_act_end:w} -% To help control the expansion, \cs{@@_act:NNNnn} should always -% be proceeded by \cs{exp:w} and ends by producing \cs{exp_end:} -% once the result has been obtained. Then loop over tokens, -% groups, and spaces in |#5|. The marker \cs{q_@@_act_mark} -% is used both to avoid losing outer braces and to detect the -% end of the token list more easily. The result is stored -% as an argument for the dummy function \cs{@@_act_result:n}. -% \begin{macrocode} -\cs_new:Npn \@@_act:NNNnn #1#2#3#4#5 - { - \group_align_safe_begin: - \@@_act_loop:w #5 \q_@@_act_mark \q_@@_act_stop - {#4} #1 #2 #3 - \@@_act_result:n { } - } -% \end{macrocode} -% In the loop, we check how the token list begins and act -% accordingly. In the \enquote{normal} case, we may have -% reached \cs{q_@@_act_mark}, the end of the list. Then -% leave \cs{exp_end:} and the result in the input stream, -% to terminate the expansion of \cs{exp:w}. -% Otherwise, apply the relevant function to the -% \enquote{arguments}, |#3| -% and to the head of the token list. Then repeat the loop. -% The scheme is the same if the token list starts with a -% group or with a space. Some extra work is needed to -% make \cs{@@_act_space:wwnNNN} gobble the space. -% \begin{macrocode} -\cs_new:Npn \@@_act_loop:w #1 \q_@@_act_stop - { - \tl_if_head_is_N_type:nTF {#1} - { \@@_act_normal:NwnNNN } - { - \tl_if_head_is_group:nTF {#1} - { \@@_act_group:nwnNNN } - { \@@_act_space:wwnNNN } - } - #1 \q_@@_act_stop - } -\cs_new:Npn \@@_act_normal:NwnNNN #1 #2 \q_@@_act_stop #3#4 - { - \if_meaning:w \q_@@_act_mark #1 - \exp_after:wN \@@_act_end:wn - \fi: - #4 {#3} #1 - \@@_act_loop:w #2 \q_@@_act_stop - {#3} #4 - } -\cs_new:Npn \@@_act_end:wn #1 \@@_act_result:n #2 - { \group_align_safe_end: \exp_end: #2 } -\cs_new:Npn \@@_act_group:nwnNNN #1 #2 \q_@@_act_stop #3#4#5 - { - #5 {#3} {#1} - \@@_act_loop:w #2 \q_@@_act_stop - {#3} #4 #5 - } -\exp_last_unbraced:NNo - \cs_new:Npn \@@_act_space:wwnNNN \c_space_tl #1 \q_@@_act_stop #2#3#4#5 - { - #5 {#2} - \@@_act_loop:w #1 \q_@@_act_stop - {#2} #3 #4 #5 - } -% \end{macrocode} -% Typically, the output is done to the right of what was already output, -% using \cs{@@_act_output:n}, but for the \cs{@@_act_reverse} functions, -% it should be done to the left. -% \begin{macrocode} -\cs_new:Npn \@@_act_output:n #1 #2 \@@_act_result:n #3 - { #2 \@@_act_result:n { #3 #1 } } -\cs_new:Npn \@@_act_reverse_output:n #1 #2 \@@_act_result:n #3 - { #2 \@@_act_result:n { #1 #3 } } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP]{\tl_reverse:n, \tl_reverse:o, \tl_reverse:V} -% \begin{macro}[EXP]{\@@_reverse_normal:nN} -% \begin{macro}[EXP]{\@@_reverse_group_preserve:nn} -% \begin{macro}[EXP]{\@@_reverse_space:n} -% The goal here is to reverse without losing spaces nor braces. -% This is done using the general internal function \cs{@@_act:NNNnn}. -% Spaces and \enquote{normal} tokens are output on the left of the current -% output. Grouped tokens are output to the left but without any reversal -% within the group. All of the internal functions here drop one argument: -% this is needed by \cs{@@_act:NNNnn} when changing case (to record -% which direction the change is in), but not when reversing the tokens. -% \begin{macrocode} -\cs_new:Npn \tl_reverse:n #1 - { - \__kernel_exp_not:w \exp_after:wN - { - \exp:w - \@@_act:NNNnn - \@@_reverse_normal:nN - \@@_reverse_group_preserve:nn - \@@_reverse_space:n - { } - {#1} - } - } -\cs_generate_variant:Nn \tl_reverse:n { o , V } -\cs_new:Npn \@@_reverse_normal:nN #1#2 - { \@@_act_reverse_output:n {#2} } -\cs_new:Npn \@@_reverse_group_preserve:nn #1#2 - { \@@_act_reverse_output:n { {#2} } } -\cs_new:Npn \@@_reverse_space:n #1 - { \@@_act_reverse_output:n { ~ } } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\tl_reverse:N, \tl_reverse:c, \tl_greverse:N, \tl_greverse:c} -% This reverses the list, leaving \cs{exp_stop_f:} in front, -% which stops the \texttt{f}-expansion. -% \begin{macrocode} -\cs_new_protected:Npn \tl_reverse:N #1 - { \tl_set:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } } -\cs_new_protected:Npn \tl_greverse:N #1 - { \tl_gset:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } } -\cs_generate_variant:Nn \tl_reverse:N { c } -\cs_generate_variant:Nn \tl_greverse:N { c } -% \end{macrocode} -% \end{macro} -% % \subsection{The first token from a token list} % % \begin{macro}{\tl_head:N, \tl_head:n, \tl_head:V, \tl_head:v, \tl_head:f} @@ -2895,32 +2817,49 @@ % empty result. The result is returned within the \tn{unexpanded} primitive. % The approach here is to use \cs{if_false:} to allow us to use |}| as % the closing delimiter: this is the only safe choice, as any other token -% would not be able to parse it's own code. Using a marker, we can see if -% what we are grabbing is exactly the marker, or there is anything else to -% deal with. Is there is, there is a loop. If not, tidy up and leave the -% item in the output stream. More detail in +% would not be able to parse it's own code. If the \tn{expanded} primitive +% is available it is used to get a fast and safe code variant in which we +% don't have to ensure that the left-most token is an internal to not break +% in an |f|-type expansion. If \tn{expanded} isn't available, using a marker, +% we can see if what we are grabbing is exactly the marker, or there is +% anything else to deal with. If there is, there is a loop. If not, tidy up +% and leave the item in the output stream. More detail in % \url{http://tex.stackexchange.com/a/70168}. % \begin{macrocode} -\cs_new:Npn \tl_head:n #1 - { - \__kernel_exp_not:w - \if_false: { \fi: \@@_head_auxi:nw #1 { } \s_@@_stop } - } -\cs_new:Npn \@@_head_auxi:nw #1#2 \s_@@_stop +\cs_if_exist:NTF \tex_expanded:D { - \exp_after:wN \@@_head_auxii:n \exp_after:wN { - \if_false: } \fi: {#1} + \cs_new:Npn \tl_head:n #1 + { + \__kernel_exp_not:w \tex_expanded:D + { { \if_false: { \fi: \@@_head_aux:n #1 { } } } } + } + \cs_new:Npn \@@_head_aux:n #1 + { + \__kernel_exp_not:w {#1} + \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: + } } -\cs_new:Npn \@@_head_auxii:n #1 { - \exp_after:wN \if_meaning:w \exp_after:wN \q_@@_nil - \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #1 } \q_@@_nil - \exp_after:wN \use_i:nn - \else: - \exp_after:wN \use_ii:nn - \fi: - {#1} - { \if_false: { \fi: \@@_head_auxi:nw #1 } } + \cs_new:Npn \tl_head:n #1 + { + \__kernel_exp_not:w + \if_false: { \fi: \@@_head_auxi:nw #1 { } \s_@@_stop } + } + \cs_new:Npn \@@_head_auxi:nw #1#2 \s_@@_stop + { + \exp_after:wN \@@_head_auxii:n \exp_after:wN { + \if_false: } \fi: {#1} + } + \exp_args:Nno \use:n + { \cs_new:Npn \@@_head_auxii:n #1 } + { + \@@_if_empty_if:o { \use_none:n #1 } + \exp_after:wN \use_ii:nnn + \fi: + \use_ii:nn + {#1} + { \if_false: { \fi: \@@_head_auxi:nw #1 } } + } } \cs_generate_variant:Nn \tl_head:n { V , v , f } \cs_new:Npn \tl_head:w #1#2 \q_stop {#1} @@ -2942,9 +2881,9 @@ % While we could optimise the test here, this would leave some tokens % \enquote{banned} in the input, which we do not have with this definition. % \begin{macrocode} -\cs_new:Npn \tl_tail:n #1 +\exp_args:Nno \use:n { \cs_new:Npn \tl_tail:n #1 } { - \__kernel_exp_not:w + \exp_after:wN \__kernel_exp_not:w \tl_if_blank:nTF {#1} { { } } { \exp_after:wN { \use_none:n #1 } } @@ -2961,6 +2900,9 @@ % \begin{macro}[pTF]{\tl_if_head_eq_charcode:nN} % \begin{macro}[pTF]{\tl_if_head_eq_charcode:fN} % \begin{macro}[pTF]{\tl_if_head_eq_catcode:nN} +% \begin{macro}[pTF]{\tl_if_head_eq_catcode:oN} +% \begin{macro}[EXP]{\@@_head_exp_not:w} +% \begin{macro}[EXP]{\@@_if_head_eq_empty_arg:w} % Accessing the first token of a token list is tricky in three cases: % when it has category code $1$ (begin-group token), when it is an % explicit space, with category code $10$ and character code $32$, or @@ -2980,21 +2922,17 @@ % cases, the first token is a character, and since we only care about % its character code, we can use \cs{str_head:n} to access it (this % works even if it is a space character). An empty argument -% results in \cs{tl_head:w} leaving two tokens: |?| which is taken in -% the \cs{if_charcode:w} test, and \cs{use_none:nn}, which ensures -% that \cs{prg_return_false:} is returned regardless of whether the -% charcode test was \texttt{true} or \texttt{false}. +% results in \cs{tl_head:w} leaving two token: |^| and +% \cs{@@_if_head_eq_empty_arg:w} which will result in the \cs{if_charcode:w} +% test being false and remove \cs{exp_not:N} and |#2|. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_eq_charcode:nN #1#2 { p , T , F , TF } { \if_charcode:w - \exp_not:N #2 \tl_if_head_is_N_type:nTF { #1 ? } - { - \exp_after:wN \exp_not:N - \@@_tl_head:w #1 { ? \use_none:nn } \s_@@_stop - } + { \@@_head_exp_not:w #1 { ^ \@@_if_head_eq_empty_arg:w } \s_@@_stop } { \str_head:n {#1} } + \exp_not:N #2 \prg_return_true: \else: \prg_return_false: @@ -3008,24 +2946,20 @@ % first token is a begin-group token or an explicit space token, and % produce the relevant token, either \cs{c_group_begin_token} or % \cs{c_space_token}. Again, for an empty argument, a hack is used, -% removing \cs{prg_return_true:} and \cs{else:} with \cs{use_none:nn} -% in case the catcode test with the (arbitrarily chosen) |?| is -% \texttt{true}. +% removing the token given by the user and leaving two tokens in the input +% stream which will make the \cs{if_catcode:w} test return \texttt{false}. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_eq_catcode:nN #1 #2 { p , T , F , TF } { \if_catcode:w - \exp_not:N #2 \tl_if_head_is_N_type:nTF { #1 ? } - { - \exp_after:wN \exp_not:N - \@@_tl_head:w #1 { ? \use_none:nn } \s_@@_stop - } + { \@@_head_exp_not:w #1 { ^ \@@_if_head_eq_empty_arg:w } \s_@@_stop } { \tl_if_head_is_group:nTF {#1} - { \c_group_begin_token } - { \c_space_token } + \c_group_begin_token + \c_space_token } + \exp_not:N #2 \prg_return_true: \else: \prg_return_false: @@ -3038,7 +2972,8 @@ % the normal case, use \cs{tl_head:w}, with no \cs{exp_not:N} this % time, since \cs{if_meaning:w} causes no expansion. With an empty % argument, the test is \texttt{true}, and \cs{use_none:nnn} removes -% |#2| and the usual \cs{prg_return_true:} and \cs{else:}. +% |#2| and \cs{prg_return_true:} and \cs{else:} (it is safe this way here as +% in this case \cs{prg_new_conditional:Npnn} didn't optimize these two away). % In the special cases, we know that the first token is a character, % hence \cs{if_charcode:w} and \cs{if_catcode:w} together are enough. % We combine them in some order, hopefully faster than the reverse. @@ -3048,8 +2983,8 @@ \prg_new_conditional:Npnn \tl_if_head_eq_meaning:nN #1#2 { p , T , F , TF } { \tl_if_head_is_N_type:nTF { #1 ? } - { \@@_if_head_eq_meaning_normal:nN } - { \@@_if_head_eq_meaning_special:nN } + \@@_if_head_eq_meaning_normal:nN + \@@_if_head_eq_meaning_special:nN {#1} #2 } \cs_new:Npn \@@_if_head_eq_meaning_normal:nN #1 #2 @@ -3064,11 +2999,11 @@ \cs_new:Npn \@@_if_head_eq_meaning_special:nN #1 #2 { \if_charcode:w \str_head:n {#1} \exp_not:N #2 - \exp_after:wN \use:n + \exp_after:wN \use_ii:nn \else: \prg_return_false: - \exp_after:wN \use_none:n \fi: + \use_none:n { \if_catcode:w \exp_not:N #2 \tl_if_head_is_group:nTF {#1} @@ -3081,48 +3016,79 @@ } } % \end{macrocode} +% +% Both \cs{tl_if_head_eq_charcode:nN} and \cs{tl_if_head_eq_catcode:nN} will +% need to get the first token of their argument and apply \cs{exp_not:N} to +% it. \cs{@@_head_exp_not:w} does exactly that. +% \begin{macrocode} +\cs_new:Npn \@@_head_exp_not:w #1 #2 \s_@@_stop + { \exp_not:N #1 } +% \end{macrocode} +% +% If the argument of \cs{tl_if_head_eq_charcode:nN} and +% \cs{tl_if_head_eq_catcode:nN} was empty \cs{@@_if_head_eq_empty_arg:w} will +% be left in the input stream. This macro has to remove \cs{exp_not:N} and +% the following token from the input stream to make sure no unbalanced +% if-construct is created and leave tokens there which make the two tests +% return \texttt{false}. +% \begin{macrocode} +\cs_new:Npn \@@_if_head_eq_empty_arg:w \exp_not:N #1 + { ? } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\tl_if_head_is_N_type:n} -% \begin{macro}[EXP]{\@@_if_head_is_N_type:w} +% \begin{macro}[EXP] +% { +% \@@_if_head_is_N_type_auxi:w , +% \@@_if_head_is_N_type_auxii:nn , +% \@@_if_head_is_N_type_auxiii:n +% } % A token list can be empty, can start with an explicit space % character (catcode 10 and charcode 32), can start with a begin-group % token (catcode 1), or start with an \texttt{N}-type argument. In -% the first two cases, the line involving \cs{@@_if_head_is_N_type:w} -% produces~|^| (and otherwise nothing). In the third case -% (begin-group token), the lines involving \cs{exp_after:wN} produce a +% the first two cases, the line involving \cs{@@_if_head_is_N_type_auxi:w} +% produces~|f| (and otherwise nothing). In the third case +% (begin-group token), the lines involving \cs{token_to_str:N} produce a % single closing brace. The category code test is thus true exactly % in the fourth case, which is what we want. One cannot optimize by -% moving one of the |*| to the beginning: if |#1| contains primitive -% conditionals, all of its occurrences must be dealt with before the -% \cs{if_catcode:w} tries to skip the \texttt{true} branch of the +% moving one of the \cs{scan_stop:} to the beginning: if |#1| contains +% primitive conditionals, all of its occurrences must be dealt with before +% the \cs{if:w} tries to skip the \texttt{true} branch of the % conditional. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_is_N_type:n #1 { p , T , F , TF } { - \if_catcode:w - \if_false: { \fi: \@@_if_head_is_N_type:w ? #1 ~ } - \exp_after:wN \use_none:n - \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } } - * * + \if:w + \if_false: { \fi: \@@_if_head_is_N_type_auxi:w \prg_do_nothing: #1 ~ } + { \exp_after:wN { \token_to_str:N #1 } } + \scan_stop: \scan_stop: \prg_return_true: \else: \prg_return_false: \fi: } -\cs_new:Npn \@@_if_head_is_N_type:w #1 ~ +\exp_args:Nno \use:n { \cs_new:Npn \@@_if_head_is_N_type_auxi:w #1 ~ } { - \tl_if_empty:oTF { \use_none:n #1 } { ^ } { } - \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: + \tl_if_empty:oTF { #1 } + { f \exp_after:wN \use_none:nn } + { \exp_after:wN \@@_if_head_is_N_type_auxii:n } + \exp_after:wN { \if_false: } \fi: } +\cs_new:Npn \@@_if_head_is_N_type_auxii:n #1 + { \exp_after:wN \use_none:n \exp_after:wN } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP,pTF]{\tl_if_head_is_group:n} +% \begin{macro}[EXP]{\@@_if_head_is_group_fi_false:w} % Pass the first token of |#1| through \cs{token_to_str:N}, then check % for the brace balance. The extra \texttt{?} caters for an empty % argument. This could be made faster, but we need all brace tricks @@ -3131,47 +3097,236 @@ % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_is_group:n #1 { p , T , F , TF } { - \if_catcode:w + \if:w \exp_after:wN \use_none:n \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } } - * * - \prg_return_false: - \else: + \scan_stop: \scan_stop: + \@@_if_head_is_group_fi_false:w + \fi: + \if_true: \prg_return_true: + \else: + \prg_return_false: \fi: } +\cs_new:Npn \@@_if_head_is_group_fi_false:w \fi: \if_true: { \fi: \if_false: } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}[EXP,pTF]{\tl_if_head_is_space:n} % \begin{macro}[EXP]{\@@_if_head_is_space:w} % The auxiliary's argument is all that is before the first explicit -% space in |?#1?~|. If that is a single~|?| the test yields -% \texttt{true}. Otherwise, that is more than one token, and the test -% yields \texttt{false}. The work is done within braces (with an +% space in |\prg_do_nothing:#1?~|. If that is a single~|\prg_do_nothing:| the +% test yields \texttt{true}. Otherwise, that is more than one token, and the +% test yields \texttt{false}. The work is done within braces (with an % |\if_false: { \fi: ... }| construction) both to hide potential % alignment tab characters from \TeX{} in a table, and to allow for -% removing what remains of the token list after its first space. The -% \cs{exp:w} and \cs{exp_end:} ensure that the result of a -% single step of expansion directly yields a balanced token list (no -% trailing closing brace). +% removing what remains of the token list after its first space. The use of +% \cs{if:w} ensures that the result of a single step of expansion directly +% yields a balanced token list (no trailing closing brace). % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_is_space:n #1 { p , T , F , TF } { - \exp:w \if_false: { \fi: - \@@_if_head_is_space:w ? #1 ? ~ } + \if:w + \if_false: { \fi: \@@_if_head_is_space:w \prg_do_nothing: #1 ? ~ } + \scan_stop: \scan_stop: + \prg_return_true: + \else: + \prg_return_false: + \fi: } -\cs_new:Npn \@@_if_head_is_space:w #1 ~ +\exp_args:Nno \use:n { \cs_new:Npn \@@_if_head_is_space:w #1 ~ } { - \tl_if_empty:oTF { \use_none:n #1 } - { \exp_after:wN \exp_end: \exp_after:wN \prg_return_true: } - { \exp_after:wN \exp_end: \exp_after:wN \prg_return_false: } + \@@_if_empty_if:o {#1} \else: f \fi: \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: } % \end{macrocode} % \end{macro} % \end{macro} % +% \subsection{Token by token changes} +% +% \begin{variable}{\s_@@_act_stop} +% The \cs[no-index]{@@_act_\ldots{}} functions may be applied to any token list. +% Hence, we use a private quark, to allow any token, even quarks, +% in the token list. +% Only \cs{s_@@_act_stop} may not appear in the token lists manipulated by +% \cs{@@_act:NNNn} functions. +% \begin{macrocode} +\scan_new:N \s_@@_act_stop +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[EXP]{\@@_act:NNNn} +% \begin{macro}[EXP]{\@@_act_output:n, \@@_act_reverse_output:n} +% \begin{macro}[EXP]{\@@_act_loop:w} +% \begin{macro}[EXP]{\@@_act_normal:NwNNN} +% \begin{macro}[EXP]{\@@_act_group:nwNNN} +% \begin{macro}[EXP]{\@@_act_space:wwNNN} +% \begin{macro}[EXP]{\@@_act_end:w} +% \begin{macro}[EXP] +% { +% \@@_act_if_head_is_space:nTF, +% \@@_act_if_head_is_space:w, +% \@@_act_if_head_is_space_true:w +% } +% \begin{macro}[EXP]{\@@_use_none_delimit_by_q_act_stop:w} +% To help control the expansion, \cs{@@_act:NNNn} should always +% be preceeded by \cs{exp:w} and ends by producing \cs{exp_end:} +% once the result has been obtained. This way no internal token of it can be +% accidentally end up in the input stream. +% Because \cs{s_@@_act_stop} can't appear without braces around it in the +% argument~|#1| of \cs{@@_act_loop:w}, we can use this marker to set up a fast +% test for leading spaces. +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_act_if_head_is_space:nTF ##1 + { + \@@_act_if_head_is_space:w + \s_@@_act_stop ##1 \s_@@_act_stop \@@_act_if_head_is_space_true:w + \s_@@_act_stop #1 \s_@@_act_stop \use_ii:nn + } + \cs_new:Npn \@@_act_if_head_is_space:w + ##1 \s_@@_act_stop #1 ##2 \s_@@_act_stop + {} + \cs_new:Npn \@@_act_if_head_is_space_true:w + \s_@@_act_stop #1 \s_@@_act_stop \use_ii:nn ##1 ##2 + {##1} + } +\@@_tmp:w { ~ } +% \end{macrocode} +% (We expand the definition \cs{@@_act_if_head_is_space:nTF} when +% setting up \cs{@@_act_loop:w}, so we can then undefine the auxiliary.) +% In the loop, we check how the token list begins and act +% accordingly. In the \enquote{group} case, we may have +% reached \cs{s_@@_act_stop}, the end of the list. Then +% leave \cs{exp_end:} and the result in the input stream, +% to terminate the expansion of \cs{exp:w}. +% Otherwise, apply the relevant function to the +% \enquote{arguments}, |#3| +% and to the head of the token list. Then repeat the loop. +% The scheme is the same if the token list starts with an |N|-type +% or with a space, making sure that +% \cs{@@_act_space:wwNNN} gobbles the space. +% \begin{macrocode} +\exp_args:Nnx \use:n { \cs_new:Npn \@@_act_loop:w #1 \s_@@_act_stop } + { + \exp_not:o { \@@_act_if_head_is_space:nTF {#1} } + \exp_not:N \@@_act_space:wwNNN + { + \exp_not:o { \tl_if_head_is_group:nTF {#1} } + \exp_not:N \@@_act_group:nwNNN + \exp_not:N \@@_act_normal:NwNNN + } + \exp_not:n {#1} \s_@@_act_stop + } +\cs_undefine:N \@@_act_if_head_is_space:nTF +\cs_new:Npn \@@_act_normal:NwNNN #1 #2 \s_@@_act_stop #3 + { + #3 #1 + \@@_act_loop:w #2 \s_@@_act_stop + #3 + } +\cs_new:Npn \@@_use_none_delimit_by_s_act_stop:w #1 \s_@@_act_stop { } +\cs_new:Npn \@@_act_end:wn #1 \@@_act_result:n #2 + { \group_align_safe_end: \exp_end: #2 } +\cs_new:Npn \@@_act_group:nwNNN #1 #2 \s_@@_act_stop #3#4#5 + { + \@@_use_none_delimit_by_s_act_stop:w #1 \@@_act_end:wn \s_@@_act_stop + #5 {#1} + \@@_act_loop:w #2 \s_@@_act_stop + #3 #4 #5 + } +\exp_last_unbraced:NNo + \cs_new:Npn \@@_act_space:wwNNN \c_space_tl #1 \s_@@_act_stop #2#3 + { + #3 + \@@_act_loop:w #1 \s_@@_act_stop + #2 #3 + } +% \end{macrocode} +% \cs{@@_act:NNNn} loops over tokens, groups, and spaces in |#4|. +% |{\s_@@_act_stop}| serves as the end of token list marker, the |?| after it +% avoids losing outer braces. The result is stored as an argument for the +% dummy function \cs{@@_act_result:n}. +% \begin{macrocode} +\cs_new:Npn \@@_act:NNNn #1#2#3#4 + { + \group_align_safe_begin: + \@@_act_loop:w #4 { \s_@@_act_stop } ? \s_@@_act_stop + #1 #3 #2 + \@@_act_result:n { } + } +% \end{macrocode} +% Typically, the output is done to the right of what was already output, +% using \cs{@@_act_output:n}, but for the \cs{@@_act_reverse} functions, +% it should be done to the left. +% \begin{macrocode} +\cs_new:Npn \@@_act_output:n #1 #2 \@@_act_result:n #3 + { #2 \@@_act_result:n { #3 #1 } } +\cs_new:Npn \@@_act_reverse_output:n #1 #2 \@@_act_result:n #3 + { #2 \@@_act_result:n { #1 #3 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\tl_reverse:n, \tl_reverse:o, \tl_reverse:V} +% \begin{macro}[EXP]{\@@_reverse_normal:nN} +% \begin{macro}[EXP]{\@@_reverse_group_preserve:nn} +% \begin{macro}[EXP]{\@@_reverse_space:n} +% The goal here is to reverse without losing spaces nor braces. +% This is done using the general internal function \cs{@@_act:NNNn}. +% Spaces and \enquote{normal} tokens are output on the left of the current +% output. Grouped tokens are output to the left but without any reversal +% within the group. +% \begin{macrocode} +\cs_new:Npn \tl_reverse:n #1 + { + \__kernel_exp_not:w \exp_after:wN + { + \exp:w + \@@_act:NNNn + \@@_reverse_normal:N + \@@_reverse_group_preserve:n + \@@_reverse_space: + {#1} + } + } +\cs_generate_variant:Nn \tl_reverse:n { o , V } +\cs_new:Npn \@@_reverse_normal:N + { \@@_act_reverse_output:n } +\cs_new:Npn \@@_reverse_group_preserve:n #1 + { \@@_act_reverse_output:n { {#1} } } +\cs_new:Npn \@@_reverse_space: + { \@@_act_reverse_output:n { ~ } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_reverse:N, \tl_reverse:c, \tl_greverse:N, \tl_greverse:c} +% This reverses the list, leaving \cs{exp_stop_f:} in front, +% which stops the \texttt{f}-expansion. +% \begin{macrocode} +\cs_new_protected:Npn \tl_reverse:N #1 + { \__kernel_tl_set:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } } +\cs_new_protected:Npn \tl_greverse:N #1 + { \__kernel_tl_gset:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } } +\cs_generate_variant:Nn \tl_reverse:N { c } +\cs_generate_variant:Nn \tl_greverse:N { c } +% \end{macrocode} +% \end{macro} +% % \subsection{Using a single item} % % \begin{macro}{\tl_item:nn, \tl_item:Nn, \tl_item:cn} @@ -3373,7 +3528,10 @@ \cs_new_protected:Npn \@@_show:NN #1#2 { \__kernel_chk_defined:NT #2 - { \exp_args:Nx #1 { \token_to_str:N #2 = \exp_not:o {#2} } } + { + \exp_args:Ne #1 + { \token_to_str:N #2 = \__kernel_exp_not:w \exp_after:wN {#2} } + } } % \end{macrocode} % \end{macro} @@ -3459,6 +3617,12 @@ % \end{macrocode} % \end{variable} % +% We finally clean up a temporary control sequence that we have used at +% various points to set up some definitions. +% \begin{macrocode} +\cs_undefine:N \@@_tmp:w +% \end{macrocode} +% % \begin{macrocode} %</package> % \end{macrocode} |