summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3quark.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/l3kernel/l3quark.dtx
Initial commit
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3quark.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3quark.dtx636
1 files changed, 636 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3kernel/l3quark.dtx b/macros/latex/contrib/l3kernel/l3quark.dtx
new file mode 100644
index 0000000000..9ef207f346
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3quark.dtx
@@ -0,0 +1,636 @@
+% \iffalse meta-comment
+%
+%% File: l3quark.dtx
+%
+% Copyright (C) 1990-2019 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}
+%</driver>
+% \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 2019-08-25}
+%
+% \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
+% { <do something with the date> }
+% \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}{\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}[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}{\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}[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}[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}[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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN q #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN s #1 } { }
+\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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex