% \iffalse meta-comment % %% File: l3quark.dtx % % Copyright (C) 1990-2020 The LaTeX3 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 \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2020-03-06} % % \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 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. % An example of the quark testing functions and their use in recursion % can be seen in the implementation of \cs{clist_map_function:NN}. % % \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} % % \section{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} %<*initex|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{macro}{\quark_if_recursion_tail_stop:N} % \UnitTested % \begin{macro}{\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} % {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o} % \UnitTested % \begin{macro} % {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on} % \UnitTested % \begin{macro}{\@@_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}{\quark_if_recursion_tail_break:NN} % \begin{macro}{\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 |\quark_if_nil:n(TF)|. 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} % % \subsection{Scan marks} % % \begin{macrocode} %<@@=scan> % \end{macrocode} % % \begin{variable}{\g_@@_marks_tl} % \UnitTested % The list of all scan marks currently declared. % \begin{macrocode} \tl_new:N \g_@@_marks_tl % \end{macrocode} % \end{variable} % % \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 } { \__kernel_msg_error:nnx { kernel } { 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} % \UnitTested % We only declare one scan mark here, more can be defined % by specific modules. % \begin{macrocode} \scan_new:N \s_stop % \end{macrocode} % \end{variable} % % \begin{macro}{\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