summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3quark.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3quark.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3quark.dtx399
1 files changed, 388 insertions, 11 deletions
diff --git a/macros/latex/contrib/l3kernel/l3quark.dtx b/macros/latex/contrib/l3kernel/l3quark.dtx
index a6c66f1561..f513fcb026 100644
--- a/macros/latex/contrib/l3kernel/l3quark.dtx
+++ b/macros/latex/contrib/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}