% \iffalse meta-comment % %% File: l3quark.dtx % % Copyright (C) 1990-2023 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "l3kernel bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \documentclass[full,kernel]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3quark} package\\ Quarks^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2023-02-07} % % \maketitle % % \begin{documentation} % % Two special types of constants in \LaTeX3 are \enquote{quarks} and % \enquote{scan marks}. By convention all constants of type quark % start out with |\q_|, and scan marks start with |\s_|. % % \section{Quarks} % % Quarks are control sequences (and in fact, token lists) that expand % to themselves and should % therefore \emph{never} be executed directly in the code. % This would result in an endless loop! % % They are meant to be used as delimiter in weird functions, the most % common use case being the `stop token' (\emph{i.e.}~\cs{q_stop}). % For example, when writing a macro to parse a user-defined date % \begin{verbatim} % \date_parse:n {19/June/1981} % \end{verbatim} % one might write a command such as % \begin{verbatim} % \cs_new:Npn \date_parse:n #1 { \date_parse_aux:w #1 \q_stop } % \cs_new:Npn \date_parse_aux:w #1 / #2 / #3 \q_stop % { } % \end{verbatim} % % Quarks are sometimes also used as error return values for functions % that receive erroneous input. % For example, in the function \cs{prop_get:NnN} to retrieve a value stored % in some key of a property list, if the key does not exist then the return value % is the quark \cs{q_no_value}. % As mentioned above, such quarks are extremely fragile and it is imperative % when using such functions that code is carefully written to check for % pathological cases to avoid leakage of a quark into an uncontrolled % environment. % % Quarks also permit the % following ingenious trick when parsing tokens: % when you pick up a token in a temporary variable % and you want to know whether you have picked up a particular quark, % all you have to do is compare the temporary variable to the quark using % \cs{tl_if_eq:NNTF}. A set of special quark testing functions is set up % below. All the quark testing functions are expandable although the % ones testing only single tokens are much faster. % % \section{Defining quarks} % % \begin{function}{\quark_new:N} % \begin{syntax} % \cs{quark_new:N} \meta{quark} % \end{syntax} % Creates a new \meta{quark} which expands only to \meta{quark}. % The \meta{quark} is defined globally, and an error message % is raised if the name was already taken. % \end{function} % % \begin{variable}{\q_stop} % Used as a marker for delimited arguments, such as % \begin{verbatim} % \cs_set:Npn \tmp:w #1#2 \q_stop {#1} % \end{verbatim} % \end{variable} % % \begin{variable}{\q_mark} % Used as a marker for delimited arguments when \cs{q_stop} is % already in use. % \end{variable} % % \begin{variable}{\q_nil} % Quark to mark a null value in structured variables or functions. Used % as an end delimiter when this may itself need to be tested % (in contrast to \cs{q_stop}, which is only ever used as a delimiter). % \end{variable} % % \begin{variable}{\q_no_value} % A canonical value for a missing value, when one is requested from % a data structure. This is therefore used as a \enquote{return} value % by functions such as \cs{prop_get:NnN} if there is no data to % return. % \end{variable} % % \section{Quark tests} % % The method used to define quarks means that the single token (\texttt{N}) % tests are faster than the multi-token (\texttt{n}) tests. The latter % should therefore only be used when the argument can definitely take % more than a single token. % % \begin{function}[EXP,pTF]{\quark_if_nil:N} % \begin{syntax} % \cs{quark_if_nil_p:N} \meta{token} % \cs{quark_if_nil:NTF} \meta{token} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{token} is equal to \cs{q_nil}. % \end{function} % % \begin{function}[EXP,pTF]{\quark_if_nil:n, \quark_if_nil:o, \quark_if_nil:V} % \begin{syntax} % \cs{quark_if_nil_p:n} \Arg{token list} \\ % \cs{quark_if_nil:nTF} \Arg{token list} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{token list} contains only \cs{q_nil} (distinct % from \meta{token list} being empty or containing \cs{q_nil} plus one % or more other tokens). % \end{function} % % \begin{function}[EXP,pTF]{\quark_if_no_value:N, \quark_if_no_value:c} % \begin{syntax} % \cs{quark_if_no_value_p:N} \meta{token} \\ % \cs{quark_if_no_value:NTF} \meta{token} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{token} is equal to \cs{q_no_value}. % \end{function} % % \begin{function}[EXP,pTF]{\quark_if_no_value:n} % \begin{syntax} % \cs{quark_if_no_value_p:n} \Arg{token list} \\ % \cs{quark_if_no_value:nTF} \Arg{token list} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{token list} contains only \cs{q_no_value} % (distinct from \meta{token list} being empty or containing % \cs{q_no_value} plus one or more other tokens). % \end{function} % % \section{Recursion} % % This module provides a uniform interface to intercepting and % terminating loops as when one is doing tail recursion. The building % blocks follow below and an example is shown in % Section~\ref{sec:l3quark:quark-example}. % % \begin{variable}{\q_recursion_tail} % This quark is appended to the data structure in question and % appears as a real element there. This means it gets any list % separators around it. % \end{variable} % % \begin{variable}{\q_recursion_stop} % This quark is added \emph{after} the data structure. Its purpose % is to make it possible to terminate the recursion at any point % easily. % \end{variable} % % \begin{function}[EXP]{\quark_if_recursion_tail_stop:N} % \begin{syntax} % \cs{quark_if_recursion_tail_stop:N} \meta{token} % \end{syntax} % Tests if \meta{token} contains only the marker % \cs{q_recursion_tail}, and if so uses % \cs{use_none_delimit_by_q_recursion_stop:w} to terminate the % recursion that this belongs to. The % recursion input must include the marker tokens \cs{q_recursion_tail} % and \cs{q_recursion_stop} as the last two items. % \end{function} % % \begin{function}[EXP,updated = 2011-09-06] % {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o} % \begin{syntax} % \cs{quark_if_recursion_tail_stop:n} \Arg{token list} % \end{syntax} % Tests if the \meta{token list} contains only % \cs{q_recursion_tail}, and if so uses % \cs{use_none_delimit_by_q_recursion_stop:w} to terminate the % recursion that this belongs to. The % recursion input must include the marker tokens \cs{q_recursion_tail} % and \cs{q_recursion_stop} as the last two items. % \end{function} % % \begin{function}[EXP]{\quark_if_recursion_tail_stop_do:Nn} % \begin{syntax} % \cs{quark_if_recursion_tail_stop_do:Nn} \meta{token} \Arg{insertion} % \end{syntax} % Tests if \meta{token} contains only the marker % \cs{q_recursion_tail}, and if so uses % \cs{use_i_delimit_by_q_recursion_stop:w} to terminate the % recursion that this belongs to. The % recursion input must include the marker tokens \cs{q_recursion_tail} % and \cs{q_recursion_stop} as the last two items. The \meta{insertion} % code is then added to the input stream after the recursion has % ended. % \end{function} % % \begin{function}[EXP,updated = 2011-09-06] % {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on} % \begin{syntax} % \cs{quark_if_recursion_tail_stop_do:nn} \Arg{token list} \Arg{insertion} % \end{syntax} % Tests if the \meta{token list} contains only % \cs{q_recursion_tail}, and if so uses % \cs{use_i_delimit_by_q_recursion_stop:w} to terminate the % recursion that this belongs to. The % recursion input must include the marker tokens \cs{q_recursion_tail} % and \cs{q_recursion_stop} as the last two items. The \meta{insertion} % code is then added to the input stream after the recursion has % ended. % \end{function} % % % \begin{function}[EXP,added = 2018-04-10] % { % \quark_if_recursion_tail_break:NN, % \quark_if_recursion_tail_break:nN % } % \begin{syntax} % \cs{quark_if_recursion_tail_break:nN} \Arg{token list} \cs[no-index]{\meta{type}_map_break:} % \end{syntax} % Tests if \meta{token list} contains only \cs{q_recursion_tail}, and % if so terminates the recursion using \cs[no-index]{\meta{type}_map_break:}. % The recursion end should be marked by \cs{prg_break_point:Nn} % \cs[no-index]{\meta{type}_map_break:}. % \end{function} % % \subsection{An example of recursion with quarks} % \label{sec:l3quark:quark-example} % % Quarks are mainly used internally in the \pkg{expl3} code to define % recursion functions such as \cs{tl_map_inline:nn} and so on. % Here is a small example to demonstrate how to use quarks in this fashion. % We shall define a command called |\my_map_dbl:nn| which takes a token list % and applies an operation to every \emph{pair} of tokens. % For example, |\my_map_dbl:nn {abcd} {[--#1--#2--]~}| would produce % \enquote{\ttfamily [--a--b--]~[--c--d--]~}. % Using quarks to define such functions simplifies their logic and ensures % robustness in many cases. % % % Here's the definition of |\my_map_dbl:nn|. % First of all, define the function that does the processing based on the % inline function argument |#2|. % Then initiate the recursion using an internal function. % The token list |#1| is terminated using \cs{q_recursion_tail}, with % delimiters according to the type of recursion (here a pair of % \cs{q_recursion_tail}), concluding with \cs{q_recursion_stop}. % These quarks are used to mark the end of the token list being operated upon. %\begin{verbatim} % \cs_new:Npn \my_map_dbl:nn #1#2 % { % \cs_set:Npn \__my_map_dbl_fn:nn ##1 ##2 {#2} % \__my_map_dbl:nn #1 \q_recursion_tail \q_recursion_tail % \q_recursion_stop % } %\end{verbatim} % % The definition of the internal recursion function follows. % First check if either of the input tokens are the termination quarks. % Then, if not, apply the inline function to the two arguments. %\begin{verbatim} % \cs_new:Nn \__my_map_dbl:nn % { % \quark_if_recursion_tail_stop:n {#1} % \quark_if_recursion_tail_stop:n {#2} % \__my_map_dbl_fn:nn {#1} {#2} %\end{verbatim} % Finally, recurse: %\begin{verbatim} % \__my_map_dbl:nn % } %\end{verbatim} % Note that contrarily to \LaTeX3 built-in mapping functions, this % mapping function cannot be nested, since the second map would overwrite % the definition of |\__my_map_dbl_fn:nn|. % % \section{Scan marks} % % Scan marks are control sequences set equal to \cs{scan_stop:}, % hence never expand in an expansion context and are (largely) % invisible if they are encountered in a typesetting context. % % Like quarks, they can be used as delimiters in weird functions % and are often safer to use for this purpose. % Since they are harmless when executed by \TeX{} in non-expandable % contexts, they can be used to mark the end of a set of instructions. % This allows to skip to that point if the end of the instructions % should not be performed (see \pkg{l3regex}). % % \begin{function}[added = 2018-04-01]{\scan_new:N} % \begin{syntax} % \cs{scan_new:N} \meta{scan mark} % \end{syntax} % Creates a new \meta{scan mark} which is set equal to \cs{scan_stop:}. % The \meta{scan mark} is defined globally, and an error message % is raised if the name was already taken by another scan mark. % \end{function} % % \begin{variable}[added = 2018-04-01]{\s_stop} % Used at the end of a set of instructions, as a marker % that can be jumped to using \cs{use_none_delimit_by_s_stop:w}. % \end{variable} % % \begin{function}[EXP,added = 2018-04-01]{\use_none_delimit_by_s_stop:w} % \begin{syntax} % \cs{use_none_delimit_by_s_stop:w} \meta{tokens} \cs{s_stop} % \end{syntax} % Removes the \meta{tokens} and \cs{s_stop} from the input stream. % This leads to a low-level \TeX{} error if \cs{s_stop} is absent. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3quark} implementation} % % \TestFiles{m3quark001.lvt} % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Quarks} % % \begin{macrocode} %<@@=quark> % \end{macrocode} % % \begin{macro}{\quark_new:N} % \UnitTested % Allocate a new quark. % \begin{macrocode} \cs_new_protected:Npn \quark_new:N #1 { \__kernel_chk_if_free_cs:N #1 \cs_gset_nopar:Npn #1 {#1} } % \end{macrocode} % \end{macro} % % \begin{variable}{\q_nil, \q_mark, \q_no_value, \q_stop} % Some \enquote{public} quarks. \cs{q_stop} is an \enquote{end of % argument} marker, \cs{q_nil} is a empty value and \cs{q_no_value} % marks an empty argument. % \begin{macrocode} \quark_new:N \q_nil \quark_new:N \q_mark \quark_new:N \q_no_value \quark_new:N \q_stop % \end{macrocode} % \end{variable} % % \begin{variable}{\q_recursion_tail, \q_recursion_stop} % Quarks for ending recursions. Only ever used there! % \cs{q_recursion_tail} is appended to whatever list structure we are % doing recursion on, meaning it is added as a proper list item with % whatever list separator is in use. \cs{q_recursion_stop} is placed % directly after the list. % \begin{macrocode} \quark_new:N \q_recursion_tail \quark_new:N \q_recursion_stop % \end{macrocode} % \end{variable} % % \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}[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 % each time a recursion is set up. Thus if the marker is found everything % can be wrapper up and finished off. The simple case is when the test % can guarantee that only a single token is being tested. In this case, % there is just a dedicated copy of the standard quark test. Both a gobbling % version and one inserting end code are provided. % \begin{macrocode} \cs_new:Npn \quark_if_recursion_tail_stop:N #1 { \if_meaning:w \q_recursion_tail #1 \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: } \cs_new:Npn \quark_if_recursion_tail_stop_do:Nn #1 { \if_meaning:w \q_recursion_tail #1 \exp_after:wN \use_i_delimit_by_q_recursion_stop:nw \else: \exp_after:wN \use_none:n \fi: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP] % {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o} % \UnitTested % \begin{macro}[EXP] % {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on} % \UnitTested % \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 % \cs{q_recursion_tail}. % \begin{macrocode} \cs_new:Npn \quark_if_recursion_tail_stop:n #1 { \tl_if_empty:oTF { \@@_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! } { \use_none_delimit_by_q_recursion_stop:w } { } } \cs_new:Npn \quark_if_recursion_tail_stop_do:nn #1 { \tl_if_empty:oTF { \@@_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! } { \use_i_delimit_by_q_recursion_stop:nw } { \use_none:n } } \cs_new:Npn \@@_if_recursion_tail:w #1 \q_recursion_tail #2 ? #3 ?! { #1 #2 } \cs_generate_variant:Nn \quark_if_recursion_tail_stop:n { o } \cs_generate_variant:Nn \quark_if_recursion_tail_stop_do:nn { o } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \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|. % \begin{macrocode} \cs_new:Npn \quark_if_recursion_tail_break:NN #1#2 { \if_meaning:w \q_recursion_tail #1 \exp_after:wN #2 \fi: } \cs_new:Npn \quark_if_recursion_tail_break:nN #1#2 { \tl_if_empty:oT { \@@_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! } {#2} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\quark_if_nil:N} % \UnitTested % \begin{macro}[pTF]{\quark_if_no_value:N, \quark_if_no_value:c} % \UnitTested % Here we test if we found a special quark as the first argument. % We better start with \cs{q_no_value} as the first argument since % the whole thing may otherwise loop if |#1| is wrongly given % a string like |aabc| instead of a single token.\footnote{It may % still loop in special circumstances however!} % \begin{macrocode} \prg_new_conditional:Npnn \quark_if_nil:N #1 { p, T , F , TF } { \if_meaning:w \q_nil #1 \prg_return_true: \else: \prg_return_false: \fi: } \prg_new_conditional:Npnn \quark_if_no_value:N #1 { p, T , F , TF } { \if_meaning:w \q_no_value #1 \prg_return_true: \else: \prg_return_false: \fi: } \prg_generate_conditional_variant:Nnn \quark_if_no_value:N { c } { p , T , F , TF } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\quark_if_nil:n, \quark_if_nil:V, \quark_if_nil:o} % \UnitTested % \begin{macro}[pTF]{\quark_if_no_value:n} % \UnitTested % \begin{macro}{\@@_if_nil:w, \@@_if_no_value:w} % \begin{macro}[EXP]{\@@_if_empty_if:o} % Let us explain \cs{quark_if_nil:nTF}. Expanding \cs{@@_if_nil:w} % once is safe thanks to the trailing \cs{q_nil} |??!|. The result of % expanding once is empty if and only if both delimited arguments |#1| % and~|#2| are empty and |#3|~is delimited by the last tokens~|?!|. % Thanks to the leading~|{}|, the argument~|#1| is empty if and only % if the argument of \cs{quark_if_nil:n} starts with \cs{q_nil}. The % argument~|#2| is empty if and only if this \cs{q_nil} is followed % immediately by~|?| or by~|{}?|, coming either from the trailing % tokens in the definition of \cs{quark_if_nil:n}, or from its % argument. In the first case, \cs{@@_if_nil:w} is followed by % |{}\q_nil| |{}?| |!\q_nil|~|??!|, hence |#3|~is delimited by the % final~|?!|, and the test returns \texttt{true} as wanted. In the % second case, the result is not empty since the first~|?!| in the % definition of \cs{quark_if_nil:n} stop~|#3|. The auxiliary here % is the same as \cs{__tl_if_empty_if:o}, with the same comments % applying. % \begin{macrocode} \prg_new_conditional:Npnn \quark_if_nil:n #1 { p, T , F , TF } { \@@_if_empty_if:o { \@@_if_nil:w {} #1 {} ? ! \q_nil ? ? ! } \prg_return_true: \else: \prg_return_false: \fi: } \cs_new:Npn \@@_if_nil:w #1 \q_nil #2 ? #3 ? ! { #1 #2 } \prg_new_conditional:Npnn \quark_if_no_value:n #1 { p, T , F , TF } { \@@_if_empty_if:o { \@@_if_no_value:w {} #1 {} ? ! \q_no_value ? ? ! } \prg_return_true: \else: \prg_return_false: \fi: } \cs_new:Npn \@@_if_no_value:w #1 \q_no_value #2 ? #3 ? ! { #1 #2 } \prg_generate_conditional_variant:Nnn \quark_if_nil:n { V , o } { p , TF , T , F } \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 } % \end{macrocode} % \end{macro} % \end{macro} % \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{quark_if_nil:nTF} % \item[N] gives an analogue of \cs{quark_if_nil:NTF} % \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 \msg_error:nnx { quark } { invalid-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 \msg_error:nnx { quark } { invalid-function } { \token_to_str:N #1 } \else: \if_meaning:w \q_nil #3 \q_nil \msg_error:nnx { quark } { invalid-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 } { \msg_error:nnxx { quark } { invalid-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} %<@@=scan> % \end{macrocode} % % \begin{macro}{\scan_new:N} % \UnitTested % Check whether the variable is already a scan mark, % then declare it to be equal to \cs{scan_stop:} globally. % \begin{macrocode} \cs_new_protected:Npn \scan_new:N #1 { \tl_if_in:NnTF \g_@@_marks_tl { #1 } { \msg_error:nnx { scanmark } { already-defined } { \token_to_str:N #1 } } { \tl_gput_right:Nn \g_@@_marks_tl {#1} \cs_new_eq:NN #1 \scan_stop: } } % \end{macrocode} % \end{macro} % % \begin{variable}{\s_stop} % \begin{variable}{\g_@@_marks_tl} % \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 the scan marks declared earlier to the pool here. % Since they lives in a different namespace, a little \pkg{l3docstrip} % cheating is necessary. % \begin{macrocode} \cs_new_eq:NN \s_stop \scan_stop: \cs_gset_nopar:Npn \g_@@_marks_tl { \s_stop %<@@=quark> \s_@@ %<@@=cs> \s_@@_mark \s_@@_stop %<@@=scan> } % \end{macrocode} % \end{variable} % \end{variable} % % \begin{macro}[EXP]{\use_none_delimit_by_s_stop:w} % \UnitTested % Similar to \cs{use_none_delimit_by_q_stop:w}. % \begin{macrocode} \cs_new:Npn \use_none_delimit_by_s_stop:w #1 \s_stop { } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex