diff options
author | Karl Berry <karl@freefriends.org> | 2020-05-11 20:47:47 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2020-05-11 20:47:47 +0000 |
commit | bffbeb435746ef4bfc3abbbce80006f657360f3a (patch) | |
tree | 25bd4caaead6a06571d1376a8d442fe79e071d62 /Master/texmf-dist/source/latex/l3kernel/l3quark.dtx | |
parent | a3fca75048862f5c0f86ec89c88da49a55beafb7 (diff) |
l3kernel (11may20)
git-svn-id: svn://tug.org/texlive/trunk@55101 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3quark.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3quark.dtx | 399 |
1 files changed, 388 insertions, 11 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx b/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx index a6c66f1561e..f513fcb026a 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-05-05} +% \date{Released 2020-05-11} % % \maketitle % @@ -399,9 +399,25 @@ % \end{macrocode} % \end{variable} % -% \begin{macro}{\quark_if_recursion_tail_stop:N} +% \begin{variable}{\s_@@} +% Private scan mark used in \pkg{l3quark}. We don't have \pkg{l3scan} +% yet, so we declare the scan mark here and add it to the scan mark pool +% later. +% \begin{macrocode} +\cs_new_eq:NN \s_@@ \scan_stop: +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\q_@@_nil} +% Private quark use for some tests. +% \begin{macrocode} +\quark_new:N \q_@@_nil +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[EXP]{\quark_if_recursion_tail_stop:N} % \UnitTested -% \begin{macro}{\quark_if_recursion_tail_stop_do:Nn} +% \begin{macro}[EXP]{\quark_if_recursion_tail_stop_do:Nn} % \UnitTested % When doing recursions, it is easy to spend a lot of time testing if the % end marker has been found. To avoid this, a dedicated end marker is used @@ -429,13 +445,13 @@ % \end{macro} % \end{macro} % -% \begin{macro} +% \begin{macro}[EXP] % {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o} % \UnitTested -% \begin{macro} +% \begin{macro}[EXP] % {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on} % \UnitTested -% \begin{macro}{\@@_if_recursion_tail:w} +% \begin{macro}[EXP]{\@@_if_recursion_tail:w} % See \cs{quark_if_nil:nTF} for the details. Expanding % \cs{@@_if_recursion_tail:w} once in front of the tokens chosen here % gives an empty result if and only if |#1|~is exactly @@ -464,8 +480,8 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\quark_if_recursion_tail_break:NN} -% \begin{macro}{\quark_if_recursion_tail_break:nN} +% \begin{macro}[EXP]{\quark_if_recursion_tail_break:NN} +% \begin{macro}[EXP]{\quark_if_recursion_tail_break:nN} % Analogues of the \cs[index=quark_if_recursion_tail_stop:n] % {quark_if_recursion_tail_stop\ldots{}} functions. % Break the mapping using |#2|. @@ -574,6 +590,353 @@ % \end{macro} % \end{macro} % +% \begin{macro}{\__kernel_quark_new_test:N} +% The function \cs{__kernel_quark_new_test:N} defines |#1| in a +% similar way as \cs[no-index]{quark_if_recursion_tail_...} functions +% (as described below), using +% \cs[no-index]{q__\meta{namespace}_recursion_tail} as the test quark +% and \cs[no-index]{q__\meta{namespace}_recursion_stop} as the +% delimiter quark, where the \meta{namespace} is determined as the +% first |_|-delimited part in~|#1|. +% +% There are six possible function types which this function can define, +% and which is defined depends on the signature of the function being +% defined: +% \begin{description} +% \def\makelabel#1{\texttt{:#1}~} +% \item[n] gives an analogue of \cs{quark_if_recursion_tail_stop:n} +% \item[nn] gives an analogue of \cs{quark_if_recursion_tail_stop_do:nn} +% \item[nN] gives an analogue of \cs{quark_if_recursion_tail_break:nN} +% \item[N] gives an analogue of \cs{quark_if_recursion_tail_stop:N} +% \item[Nn] gives an analogue of \cs{quark_if_recursion_tail_stop_do:Nn} +% \item[NN] gives an analogue of \cs{quark_if_recursion_tail_break:NN} +% \end{description} +% Any other signature causes an error, as does a function without signature. +% +% \begin{macro}{\__kernel_quark_new_conditional:Nn} +% Similar to \cs{__kernel_quark_new_test:N}, but defines quark +% branching conditionals like \cs{quark_if_nil:nTF} that test for the +% quark \cs[no-index]{q__\meta{namespace}_\meta{name}}. +% The \meta{namespace} and \meta{name} are determined from the +% conditional~|#1|, which must take the rather rigid form +% \cs[no-index]{__\meta{namespace}_quark_if_\meta{name}:\meta{arg spec}}. +% There are only two cases for the \meta{arg spec} here: +% \begin{description} +% \def\makelabel#1{\texttt{:#1}~} +% \item[n] gives an analogue of \cs[no-index]{quark_if_nil:n(TF)} +% \item[N] gives an analogue of \cs[no-index]{quark_if_nil:N(TF)} +% \end{description} +% Any other signature causes an error, as does a function without signature. +% We use low-level emptiness tests as \pkg{l3tl} is not available yet when these +% functions are used; thankfully we only care about whether strings +% are empty so a simple \cs{if_meaning:w} \cs{q_nil} \meta{string} +% \cs{q_nil} suffices. +% +% \begin{macro}{\@@_new_test:NNNn, \@@_new_test:Nccn, +% \@@_new_test_aux:nnNNnnnn} +% \begin{macro}{\@@_new_conditional:Nnnn, \@@_new_conditional:Nxxn} +% \begin{macrocode} +\cs_new_protected:Npn \__kernel_quark_new_test:N #1 + { \@@_new_test_aux:Nx #1 { \@@_module_name:N #1 } } +\cs_new_protected:Npn \@@_new_test_aux:Nn #1 #2 + { + \if_meaning:w \q_nil #2 \q_nil + \__kernel_msg_error:nnx { kernel } { invalid-quark-function } + { \token_to_str:N #1 } + \else: + \@@_new_test:Nccn #1 + { q__#2_recursion_tail } { q__#2_recursion_stop } { __#2 } + \fi: + } +\cs_generate_variant:Nn \@@_new_test_aux:Nn { Nx } +\cs_new_protected:Npn \@@_new_test:NNNn #1 + { + \exp_last_unbraced:Nf \@@_new_test_aux:nnNNnnnn + { \cs_split_function:N #1 } + #1 { test } + } +\cs_generate_variant:Nn \@@_new_test:NNNn { Ncc } +\cs_new_protected:Npn \__kernel_quark_new_conditional:Nn #1 + { + \@@_new_conditional:Nxxn #1 + { \@@_quark_conditional_name:N #1 } + { \@@_module_name:N #1 } + } +\cs_new_protected:Npn \@@_new_conditional:Nnnn #1#2#3#4 + { + \if_meaning:w \q_nil #2 \q_nil + \__kernel_msg_error:nnx { kernel } { invalid-quark-function } + { \token_to_str:N #1 } + \else: + \if_meaning:w \q_nil #3 \q_nil + \__kernel_msg_error:nnx { kernel } { invalid-quark-function } + { \token_to_str:N #1 } + \else: + \exp_last_unbraced:Nf \@@_new_test_aux:nnNNnnnn + { \cs_split_function:N #1 } + #1 { conditional } + {#2} {#3} {#4} + \fi: + \fi: + } +\cs_generate_variant:Nn \@@_new_conditional:Nnnn { Nxx } +\cs_new_protected:Npn \@@_new_test_aux:nnNNnnnn #1 #2 #3 #4 #5 + { + \cs_if_exist_use:cTF { @@_new_#5_#2:Nnnn } { #4 } + { + \__kernel_msg_error:nnxx { kernel } { invalid-quark-function } + { \token_to_str:N #4 } {#2} + \use_none:nnn + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{ +% \@@_new_test_n:Nnnn, \@@_new_test_nn:Nnnn, +% \@@_new_test_N:Nnnn, \@@_new_test_Nn:Nnnn, +% \@@_new_test_NN:Nnnn, \@@_new_test_NN:Nnnn, +% } +% These macros implement the six possibilities mentioned above, passing +% the right arguments to \cs{@@_new_test_aux_do:nNNnnnnNNn}, +% which defines some auxiliaries, and then to +% \cs{@@_new_test_define_tl:nNnNNn} (|:n(n)| variants) or to +% \cs{@@_new_test_define_ifx:nNnNNn} (|:N(n)|) which define the +% main conditionals. +% \begin{macrocode} +\cs_new_protected:Npn \@@_new_test_n:Nnnn #1 #2 #3 #4 + { + \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { none } { } { } { } + \@@_new_test_define_tl:nNnNNn #1 { } + } +\cs_new_protected:Npn \@@_new_test_nn:Nnnn #1 #2 #3 #4 + { + \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2} + \@@_new_test_define_tl:nNnNNn #1 { \use_none:n } + } +\cs_new_protected:Npn \@@_new_test_nN:Nnnn #1 #2 #3 #4 + { + \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2} + \@@_new_test_define_break_tl:nNNNNn #1 { } + } +\cs_new_protected:Npn \@@_new_test_N:Nnnn #1 #2 #3 #4 + { + \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { none } { } { } { } + \@@_new_test_define_ifx:nNnNNn #1 { } + } +\cs_new_protected:Npn \@@_new_test_Nn:Nnnn #1 #2 #3 #4 + { + \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2} + \@@_new_test_define_ifx:nNnNNn #1 + { \else: \exp_after:wN \use_none:n } + } +\cs_new_protected:Npn \@@_new_test_NN:Nnnn #1 #2 #3 #4 + { + \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2} + \@@_new_test_define_break_ifx:nNNNNn #1 { } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ +% \@@_new_test_aux_do:nNNnnnnNNn, +% \@@_test_define_aux:NNNNnnNNn +% } +% \cs{@@_new_test_aux_do:nNNnnnnNNn} makes the control sequence names +% which will be used by \cs{@@_test_define_aux:NNNNnnNNn}, and then later +% by \cs{@@_new_test_define_tl:nNnNNn} or +% \cs{@@_new_test_define_ifx:nNnNNn}. The control sequences defined +% here are analogous to \cs{@@_if_recursion_tail:w} and to +% \cs[no-index]{use_(none|i)_delimit_by_q_recursion_stop:(|n)w}. +% +% The name is composed by the name-space and the name of the quarks. +% Suppose \cs{__kernel_quark_new_test:N} was used with: +% \begin{verbatim} +% \__kernel_quark_new_test:N \__test_quark_tail:n +% \end{verbatim} +% then the first auxiliary will be \cs[no-index]{__test_quark_recursion_tail:w}, +% and the second one will be +% \cs[no-index]{__test_use_none_delimit_by_q_recursion_stop:w}. +% +% Note that the actual quarks are \emph{not} defined here. They should +% be defined separately using \cs{quark_new:N}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_new_test_aux_do:nNNnnnnNNn #1 #2 #3 #4 #5 + { + \exp_args:Ncc \@@_test_define_aux:NNNNnnNNn + { #1 _quark_recursion_tail:w } + { #1 _use_ #4 _delimit_by_q_recursion_stop: #5 w } + #2 #3 + } +\cs_new_protected:Npn \@@_test_define_aux:NNNNnnNNn #1 #2 #3 #4 #5 #6 #7 + { + \cs_gset:Npn #1 ##1 #3 ##2 ? ##3 ?! { ##1 ##2 } + \cs_gset:Npn #2 ##1 #6 #4 {#5} + #7 {##1} #1 #2 #3 + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ +% \@@_new_test_define_tl:nNnNNn, +% \@@_new_test_define_ifx:nNnNNn +% } +% \begin{macro}{ +% \@@_new_test_define_break_tl:nNNNNn, +% \@@_new_test_define_break_ifx:nNNNNn +% } +% Finally, these two macros define the main conditional function using +% what's been set up before. +% \begin{macrocode} +\cs_new_protected:Npn \@@_new_test_define_tl:nNnNNn #1 #2 #3 #4 #5 #6 + { + \cs_new:Npn #5 #1 + { + \tl_if_empty:oTF + { #2 {} ##1 {} ?! #4 ??! } + {#3} {#6} + } + } +\cs_new_protected:Npn \@@_new_test_define_ifx:nNnNNn #1 #2 #3 #4 #5 #6 + { + \cs_new:Npn #5 #1 + { + \if_meaning:w #4 ##1 + \exp_after:wN #3 + #6 + \fi: + } + } +\cs_new_protected:Npn \@@_new_test_define_break_tl:nNNNNn #1 #2 #3 + { \@@_new_test_define_tl:nNnNNn {##1##2} #2 {##2} } +\cs_new_protected:Npn \@@_new_test_define_break_ifx:nNNNNn #1 #2 #3 + { \@@_new_test_define_ifx:nNnNNn {##1##2} #2 {##2} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{ +% \@@_new_conditional_n:Nnnn, +% \@@_new_conditional_N:Nnnn, +% } +% These macros implement the two possibilities for branching quark +% conditionals, passing +% the right arguments to \cs{@@_new_conditional_aux_do:NNnnn}, +% which defines some auxiliaries and defines the +% main conditionals. +% \begin{macrocode} +\cs_new_protected:Npn \@@_new_conditional_n:Nnnn + { \@@_new_conditional_aux_do:NNnnn \use_i:nn } +\cs_new_protected:Npn \@@_new_conditional_N:Nnnn + { \@@_new_conditional_aux_do:NNnnn \use_ii:nn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ +% \@@_new_conditional_aux_do:NNnnn, +% \@@_new_conditional_define:NNNNn, +% } +% Similar to the previous macros, but branching conditionals only +% require one auxiliary, so we take a shortcut. In +% \cs{@@_new_conditional_define:NNNNn}, |#4| is \cs{use_i:nn} to +% define the \texttt{n}-type function (which needs an auxiliary) and +% is \cs{use_ii:nn} to define the \texttt{N}-type function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_new_conditional_aux_do:NNnnn #1 #2 #3 #4 + { + \exp_args:Ncc \@@_new_conditional_define:NNNNn + { __ #4 _if_quark_ #3 :w } { q__ #4 _ #3 } #2 #1 + } +\cs_new_protected:Npn \@@_new_conditional_define:NNNNn #1 #2 #3 #4 #5 + { + #4 { \cs_gset:Npn #1 ##1 #2 ##2 ? ##3 ?! { ##1 ##2 } } { } + \exp_args:Nno \use:n { \prg_new_conditional:Npnn #3 ##1 {#5} } + { + #4 { \@@_if_empty_if:o { #1 {} ##1 {} ?! #2 ??! } } + { \if_meaning:w #2 ##1 } + \prg_return_true: \else: \prg_return_false: \fi: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_module_name:N} +% \begin{macro}[EXP]{ +% \@@_module_name:w, +% \@@_module_name_loop:w, +% \@@_module_name_end:w +% } +% \cs{@@_module_name:N} takes a control sequence and returns its +% \meta{module} name, determined as the first non-empty +% non-single-character word, separated by |_| or~|:|. These rules +% give the correct result for public functions +% \cs[no-index]{\meta{module}_\ldots{}}, private functions +% \cs[no-index]{__\meta{module}_\ldots{}}, and variables such as +% \cs[no-index]{l_\meta{module}_\ldots{}}. If no valid module is +% found the result is an empty string. The approach is to first cut +% off everything after the (first) |:| if any is present, then +% repeatedly grab |_|-delimited words until finding one of length at +% least~$2$ (we use low-level tests as \pkg{l3tl} is not fully +% available when \cs{__kernel_quark_new_test:N} is first used. +% If no \meta{module} is found (such as in \cs{::n}) we +% get the trailing marker \cs{use_none:n} |{}|, which expands to +% nothing. +% \begin{macrocode} +\cs_set:Npn \@@_tmp:w #1#2 + { + \cs_new:Npn \@@_module_name:N ##1 + { + \exp_last_unbraced:Nf \@@_module_name:w + { \cs_to_str:N ##1 } #1 \s_@@ + } + \cs_new:Npn \@@_module_name:w ##1 #1 ##2 \s_@@ + { \@@_module_name_loop:w ##1 #2 \use_none:n { } #2 \s_@@ } + \cs_new:Npn \@@_module_name_loop:w ##1 #2 + { + \use_i_ii:nnn \if_meaning:w \prg_do_nothing: + ##1 \prg_do_nothing: \prg_do_nothing: + \exp_after:wN \@@_module_name_loop:w + \else: + \@@_module_name_end:w ##1 + \fi: + } + \cs_new:Npn \@@_module_name_end:w + ##1 \fi: ##2 \s_@@ { \fi: ##1 } + } +\exp_after:wN \@@_tmp:w \tl_to_str:n { : _ } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_quark_conditional_name:N, \@@_quark_conditional_name:w} +% \cs{@@_quark_conditional_name:N} determines the quark name that the quark +% conditional function~|##1| queries, as the part of the function name +% between |_quark_if_| and the trailing~|:|. Again we define it +% through \cs{@@_tmp:w}, which receives |:| as |#1| and |_quark_if_| +% as |#2|. The auxiliary \cs{@@_quark_conditional_name:w} returns the part +% between the first |_quark_if_| and the next~|:|, and we apply this +% auxiliary to the function name followed by |:| (in case the function +% name is lacking a signature), and |_quark_if_:| so that +% \cs{@@_quark_conditional_name:N} returns an empty string if |_quark_if_| is +% not present. +% \begin{macrocode} +\cs_set:Npn \@@_tmp:w #1 #2 \s_@@ + { + \cs_new:Npn \@@_quark_conditional_name:N ##1 + { + \exp_last_unbraced:Nf \@@_quark_conditional_name:w + { \cs_to_str:N ##1 } #1 #2 #1 \s_@@ + } + \cs_new:Npn \@@_quark_conditional_name:w + ##1 #2 ##2 #1 ##3 \s_@@ {##2} + } +\exp_after:wN \@@_tmp:w \tl_to_str:n { : _quark_if_ } \s_@@ +% \end{macrocode} +% \end{macro} +% % \subsection{Scan marks} % % \begin{macrocode} @@ -583,8 +946,9 @@ % \begin{variable}{\g_@@_marks_tl} % \UnitTested % The list of all scan marks currently declared. +% No \pkg{l3tl} yet, so define this by hand. % \begin{macrocode} -\tl_new:N \g_@@_marks_tl +\cs_gset:Npn \g_@@_marks_tl { } % \end{macrocode} % \end{variable} % @@ -612,12 +976,25 @@ % \UnitTested % We only declare one scan mark here, more can be defined % by specific modules. +% Can't use \cs{scan_new:N} yet because \pkg{l3tl} isn't loaded, +% so define \cs{s_stop} by hand and add it to \cs{g_@@_marks_tl}. +% We also add \cs{s__quark} (declared earlier) to the pool here. +% Since it lives in a different namespace, a little \pkg{l3docstrip} +% cheating is necessary. % \begin{macrocode} -\scan_new:N \s_stop +\cs_new_eq:NN \s_stop \scan_stop: +\cs_gset_nopar:Npx \g_@@_marks_tl + { + \exp_not:o \g_@@_marks_tl + \s_stop +%<@@=quark> + \s_@@ +%<@@=scan> + } % \end{macrocode} % \end{variable} % -% \begin{macro}{\use_none_delimit_by_s_stop:w} +% \begin{macro}[EXP]{\use_none_delimit_by_s_stop:w} % \UnitTested % Similar to \cs{use_none_delimit_by_q_stop:w}. % \begin{macrocode} |