summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3tl.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3tl.dtx860
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}