summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/etl/etl.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/etl/etl.dtx')
-rw-r--r--macros/latex/contrib/etl/etl.dtx1699
1 files changed, 1699 insertions, 0 deletions
diff --git a/macros/latex/contrib/etl/etl.dtx b/macros/latex/contrib/etl/etl.dtx
new file mode 100644
index 0000000000..93b90ca9d0
--- /dev/null
+++ b/macros/latex/contrib/etl/etl.dtx
@@ -0,0 +1,1699 @@
+% \iffalse meta-comment
+%
+% File: etl.dtx Copyright (C) 2020-2021 Jonathan P. Spratte
+%
+% This work 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:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% ------------------------------------------------------------------------------
+%
+%<*driver>^^A>>=
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+\input l3docstrip.tex
+\askforoverwritefalse
+\preamble
+
+--------------------------------------------------------------
+etl -- expandable token list operations
+E-mail: jspratte@yahoo.de
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+--------------------------------------------------------------
+
+Copyright (C) 2020-2021 Jonathan P. Spratte
+
+This work 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:
+
+ http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Jonathan P. Spratte.
+
+This work consists of the file etl.dtx
+and the derived files etl.pdf
+ etl.sty
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+\generate{\file{etl.sty}{\from{etl.dtx}{pkg}}}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%
+\IfFileExists{etl.sty}{\RequirePackage{etl}}{}
+\PassOptionsToPackage{full}{textcomp}
+\documentclass{l3doc}
+\RequirePackage[oldstylenums,nott]{kpfonts}
+\RequirePackage{randtext,xcolor}
+\input{glyphtounicode}
+\usepackage{xistercian}
+\pdfgentounicode=1
+\let\metaORIG\meta
+\protected\def\meta #1{\texttt{\metaORIG{#1}}}
+\renewcommand*\thefootnote{\cistercian{footnote}}
+\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black}
+\makeatletter
+\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}}
+\protected\def\savefootnotestuff
+ {%
+ \let\mysavethefootnote\thefootnote
+ \let\mysave@makefnmark\@makefnmark
+ \let\mysave@makefntext\@makefntext
+ }
+\def\restorefootnotestuff{\texorpdfstring\restorefootnotestuff@{}}
+\def\restorefootnotestuff@
+ {%
+ \global\let\thefootnote\mysavethefootnote
+ \global\let\@makefnmark\mysave@makefnmark
+ \global\let\@makefntext\mysave@makefntext
+ }
+\makeatother
+\begin{document}
+ \savefootnotestuff
+ \title
+ {^^A
+ \restorefootnotestuff
+ The \pkg{etl} package\\ expandable token list operations^^A
+ }
+ \GetFileInfo{etl.sty}
+ \date{\filedate\space\fileversion}
+ \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}}
+ \DocInput{etl.dtx}
+\end{document}
+%</driver>^^A=<<
+% \fi
+%
+% \maketitle
+% \setcounter{footnote}{1}
+%
+% \tableofcontents
+%
+% \begin{documentation}^^A>>=
+%
+% \section{Documentation}
+%
+% The \pkg{etl} package provides a few \emph{slow but expandable} alternatives
+% to unexpandable functions found inside the \pkg{l3tl} module of \pkg{expl3}.
+% All user functions must not contain the tokens \cs{s__etl_stop}\footnote{At
+% any nesting level of groups} or \cs{__etl_act_result:n}\footnote{Outside of
+% some local brace groups} in any argument (but there might be other forbidden
+% tokens, all of which are internals to this package, and usually shouldn't
+% somehow end up inside the input stream by accident).
+%
+% There is another limitation of this package: There are tokens which cannot
+% expandably be differentiated from each other, those are active characters let
+% to the same character with a different category code, something like the
+% following:
+%
+% \begin{verbatim}
+% \char_set_catcode_letter:N a
+% \char_set_active_eq:NN a a
+% \char_set_catcode_active:N a
+% \end{verbatim}
+%
+% After this the active `|a|'s couldn't be told apart from non-active `|a|'s of
+% category letter by the parsers in this package.\footnote{Thanks to Bruno Le
+% Floch for pointing that out.} In general two tokens are considered equal if
+% \cs{etl_token_if_eq:NNTF} yields true (see there). Another limitation is that
+% the parser doesn't consider the character code of tokens with category 1 or 2
+% (group begin and group end, typically |{}|), instead all tokens found with
+% these two category codes are normalised to \texttt{\{\textsubscript{1}} and
+% \texttt{\}\textsubscript{2}}.
+%
+% The core macro \cs{etl_act:nnnnnnn} is modelled after an internal of
+% \pkg{l3tl} called \cs{__tl_act:NNNn} but with some more possibilities added.
+%
+%
+% \subsection{A general loop}
+%
+% \begin{function}[EXP]{\etl_act:nnnnnnn, \etl_act:nnnnnn, \etl_act:nnnnn}
+% \begin{syntax}
+% \cs{etl_act:nnnnnnn} \Arg{normal} \Arg{space} \Arg{group} \Arg{final} \Arg{status} \Arg{output} \Arg{token list}
+% \cs{etl_act:nnnnnn} \Arg{normal} \Arg{space} \Arg{group} \Arg{final} \Arg{status} \Arg{token list}
+% \cs{etl_act:nnnnn} \Arg{normal} \Arg{space} \Arg{group} \Arg{status} \Arg{token list}
+% \end{syntax}
+% This function will act on the \meta{token list} (somewhat a
+% |map_tokens|-function). Both \meta{normal} and \meta{group} should be code
+% that expects two following arguments (the first being the \meta{status}, the
+% second the next |N|-type token or the contents of the next group in the
+% \meta{token list}), and \meta{space} should expect only the \meta{status} as
+% a following argument.
+%
+% You can also specify \meta{final} code which will receive the \meta{status}
+% followed by the output (which you can assign with \cs{etl_act_output:n} and
+% \cs{etl_act_output_pre:n}), and will be used inside an |e|-expansion context
+% (so you'll want to protect anything you want to output from further
+% expansion using \cs{exp_not:n}). Also you can specify some \meta{output}
+% which should be there from the beginning.
+%
+% Functions without the argument \meta{output} will start with an empty
+% output, and those without \meta{final} will just output the results at the
+% end.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion. If you don't need the \meta{final} argument processor and don't
+% have to reorder some of the output you can also use \cs{exp_not:n} to
+% directly output tokens where you currently are, this might be faster.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_output:n, \etl_act_output_pre:n}
+% \begin{syntax}
+% \cs{etl_act_output:n} \Arg{token list}
+% \end{syntax}
+% This will add \meta{token list} to the output of \cs{etl_act:nnnnnnn}. The
+% normal version will add \meta{token list} after the current output, the
+% |pre| variant will put it before the current output.
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_output_rest:, \etl_act_output_rest_pre:}
+% \begin{syntax}
+% \cs{etl_act_output_rest:}
+% \end{syntax}
+% After this macro was used all remaining things inside the \meta{token list}
+% argument of \cs{etl_act:nnnnnnn} will be added to the output. The normal
+% version will add it to the end of the output, the |pre| variant will put the
+% remainder before the current output in reversed order.
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_status:n}
+% \begin{syntax}
+% \cs{etl_act_status:n} \Arg{status}
+% \end{syntax}
+% This will change the current status of \cs{etl_act:nnnnnnn} to
+% \meta{status}.
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_put_back:n}
+% \begin{syntax}
+% \cs{etl_act_put_back:n} \Arg{token list}
+% \end{syntax}
+% You can put \meta{token list} back into the input stream to be reconsidered
+% by the current \cs{etl_act:nnnnnnn} loop (of course this doesn't have to be
+% literally put back, you might add completely new contents with this).
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \etl_act_switch:nnn,
+% \etl_act_switch_normal:n, \etl_act_switch_space:n, \etl_act_switch_group:n
+% }
+% \begin{syntax}
+% \cs{etl_act_switch:nnn} \Arg{normal} \Arg{space} \Arg{group}
+% \cs{etl_act_switch_normal:n} \Arg{normal}
+% \cs{etl_act_switch_space:n} \Arg{space}
+% \cs{etl_act_switch_group:n} \Arg{group}
+% \end{syntax}
+% With these functions you can change the provided code to act on
+% \meta{normal} (so |N|-type) tokens, \meta{space}s, and \meta{group}s.
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_do_final:}
+% \begin{syntax}
+% \cs{etl_act_do_final:}
+% \end{syntax}
+% This will immediately stop the current \cs{etl_act:nnnnnnn} invocation and
+% execute the provided \meta{final} code (or if a variant without the
+% \meta{final} code was used, the output). The \meta{final} code will receive
+% the current status followed by the current output as two arguments (just
+% like it would when the end of the \meta{token list} was reached).
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_break:, \etl_act_break_discard:}
+% \begin{syntax}
+% \cs{etl_act_break:}
+% \end{syntax}
+% This will immediately stop the current \cs{etl_act:nnnnnnn} invocation and
+% leave the current output in the input stream. The |discard| variant will
+% gobble the current output and leave nothing in the input stream.
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_break:n}
+% \begin{syntax}
+% \cs{etl_act_break:n} \Arg{token list}
+% \end{syntax}
+% This will immediately stop the current \cs{etl_act:nnnnnnn} invocation,
+% gobble the current output and leave \meta{token list} in the input stream.
+% Might be used inside of the \meta{normal}, \meta{space}, or \meta{group}
+% code of \cs{etl_act:nnnnnnn}.
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_act_break_pre:n, \etl_act_break_post:n}
+% \begin{syntax}
+% \cs{etl_act_break_pre:n} \Arg{token list}
+% \end{syntax}
+% This will immediately stop the current \cs{etl_act:nnnnnnn} invocation and
+% leave the current output in the input stream, and in the case of the normal
+% variant followed by \meta{token list}, in the |pre|-case preceded by
+% \meta{token list}.
+% \end{function}
+%
+%
+% \subsection{Conditionals}\label{sec:conditionals}
+%
+% \begin{function}[pTF]{\etl_token_if_eq:NN}
+% \begin{syntax}
+% \cs{etl_token_if_eq_p:NN} \meta{token_1} \meta{token_2}
+% \cs{etl_token_if_eq:NNTF} \meta{token_1} \meta{token_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares \meta{token_1} and \meta{token_2} and yields |true| if the two are
+% equal. Two tokens are considered equal if they have the same meaning (so if
+% \cs{if_meaning:w} is true) and the same string representation (so if
+% \cs{str_if_eq:nnTF} is true).
+% \end{function}
+%
+% \begin{function}[pTF]{\etl_if_eq:nn}
+% \begin{syntax}
+% \cs{etl_if_eq_p:nn} \Arg{token list_1} \Arg{token list_2}
+% \cs{etl_if_eq:nnTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares \meta{token list_1} and \meta{token list_2} with each other on a
+% token-by-token basis. Keep in mind that there are tokens which can't be told
+% apart from each other, and that groups are normalised. If both token lists
+% match (modulo the mentioned limitations) the \meta{true code} is left in the
+% input stream, else the \meta{false code}.
+% \end{function}
+%
+% \begin{function}[pTF]{\etl_token_if_in:nN}
+% \begin{syntax}
+% \cs{etl_token_if_in_p:nN} \Arg{token list} \meta{token}
+% \cs{etl_token_if_in:nNTF} \Arg{token list} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Searches for \meta{token} inside the \meta{token list}. If it is found
+% returns true. Brace groups inside the \meta{token list} are ignored.
+% \end{function}
+%
+% \begin{function}[pTF]{\etl_token_if_in_deep:nN}
+% \begin{syntax}
+% \cs{etl_token_if_in_deep_p:nN} \Arg{token list} \meta{token}
+% \cs{etl_token_if_in_deep:nNTF} \Arg{token list} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Searches for \meta{token} inside the \meta{token list}. If it is found
+% returns true. Brace groups inside the \meta{token list} are recursively
+% searched as well.
+% \end{function}
+%
+% \begin{function}[pTF]{\etl_if_in:nn}
+% \begin{syntax}
+% \cs{etl_if_in_p:nn} \Arg{token list} \Arg{search text}
+% \cs{etl_if_in:nnTF} \Arg{token list} \Arg{search text} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Searches for \meta{search text} inside of \meta{token list}. If it is
+% found the \meta{true code} is left in the input stream, else the \meta{false
+% code}. Both macro parameter tokens as well as tokens with category code~1
+% and 2 (normally |{}|) can be part of \meta{search text} (unlike for the
+% similar function \cs{tl_if_in:nnTF}). Material inside of groups in
+% \meta{token list} is ignored (except for the groups contained in
+% \meta{search text}). So the following would first yield |true| and then
+% |false|:
+% \end{function}
+% \begin{verbatim}
+% \etl_if_in:nnTF { a{b{c}} } { {b{c}} } { true } { false }
+% \etl_if_in:nnTF { a{b{c}} } { b{c} } { true } { false }
+% \end{verbatim}
+%
+% \begin{function}[pTF]{\etl_if_in_deep:nn}
+% \begin{syntax}
+% \cs{etl_if_in_deep_p:nn} \Arg{token list} \Arg{search text}
+% \cs{etl_if_in_deep:nnTF} \Arg{token list} \Arg{search text} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Does the same as \cs{etl_if_in:nnTF} but also recursively searches inside of
+% groups in \meta{token list}. So this would yield |true| in both of the
+% cases in above example.
+% \end{function}
+%
+%
+% \subsection{Modifying token lists}
+%
+% \begin{function}[EXP]{\etl_token_replace_once:nNn}
+% \begin{syntax}
+% \cs{etl_token_replace_once:nNn} \Arg{token list} \meta{token} \Arg{replacement}
+% \end{syntax}
+% This function will replace the first occurrence of \meta{token} inside of
+% \meta{token list} that is not hidden inside a group with \meta{replacement}.
+% The \meta{token} has to be a valid |N|-type argument.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_token_replace_all:nNn}
+% \begin{syntax}
+% \cs{etl_token_replace_all:nNn} \Arg{token list} \meta{token} \Arg{replacement}
+% \end{syntax}
+% This function will replace each occurrence of \meta{token} inside of
+% \meta{token list} that is not hidden inside a group with \meta{replacement}.
+% The \meta{token} has to be a valid |N|-type argument.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_token_replace_all_deep:nNn}
+% \begin{syntax}
+% \cs{etl_token_replace_all_deep:nNn} \Arg{token list} \meta{token} \Arg{replacement}
+% \end{syntax}
+% This function will replace each occurrence of \meta{token} inside of
+% \meta{token list} with \meta{replacement}.
+% The \meta{token} has to be a valid |N|-type argument.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_replace_once:nnn}
+% \begin{syntax}
+% \cs{etl_replace_once:nnn} \meta{token list} \meta{search text} \meta{replacement}
+% \end{syntax}
+% This function will replace the first occurrence of \meta{search text}
+% inside of \meta{token list} that is not hidden inside a group with
+% \meta{replacement}.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_replace_all:nnn}
+% \begin{syntax}
+% \cs{etl_replace_all:nnn} \meta{token list} \meta{search text} \meta{replacement}
+% \end{syntax}
+% This function will replace all occurrences of \meta{search text}
+% inside of \meta{token list} that are not hidden inside a group with
+% \meta{replacement}.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\etl_replace_all_deep:nnn}
+% \begin{syntax}
+% \cs{etl_replace_all_deep:nnn} \meta{token list} \meta{search text} \meta{replacement}
+% \end{syntax}
+% This function will replace all occurrences of \meta{search text}
+% inside of \meta{token list} with \meta{replacement}.
+%
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \hbox{|x|- or} |e|-type
+% argument expansion. The result will be returned after exactly two steps of
+% expansion.
+% \end{texnote}
+% \end{function}
+%
+%
+% \subsection{New expandable fast tests}
+%
+% Functions generated with the means in this section are as fast as the
+% \pkg{l3tl} variants of them, but have at least one fixed argument.
+%
+% \begin{function}{\etl_new_if_in:Nnn}
+% \begin{syntax}
+% \cs{etl_new_if_in:Nnn} \meta{function} \meta{search text} \meta{conditions}
+% \end{syntax}
+% This will define a new \meta{function} which will act as a conditional and
+% search for \meta{search text} inside of an |n|-type argument completely
+% expandable. The \meta{conditions} should be a comma-separated list
+% containing one or more of |p|, |T|, |F| and |TF| (just like for
+% \cs{prg_new_conditional:Npnn}). The \meta{search text} must not contain
+% tokens with category code~1 or 2 (normally |{}|) and can't contain macro
+% parameter tokens (normally |#|).
+% Unlike for the conditionals in \autoref{sec:conditionals}, the \meta{search
+% text} of functions created with \cs{etl_new_if_in:Nnn} might contain
+% \cs{s__etl_stop} tokens.
+%
+% So the following would yield |true| followed by |false|:
+% \end{function}
+% \begin{verbatim}
+% \etl_new_if_in:Nnn \my_if_a_in:n { a } { TF }
+% \my_if_a_in:nTF { a text } { true } { false }
+% \my_if_a_in:nTF { text } { true } { false }
+% \end{verbatim}
+%
+%
+% \subsection{Bugs and Feature Requests}
+%
+% If you find bugs or want to request features you can do so either via email
+% (see the first page) or via Github at
+% \url{https://github.com/Skillmon/ltx_etl/issues}.
+%
+% \end{documentation}^^A=<<
+%
+% \clearpage
+%
+% \begin{implementation}^^A>>=
+%
+% \section{Implementation}
+%
+% \begin{macrocode}
+%<*pkg>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=etl>
+% \end{macrocode}
+%
+% Tell who we are:
+% \begin{macrocode}
+\ProvidesExplPackage{etl}
+ {2021-08-20} {0.1}
+ {expandable token list manipulation}
+% \end{macrocode}
+%
+% Ensure dependencies are met:
+% \begin{macrocode}
+\cs_if_exist:NF \tex_expanded:D
+ {
+ \msg_new:nnn { etl } { expanded-missing }
+ { The expanded primitive is required. }
+ \msg_fatal:nn { etl } { expanded-missing }
+ }
+% \end{macrocode}
+%
+%
+% \subsection{Primitives}
+%
+% \begin{macro}[EXP]{\@@_expanded:w,\@@_unexpanded:w,\@@_detokenize:w}
+% Private copies of a few primitives (evil code for \pkg{expl3}).
+% \begin{macrocode}
+\cs_new_eq:NN \@@_expanded:w \tex_expanded:D
+\cs_new_eq:NN \@@_unexpanded:w \tex_unexpanded:D
+\cs_new_eq:NN \@@_detokenize:w \tex_detokenize:D
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \subsection{Variables}
+%
+% \begin{variable}{\s_@@_stop,\s_@@_mark}
+% Scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+\scan_new:N \s_@@_mark
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Small auxiliaries}
+%
+% \begin{macro}[EXP]{\@@_if_mark:nTF,\@@_if_mark:w,\@@_if_mark_true:w}
+% A small check whether some argument contains the scan mark \cs{s_@@_mark},
+% the mark should always be the last token of the argument (and only a single
+% such mark should be contained) to ensure correct behaviour.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_mark:nTF #1
+ { \@@_if_mark:w #1 \@@_if_mark_true:w \s_@@_mark \use_ii:nn }
+\cs_new:Npn \@@_if_mark:w #1 \s_@@_mark {}
+\cs_new:Npn \@@_if_mark_true:w \s_@@_mark \use_ii:nn #1#2 {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_split_first:w}
+% Can be used to extract the first element from a token list, it should always
+% be used like this:
+% |\exp_after:wN |\meta{function}^^A
+% | \@@_expanded:w { \@@_split_first:w |\meta{arg}| }|.
+% \begin{macrocode}
+\cs_new:Npn \@@_split_first:w #1
+ {
+ { \@@_unexpanded:w {#1} }
+ \if_false: { \fi: \exp_after:wN } \exp_after:wN { \if_false: } \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_turn_true:w,\@@_fi_turn_false:w}
+% Fast ways to change the outcome of a test.
+% \begin{macrocode}
+\cs_new:Npn \@@_turn_true:w \if_false: { \if_true: }
+\cs_new:Npn \@@_fi_turn_false:w \fi: \if_true: { \fi: \if_false: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_rm_space:w}
+% Fast macro to gobble an immediately following single space.
+% \begin{macrocode}
+\use:n { \cs_new:Npn \@@_rm_space:w } ~ {}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_if_empty:nTF, \@@_if_empty:nT, \@@_if_empty:w,
+% \@@_if_empty_true:w, \@@_if_empty_true_TF:w
+% }
+% This is a fast test whether something is empty or not. The argument must not
+% contain \cs{s_@@_stop} for this to work (but since that limitation is true
+% for most if not all user facing functions of this module, this is fine to
+% gain a bit of speed).
+% \begin{macrocode}
+\cs_new:Npn \@@_if_empty:nT #1
+ {
+ \@@_if_empty:w
+ \s_@@_stop #1 \s_@@_stop \@@_if_empty_true:w
+ \s_@@_stop \s_@@_stop \use_none:n
+ }
+\cs_new:Npn \@@_if_empty:nTF #1
+ {
+ \@@_if_empty:w
+ \s_@@_stop #1 \s_@@_stop \@@_if_empty_true_TF:w
+ \s_@@_stop \s_@@_stop \use_ii:nn
+ }
+\cs_new:Npn \@@_if_empty:w #1 \s_@@_stop \s_@@_stop {}
+\cs_new:Npn \@@_if_empty_true:w \s_@@_stop \s_@@_stop \use_none:n #1
+ {#1}
+\cs_new:Npn \@@_if_empty_true_TF:w \s_@@_stop \s_@@_stop \use_ii:nn #1#2
+ {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_head_is_group:nTF,\@@_if_head_is_group:nT}
+% This test works pretty much the same way \cs{tl_if_head_is_group:nTF} works,
+% but it is faster because it gets rid of the unnecessary \cs{if:w} and
+% instead only works by argument gobbling. Drawback is that if you only expand
+% the macro twice you could end up with unbalanced braces.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_head_is_group:nTF #1
+ {
+ \exp_after:wN \use_none:n \exp_after:wN
+ {
+ \exp_after:wN { \token_to_str:N #1 ? }
+ \exp_after:wN \use_iii:nnnn \token_to_str:N
+ }
+ \use_ii:nn
+ }
+\cs_new:Npn \@@_if_head_is_group:nT #1
+ {
+ \exp_after:wN \use_none:n \exp_after:wN
+ {
+ \exp_after:wN { \token_to_str:N #1 ? }
+ \exp_after:wN \use_iii:nnn \token_to_str:N
+ }
+ \use_none:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \subsection{The \texttt{act} loop}
+%
+% \begin{macro}[EXP]{\etl_act:nnnnnnn,\etl_act:nnnnnn,\etl_act:nnnnn}
+% \begin{macro}[EXP]{\@@_act:nnnnnnn}
+% \begin{macro}[EXP]{\@@_act_just_result:nn}
+% The act loop is modelled after the \pkg{expl3} internal \cs{__tl_act:NNNn}
+% but with a few more features making it more general. Those are (argument
+% to \cs{etl_act:nnnnnnn} in parentheses): a status (|#5|),
+% |n|-type mapping instead of just |N|-type functions, a final result
+% processing (|#4|), and the possibility to preset some output (|#6|). The
+% other arguments are: |#7| the token list on which we should act, |#1|
+% function to use for |N|-type elements in that list, |#2| function to use for
+% spaces in that list, and |#3| function to use on groups in that list.
+%
+% Just like the \cs{__tl_act:NNNn} function, this has a token which must not
+% occur in the arguments, in this case that token is \cs{s_@@_stop}. The
+% result is stored as an argument to the (undefined) function
+% \cs{@@_act_result:n}.
+% \begin{macrocode}
+\cs_new:Npn \etl_act:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \@@_unexpanded:w \@@_expanded:w
+ {
+ {
+ \@@_act:w #7 {\s_@@_stop} . \s_@@_stop {#5} {#1} {#2} {#3}
+ \@@_act_result:n {#6} {#4}
+ }
+ }
+ }
+% \end{macrocode}
+% We also provide a version without the \cs{@@_unexpanded:w} around it for
+% internal purposes, in which we'd otherwise have to remove it for correct
+% behaviour.
+% \begin{macrocode}
+\cs_new:Npn \@@_act:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \@@_expanded:w
+ {
+ \@@_act:w #7 {\s_@@_stop} . \s_@@_stop {#5} {#1} {#2} {#3}
+ \@@_act_result:n {#6} {#4}
+ }
+ }
+% \end{macrocode}
+% We also provide two reduced function variants, the first without presetting
+% some output, the second also without the final processor.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_act:nnnnnn #1#2#3#4#5#6 }
+ { \etl_act:nnnnnnn {#1} {#2} {#3} {#4} {#5} {} {#6} }
+\exp_args:Nno \use:n { \cs_new:Npn \etl_act:nnnnn #1#2#3#4#5 }
+ { \etl_act:nnnnnnn {#1} {#2} {#3} \@@_act_just_result:nn {#4} {} {#5} }
+% \end{macrocode}
+% The final processor is provided with two |n|-type arguments (both in braces)
+% the first being the status, the second the output. To just get the output we
+% gobble the status and put \cs{@@_unexpanded:w} there to protect the final
+% output from further expanding.
+% \begin{macrocode}
+\cs_new:Npn \@@_act_just_result:nn #1 { \@@_unexpanded:w }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_head_is_space:nTF,\@@_if_head_is_space_true:w}
+% \begin{macro}[EXP]{\@@_if_head_is_N_type:nTF,\@@_if_head_is_N_type_false:w}
+% \begin{macro}[EXP]{\@@_act:w,\@@_act_if_space:w,\@@_act_space:w}
+% We need a few macros with spaces at weird places so define them here. Since
+% we got the limitation of not allowing \cs{s_@@_stop} we can use that token
+% to get some fast tests. The first tests for a space at the front, and since
+% that one is pretty fast we can use it to build a faster alternative to check
+% for a starting |N|-type as well (with the drawback that this would yield
+% |true| for an empty argument, something we have to keep in mind).
+% \begin{macrocode}
+\group_begin:
+ \cs_set:Npn \@@_tmp:n #1
+ {
+ \cs_new:Npn \@@_if_head_is_space:nTF ##1
+ {
+ \@@_act_if_space:w
+ \s_@@_stop ##1 \s_@@_stop \@@_if_head_is_space_true:w
+ \s_@@_stop #1 \s_@@_stop \use_ii:nn
+ }
+ \cs_new:Npn \@@_if_head_is_space_true:w
+ \s_@@_stop #1 \s_@@_stop \use_ii:nn ##1##2
+ {##1}
+ \cs_new:Npn \@@_if_head_is_N_type:nTF ##1
+ {
+ \@@_act_if_space:w
+ \s_@@_stop ##1 \s_@@_stop \@@_if_head_is_N_type_false:w
+ \s_@@_stop #1 \s_@@_stop
+ \@@_if_head_is_group:nT {##1} \use_iii:nnn
+ \use_i:nn
+ }
+ \cs_new:Npn \@@_if_head_is_N_type_false:w
+ \s_@@_stop #1 \s_@@_stop
+ \@@_if_head_is_group:nT ##1 \use_iii:nnn
+ \use_i:nn
+ ##2##3
+ {##3}
+% \end{macrocode}
+% The act loop \cs{@@_act:w} grabs the remainder of the list, delimited by
+% \cs{s_@@_stop}, picks up the status (|##2|), and the user provided functions
+% for |N|-types (|##3|), spaces (|##4|), and groups (|##5|). We need to check
+% which type is at the head of the token list (the space test is a bit
+% stripped down, and very fast this way).
+% \begin{macrocode}
+ \cs_new:Npn \@@_act:w ##1 \s_@@_stop ##2##3##4##5
+ {
+ \@@_act_if_space:w
+ \s_@@_stop ##1 \s_@@_stop \@@_act_space:w {##4}
+ \s_@@_stop #1 \s_@@_stop
+ \@@_if_head_is_group:nT {##1} \@@_act_group:w
+ \@@_act_normal:w {##3} {##5}
+ {##2} ##1 \s_@@_stop {##2} {##3} {##4} {##5}
+ }
+% \end{macrocode}
+% The check for spaces just gobbles everything up to the first
+% \cs{s_@@_stop}\verb*| |. If we found a space at the head we remove that
+% space and leave in the input the space function, the status, and
+% \cs{@@_act:w} for the next iteration.
+% \begin{macrocode}
+ \cs_new:Npn \@@_act_if_space:w ##1 \s_@@_stop #1 ##2 \s_@@_stop {}
+ \cs_new:Npn \@@_act_space:w
+ ##1 \s_@@_stop #1 \s_@@_stop
+ \@@_if_head_is_group:nT ##2 \@@_act_group:w \@@_act_normal:w ##3 ##4
+ ##5 #1
+ { ##1 {##5} \@@_act:w }
+ }
+ \@@_tmp:n { ~ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_act_normal:w}
+% For a normal token we can act quite easy, just pick up that token and leave
+% the next iteration in the input stream (|#2| is the group code, which is
+% gobbled).
+% \begin{macrocode}
+\cs_new:Npn \@@_act_normal:w #1#2#3#4 { #1 {#3} #4 \@@_act:w }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_act_group:w,\@@_act_if_end:w}
+% Since the end marker is a single \cs{s_@@_stop} in a group, we have to test
+% whether that end marker is found. The test here leads to undefined behaviour
+% if the user supplied token list contains such a marker at an any point. If
+% the end marker is found we call the final handler (for which we have to
+% remove the \cs{s_@@_stop} to correctly grab its arguments), else we provide
+% the user supplied function the next group and input the next iteration. |#1|
+% is the normal function, which is gobbled.
+% \begin{macrocode}
+\cs_new:Npn \@@_act_group:w \@@_act_normal:w #1#2#3#4
+ {
+ \@@_act_if_end:w #4 \use_i:nn \etl_act_do_final: \s_@@_stop
+ #2 {#3} {#4} \@@_act:w
+ }
+\cs_new:Npn \@@_act_if_end:w #1 \s_@@_stop {}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]
+% {
+% \etl_act_output:n, \etl_act_output_pre:n,
+% \etl_act_output_rest:, \etl_act_output_rest_pre:
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_act_output_normal:nN, \@@_act_output_space:n, \@@_act_output_group:nn,
+% \@@_act_output_normal_pre:nN, \@@_act_output_space_pre:n,
+% \@@_act_output_group_pre:nn
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_act_unexpanded_normal:nN, \@@_act_unexpanded_space:n,
+% \@@_act_unexpanded_group:nn
+% }
+% To allow reordering the output we unfortunately can't just use
+% \cs{@@_unexpanded:w} and be done, so we have to shift a few tokens around
+% instead. All the output macros work by the same idea, except for
+% \cs{etl_act_output_rest:} and \cs{etl_act_output_rest_pre:}, since it's a
+% non-trivial task to get the remainder of the argument. Instead these two
+% swap out the user provided functions for some that only pass through the
+% input as output, for which we need six internal output macros.
+%
+% In those cases in which we don't need reordering, we can internally shortcut
+% using \cs{@@_unexpanded:w}.
+% \begin{macrocode}
+\cs_new:Npn \etl_act_output:n #1 #2 \@@_act_result:n #3
+ { #2 \@@_act_result:n { #3 #1 } }
+\cs_new:Npn \etl_act_output_pre:n #1 #2 \@@_act_result:n #3
+ { #2 \@@_act_result:n { #1 #3 } }
+\cs_new:Npn \etl_act_output_rest: #1 \s_@@_stop #2#3#4#5
+ {
+ #1 \s_@@_stop {#2}
+ \@@_act_output_normal:nN \@@_act_output_space:n \@@_act_output_group:nn
+ }
+\cs_new:Npn \etl_act_output_rest_pre: #1 \s_@@_stop #2#3#4#5
+ {
+ #1 \s_@@_stop {#2}
+ \@@_act_output_normal_pre:nN
+ \@@_act_output_space_pre:n
+ \@@_act_output_group_pre:nn
+ }
+\cs_new:Npn \@@_act_output_normal:nN #1#2 #3 \@@_act_result:n #4
+ { #3 \@@_act_result:n { #4 #2 } }
+\cs_new:Npn \@@_act_output_space:n #1 #2 \@@_act_result:n #3
+ { #2 \@@_act_result:n { #3 ~ } }
+\cs_new:Npn \@@_act_output_group:nn #1#2 #3 \@@_act_result:n #4
+ { #3 \@@_act_result:n { #4 {#2} } }
+\cs_new:Npn \@@_act_output_normal_pre:nN #1#2 #3 \@@_act_result:n #4
+ { #3 \@@_act_result:n { #2 #4 } }
+\cs_new:Npn \@@_act_output_space_pre:n #1 #2 \@@_act_result:n #3
+ { #2 \@@_act_result:n { ~ #3 } }
+\cs_new:Npn \@@_act_output_group_pre:nn #1#2 #3 \@@_act_result:n #4
+ { #3 \@@_act_result:n { {#2} #4 } }
+\cs_new:Npn \@@_act_unexpanded_normal:nN #1 { \exp_not:N }
+\cs_new:Npn \@@_act_unexpanded_space:n #1 { ~ }
+\cs_new:Npn \@@_act_unexpanded_group:nn #1#2 { { \@@_unexpanded:w {#2} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_act_status:n}
+% Just switch out the status which is stored immediately after \cs{s_@@_stop}.
+% \begin{macrocode}
+\cs_new:Npn \etl_act_status:n #1 #2 \s_@@_stop #3
+ { #2 \s_@@_stop {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_act_put_back:n}
+% Place the first argument after the next iteration of the loop. This macro
+% might strip a set of braces around |#2|, because it could happen that the
+% user provided code only leaves one group between this functions argument
+% and \cs{@@_act:w}, but that would arguably be wrong input anyway, an easy
+% fix would be to use
+% \begin{verbatim}
+% \cs_new:Npn \etl_act_put_back:n #1
+% { \@@_act_put_back:nw {#1} \prg_do_nothing: }
+% \cs_new:Npn \@@_act_put_back:nw #1 #2 \@@_act:w { #2 \@@_act:w #1 }
+% \end{verbatim}
+% instead of:
+% \begin{macrocode}
+\cs_new:Npn \etl_act_put_back:n #1 #2 \@@_act:w { #2 \@@_act:w #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \etl_act_switch:nnn, \etl_act_switch_normal:n,
+% \etl_act_switch_space:n, \etl_act_switch_group:n
+% }
+% Pretty straight forward, just switch out the user provided functions for the
+% new argument.
+% \begin{macrocode}
+\cs_new:Npn \etl_act_switch:nnn #1#2#3 #4 \s_@@_stop #5#6#7#8
+ { #4 \s_@@_stop {#5} {#1} {#2} {#3} }
+\cs_new:Npn \etl_act_switch_normal:n #1 #2 \s_@@_stop #3#4
+ { #2 \s_@@_stop {#3} {#1} }
+\cs_new:Npn \etl_act_switch_space:n #1 #2 \s_@@_stop #3#4#5
+ { #2 \s_@@_stop {#3} {#4} {#1} }
+\cs_new:Npn \etl_act_switch_group:n #1 #2 \s_@@_stop #3#4#5#6
+ { #2 \s_@@_stop {#3} {#4} {#5} {#1} }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]
+% {
+% \etl_act_do_final:,
+% \etl_act_break:,\etl_act_break_discard:,
+% \etl_act_break:n, \etl_act_break_pre:n, \etl_act_break_post:n
+% }
+% These are different forms to end the loop. The first will gobble the
+% remainder and apply the final action on the token list currently stored for
+% output.
+%
+% The |break| variants will gobble the final action and output what's
+% currently there (except for the |discard| variant).
+% \begin{macrocode}
+\cs_new:Npn \etl_act_do_final: #1 \s_@@_stop #2#3 \@@_act_result:n #4#5
+ { #5 {#2} {#4} }
+\cs_new:Npn \etl_act_break: #1 \@@_act_result:n #2#3 { \@@_unexpanded:w {#2} }
+\cs_new:Npn \etl_act_break_discard: #1 \@@_act_result:n #2#3 {}
+\cs_new:Npn \etl_act_break:n #1 #2 \@@_act_result:n #3#4
+ { \@@_unexpanded:w {#1} }
+\cs_new:Npn \etl_act_break_pre:n #1 #2 \@@_act_result:n #3#4
+ { \@@_unexpanded:w { #1 #3 } }
+\cs_new:Npn \etl_act_break_post:n #1 #2 \@@_act_result:n #3#4
+ { \@@_unexpanded:w { #3 #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \subsection{Expandable tests}
+%
+% \begin{macro}[pTF]{\etl_token_if_eq:NN}
+% We consider two tokens equal when they have the same meaning and the same
+% string representation. This isn't always correct. If an active character is
+% let to the same character with a different category code those two tokens
+% aren't distinguishable by expansion, \emph{afaik}. To get the optimisation
+% of \cs{prg_new_conditional:Npnn} we use \cs{if_false:} and turn it true if
+% both tests are true (this is easier than coding all four variants by hand,
+% even though that could give slightly better performance). The exception
+% being the |TF| variant, since that is used in the inner loop of many
+% functions. The braces around the arguments of \cs{token_if_eq_meaning:NNT}
+% are necessary because of the first step of expansion applied to that
+% function.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_token_if_eq:NNTF #1#2 }
+ {
+ \token_if_eq_meaning:NNT {#1} {#2} { \str_if_eq:nnT #1#2 \use_ii:nnn }
+ \use_ii:nn
+ }
+\exp_args:Nno
+\use:n { \prg_new_conditional:Npnn \etl_token_if_eq:NN #1#2 { T , F , p } }
+ {
+ \token_if_eq_meaning:NNT {#1} {#2} { \str_if_eq:nnT #1#2 \@@_turn_true:w }
+ \if_false:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\etl_token_if_in:nN}
+% \begin{macro}[EXP]{\@@_token_if_in:NnN}
+% Searching for just a single token is rather easy, we just loop over the list
+% and compare the |N|-type tokens to the one token provided. If we find a
+% match we break and return |true|, else we'll return |false| eventually.
+% \begin{macrocode}
+\exp_args:Nno
+\use:n { \prg_new_conditional:Npnn \etl_token_if_in:nN #1#2 { TF , T , F , p } }
+ {
+ \@@_act:nnnnnnn
+ \@@_token_if_in:NN \use_none:n \use_none:nn
+ \@@_act_just_result:nn
+ {#2}
+ \if_false:
+ {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_token_if_in:NN #1#2 }
+ {
+ \etl_token_if_eq:NNTF {#1} {#2} { \etl_act_break:n \if_true: } {}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\etl_token_if_in_deep:nN}
+% \begin{macro}[EXP]{\@@_token_if_in_deep:Nn}
+% The deep variant just has to recursively call itself on groups to also
+% search those.
+% \begin{macrocode}
+\exp_args:Nno \use:n
+ { \prg_new_conditional:Npnn \etl_token_if_in_deep:nN #1#2 { TF , T , F , p } }
+ {
+ \@@_act:nnnnnnn
+ \@@_token_if_in:NN \use_none:n \@@_token_if_in_deep:Nn
+ \@@_act_just_result:nn
+ {#2}
+ \if_false:
+ {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_token_if_in_deep:Nn #1#2 }
+ { \etl_token_if_in_deep:nNT {#2} {#1} { \etl_act_break:n \if_true: } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\etl_if_eq:nn}
+% \begin{macro}[EXP]{\@@_if_eq_normal:nN,\@@_if_eq_normal:NnN}
+% \begin{macro}[EXP]{\@@_if_eq_space:n}
+% \begin{macro}[EXP]{\@@_if_eq_group:nn,\@@_if_eq_group:nnn}
+% \begin{macro}[EXP]{\@@_if_eq_final:nn}
+% The test needs to compare the full lists on a token-by-token basis. One of
+% the two lists is stored inside the status the other is processed. The |act|
+% code will then leave either \cs{if_false:} or \cs{if_true:} in the input
+% stream.
+% \begin{macrocode}
+\exp_args:Nno
+\use:n { \prg_new_conditional:Npnn \etl_if_eq:nn #1#2 { TF , T , F , p } }
+ {
+ \@@_act:nnnnnnn
+ \@@_if_eq_normal:nN
+ \@@_if_eq_space:n
+ \@@_if_eq_group:nn
+ \@@_if_eq_final:nn
+ {#2}
+ {}
+ {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% To compare the next token we need to check whether the status is already
+% empty (which would mean that token list is longer, hence not equal), if it's
+% not empty and the head is |N|-type we compare these two (the test here for
+% |N|-type fails for empty arguments, hence we have to test this separately).
+% If they are equal we store the rest of the second token list in the status
+% and go on with the loop, else we break out and return false.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_eq_normal:nN #1#2 }
+ {
+ \@@_if_empty:nT {#1} { \etl_act_break:n \if_false: }
+ \@@_if_head_is_N_type:nTF {#1}
+ {
+ \exp_after:wN \@@_if_eq_normal:NnN
+ \@@_expanded:w { \@@_split_first:w #1 }
+ #2
+ }
+ { \etl_act_break:n \if_false: }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_eq_normal:NnN #1#2#3 }
+ {
+ \etl_token_if_eq:NNTF {#1} {#3}
+ { \etl_act_status:n {#2} }
+ { \etl_act_break:n \if_false: }
+ }
+% \end{macrocode}
+% Spaces are pretty similar, but easier, we don't need to split of the first
+% token in a complicated manner, instead we just gobble a leading space.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_eq_space:n #1 }
+ {
+ \@@_if_head_is_space:nTF {#1}
+ { \exp_after:wN \etl_act_status:n \exp_after:wN { \@@_rm_space:w #1 } }
+ { \etl_act_break:n \if_false: }
+ }
+% \end{macrocode}
+% Groups are similarly handled to normal arguments, but instead of comparing
+% only two tokens we have to compare by recursion.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_eq_group:nn #1 }
+ {
+ \@@_if_head_is_group:nTF {#1}
+ {
+ \exp_after:wN \@@_if_eq_group:nnn
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ { \etl_act_break:n \if_false: }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_eq_group:nnn #1#2#3 }
+ {
+ \etl_if_eq:nnTF {#1} {#3}
+ { \etl_act_status:n {#2} }
+ { \etl_act_break:n \if_false: }
+ }
+% \end{macrocode}
+% Finally, if the loop didn't break until the first token list is empty we
+% just have to make sure that the second list is also empty by now. If that's
+% the case the two are equal, else not. We need to leave either true or false
+% (protected against the \cs{@@_expanded:w} expansion) in the input.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_eq_final:nn #1#2 }
+ {
+ \exp_after:wN
+ \@@_unexpanded:w
+ \@@_if_empty:nT {#1} { { \if_true: } \use_none:n } { \if_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\etl_if_in:nn}
+% \begin{macro}[EXP]
+% {\@@_if_in_normal:nN,\@@_if_in_normal:nnnN,\@@_if_in_normal:NnnnN}
+% \begin{macro}[EXP]{\@@_if_in_space:n,\@@_if_in_space:nnn}
+% \begin{macro}[EXP]
+% {\@@_if_in_group:nn,\@@_if_in_group:nnnn,\@@_if_in_group:nnnnn}
+% \begin{macro}[EXP]{\@@_if_in_put_back:n}
+% \cs{etl_if_in:nn} has to reevaluate every token but the very first in order
+% to compare them, else something like |aab| wouldn't contain |ab| according
+% to the test, because the second |a| would've been gobbled. For this we need
+% \cs{@@_if_in_put_back:n} which will remove the first token (we need to
+% watch out for spaces) and puts the rest back using \cs{etl_act_put_back:n}.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_put_back:n #1 }
+ {
+ \@@_if_head_is_space:nTF {#1}
+ { \exp_after:wN \etl_act_put_back:n \exp_after:wN { \@@_rm_space:w #1 } }
+ { \exp_after:wN \etl_act_put_back:n \exp_after:wN { \use_none:n #1 } }
+ }
+% \end{macrocode}
+% As already said, we'll need to reinsert some tokens, and we'll might have to
+% revert what was already matched, so inside of the status we store the
+% remainder of the pattern which needs to be matched, followed by the entire
+% pattern, followed by the tokens which were already matched (and might need
+% to be put back). As soon as the pattern is matched the remainder will be
+% empty and we'll leave \cs{if_true:} in the input, at the end of the entire
+% list we'll leave \cs{if_false:}, which we store in the prefilled output. The
+% emptiness of the pattern will be checked before the next token is evaluated,
+% so the trailing space after |#1| does no harm but allows the token list to
+% end in the pattern.
+%
+% All of the macros used as arguments to \cs{@@_act:nnnnnnn} will need to
+% unbrace the status which will then lead to three arguments. Else this is
+% pretty much the same idea as \cs{etl_if_eq:nnTF}.
+% \begin{macrocode}
+\exp_args:Nno
+\use:n { \prg_new_conditional:Npnn \etl_if_in:nn #1#2 { TF , T , F , p } }
+ {
+ \@@_act:nnnnnnn
+ \@@_if_in_normal:nN \@@_if_in_space:n \@@_if_in_group:nn
+ \@@_act_just_result:nn
+ { { #2 } { #2 } {} }
+ \if_false:
+ { #1 ~ }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% Just like \cs{@@_if_in_group:nn}, \cs{@@_if_in_normal:nN} needs to split off
+% the first token of the pattern, for which \cs{@@_split_first:w} is used,
+% and \cs{@@_if_in_space:n} needs to trim off a leading space.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_in_normal:nN #1 { \@@_if_in_normal:nnnN #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_normal:nnnN #1#2#3#4 }
+ {
+ \@@_if_empty:nT {#1} { \etl_act_break:n \if_true: }
+ \@@_if_head_is_N_type:nTF {#1}
+ {
+ \exp_after:wN \@@_if_in_normal:NnnnN
+ \@@_expanded:w { \@@_split_first:w #1 } {#2} {#3} #4
+ }
+ {
+ \etl_act_status:n { {#2} {#2} {} }
+ \@@_if_in_put_back:n { #3 #4 }
+ }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_normal:NnnnN #1#2#3#4#5 }
+ {
+ \etl_token_if_eq:NNTF {#1} {#5}
+ { \etl_act_status:n { {#2} {#3} {#4#5} } }
+ {
+ \@@_if_in_put_back:n { #4 #5 }
+ \etl_act_status:n { {#3} {#3} {} }
+ }
+ }
+\cs_new:Npn \@@_if_in_space:n #1 { \@@_if_in_space:nnn #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_space:nnn #1#2#3 }
+ {
+ \@@_if_empty:nT {#1} { \etl_act_break:n \if_true: }
+ \@@_if_head_is_space:nTF {#1}
+ {
+ \exp_after:wN \etl_act_status:n \exp_after:wN
+ { \exp_after:wN { \@@_rm_space:w #1 } {#2} { #3 ~ } }
+ }
+ {
+ \@@_if_in_put_back:n { #3 ~ }
+ \etl_act_status:n { {#2} {#2} {} }
+ }
+ }
+\cs_new:Npn \@@_if_in_group:nn #1 { \@@_if_in_group:nnnn #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_group:nnnn #1 }
+ {
+ \@@_if_empty:nT {#1} { \etl_act_break:n \if_true: }
+ \@@_if_head_is_group:nTF {#1}
+ {
+ \exp_after:wN \@@_if_in_group:nnnnn
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ { \@@_if_in_group_false:nnn }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_group:nnnnn #1#2#3#4#5 }
+ {
+ \etl_if_eq:nnTF {#1} {#5}
+ { \etl_act_status:n { {#2} {#3} { #4 {#5} } } }
+ {
+ \@@_if_in_put_back:n { #4 {#5} }
+ \etl_act_status:n { {#3} {#3} {} }
+ }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_group_false:nnn #1#2#3 }
+ {
+ \@@_if_in_put_back:n { #2 {#3} }
+ \etl_act_status:n { {#1} {#1} {} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\etl_if_in_deep:nn}
+% \begin{macro}[EXP]
+% {
+% \@@_if_in_group_deep:nn, \@@_if_in_group_deep:nnnn,
+% \@@_if_in_group_deep:nnnnn, \@@_if_in_group_deep_false:nnn
+% }
+% In essence this is the same as \cs{etl_if_in:nnTF}, but additionally
+% every time a group is encountered we need to search that group by recursion
+% as well after directly comparing it to the pattern.
+% \begin{macrocode}
+\exp_args:Nno
+\use:n { \prg_new_conditional:Npnn \etl_if_in_deep:nn #1#2 { TF , T , F , p } }
+ {
+ \@@_act:nnnnnnn
+ \@@_if_in_normal:nN \@@_if_in_space:n \@@_if_in_group_deep:nn
+ \@@_act_just_result:nn
+ { { #2 } { #2 } {} }
+ \if_false:
+ { #1 ~ }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_in_group_deep:nn #1 { \@@_if_in_group_deep:nnnn #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_group_deep:nnnn #1 }
+ {
+ \@@_if_empty:nT {#1} { \etl_act_break:n \if_true: }
+ \@@_if_head_is_group:nTF {#1}
+ {
+ \exp_after:wN \@@_if_in_group_deep:nnnnn
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ { \@@_if_in_group_deep_false:nnn }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_group_deep:nnnnn #1#2#3#4#5 }
+ {
+ \etl_if_eq:nnTF {#1} {#5}
+ { \etl_act_status:n { {#2} {#3} { #4 {#5} } } }
+ {
+ \etl_if_in_deep:nnT {#5} {#3} { \etl_act_break:n \if_true: }
+ \@@_if_in_put_back:n { #4 {#5} }
+ \etl_act_status:n { {#3} {#3} {} }
+ }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_in_group_deep_false:nnn #1#2#3 }
+ {
+ \etl_if_in_deep:nnT {#3} {#1} { \etl_act_break:n \if_true: }
+ \@@_if_in_put_back:n { #2 {#3} }
+ \etl_act_status:n { {#1} {#1} {} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_token_replace_all:nNn}
+% \begin{macro}[EXP]{\@@_token_replace:NnnN}
+% Replaceing a single token (and in fact the same is true for all the
+% replacement actions in this package) doesn't need reordering and no post
+% processing, so we can use in place output using \cs{@@_unexpanded:w}. We
+% store the token we want to replace inside the act function, as well as an
+% additional argument which will be executed once a replacement was done
+% (this is used for the \cs{etl_token_replace_once:nNn} function).
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_token_replace_all:nNn #1#2#3 }
+ {
+ \etl_act:nnnnnn
+ { \@@_token_replace:NnnN #2 {} }
+ \@@_act_unexpanded_space:n
+ \@@_act_unexpanded_group:nn
+ \use_none:nn
+ {#3}
+ {#1}
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_token_replace:NnnN #1#2#3#4 }
+ {
+ \etl_token_if_eq:NNTF {#1} {#4}
+ { \@@_unexpanded:w {#3} #2 }
+ { \@@_unexpanded:w {#4} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_token_replace_all_deep:nNn}
+% \begin{macro}[EXP]{\@@_token_replace_deep:Nnn}
+% Deep replacement is done by recursion. Since the deep variant will not
+% execute any additional code we omit such an additional argument for it.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_token_replace_all_deep:nNn #1#2#3 }
+ {
+ \etl_act:nnnnnn
+ { \@@_token_replace:NnnN #2 {} }
+ \@@_act_unexpanded_space:n
+ { \@@_token_replace_deep:Nnn #2 }
+ \use_none:nn
+ {#3}
+ {#1}
+ }
+% \end{macrocode}
+% Here |{#1}| is used to get correct results from the first step of expansion
+% done directly.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_token_replace_deep:Nnn #1#2#3 }
+ { \exp_after:wN { \etl_token_replace_all_deep:nNn {#3} {#1} {#2} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_token_replace_once:nNn}
+% To only handle the first matching token we just let the replacement internal
+% exchange the function to directly output any token.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_token_replace_once:nNn #1#2#3 }
+ {
+ \etl_act:nnnnnn
+ {
+ \@@_token_replace:NnnN #2
+ { \etl_act_switch_normal:n \@@_act_unexpanded_normal:nN }
+ }
+ \@@_act_unexpanded_space:n
+ \@@_act_unexpanded_group:nn
+ \use_none:nn
+ {#3}
+ {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_replace_all:nnn}
+% \begin{macro}[EXP]
+% {
+% \@@_replace_put_back:nnnN,
+% \@@_replace_put_back_normal:Nn, \@@_replace_put_back_group:nn
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_replace_normal:nN, \@@_replace_normal:nnnnNN,
+% \@@_replace_normal:NnnnnNN, \@@_replace_normal_false:nnnNN
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_replace_space:n, \@@_replace_space:nnnnN, \@@_replace_space_aux:nnnnN,
+% \@@_replace_space_false:nnnN
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_replace_group:nn, \@@_replace_group:nnnnNn, \@@_replace_group:nnnnnNn,
+% \@@_replace_group_false:nnnNn
+% }
+% \begin{macro}[EXP]{\@@_replace_final:nn, \@@_replace_final:nnnnNn}
+% Replacing an arbitrary number of tokens (which might include braces and
+% spaces) is quite a bit harder than a single |N|-type. We place in the status
+% the remainder of the pattern, the full pattern, delayed tokens (those
+% which matched the pattern), the replacement, and a marker which should tell
+% us whether we want to only replace the first match (if so use
+% \cs{s_@@_mark}, else any other single token).
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_replace_all:nnn #1#2#3 }
+ {
+ \etl_act:nnnnnn
+ \@@_replace_normal:nN
+ \@@_replace_space:n
+ \@@_replace_group:nn
+ \@@_replace_final:nn
+ { {#2} {#2} {} {#3} \scan_stop: }
+ {#1}
+ }
+% \end{macrocode}
+% We again need to be able to put back a few tokens, but this time we also
+% need to know whether the first token is an |N|-type or group, because we
+% can't just gobble the first element but need to output it unchanged.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_put_back:nnnN #1#2#3#4 }
+ {
+ \@@_if_head_is_space:nTF {#1}
+ {
+ \exp_after:wN \etl_act_put_back:n \exp_after:wN { \@@_rm_space:w #1 } ~
+ }
+ {
+ \@@_if_head_is_group:nTF {#1}
+ { \exp_after:wN \@@_replace_put_back_group:nn }
+ { \exp_after:wN \@@_replace_put_back_normal:Nn }
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ \etl_act_status:n { {#2} {#2} {} {#3} #4 }
+ }
+\cs_new:Npn \@@_replace_put_back_group:nn #1
+ {
+ \@@_unexpanded:w { {#1} }
+ \etl_act_put_back:n
+ }
+\cs_new:Npn \@@_replace_put_back_normal:Nn #1
+ {
+ \@@_unexpanded:w {#1}
+ \etl_act_put_back:n
+ }
+\cs_new:Npn \@@_replace_normal:nN #1 { \@@_replace_normal:nnnnNN #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_normal:nnnnNN #1 }
+ {
+ \@@_if_head_is_N_type:nTF {#1}
+ {
+ \exp_after:wN \@@_replace_normal:NnnnnNN
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ { \@@_replace_normal_false:nnnNN }
+ }
+% \end{macrocode}
+% Just to keep track of the different arguments here: |#1| is the next token
+% in the pattern, |#2| is the remainder of the pattern, |#3| is the full
+% pattern stored for reuse, |#4| are the delayed tokens, which might need to
+% be put back, |#5| is the replacement text, |#6| is the marker which might
+% indicate the |once| function, and |#7| is the next token of the input.
+% \begin{macrocode}
+\exp_args:Nno
+\use:n { \cs_new:Npn \@@_replace_normal:NnnnnNN #1#2#3#4#5#6#7 }
+ {
+ \etl_token_if_eq:NNTF {#1} {#7}
+ {
+ \@@_if_empty:nTF {#2}
+ {
+ \@@_unexpanded:w {#5}
+ \@@_if_mark:nTF {#6}
+ {
+ \etl_act_switch:nnn
+ \@@_act_unexpanded_normal:nN
+ \@@_act_unexpanded_space:n
+ \@@_act_unexpanded_group:nn
+ \etl_act_status:n { {} {} {} {} #6 }
+ }
+ { \etl_act_status:n { {#3} {#3} {} {#5} #6 } }
+ }
+ { \etl_act_status:n { {#2} {#3} { #4 #7 } {#5} #6 } }
+ }
+ { \@@_replace_put_back:nnnN { #4 #7 } {#3} {#5} #6 }
+ }
+\exp_args:Nno
+\use:n { \cs_new:Npn \@@_replace_normal_false:nnnNN #1#2#3#4#5 }
+ { \@@_replace_put_back:nnnN { #2 #5 } {#1} {#3} {#4} }
+\cs_new:Npn \@@_replace_space:n #1 { \@@_replace_space:nnnnN #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_space:nnnnN #1 }
+ {
+ \@@_if_head_is_space:nTF {#1}
+ {
+ \exp_after:wN \@@_replace_space_aux:nnnnN \exp_after:wN
+ { \@@_rm_space:w #1 }
+ }
+ { \@@_replace_space_false:nnnN }
+ }
+% \end{macrocode}
+% Again, to keep track, |#1| is the remainder of the pattern, |#2| is the full
+% pattern, |#3| the delayed tokens, |#4| the replacement text, |#5| the marker
+% for the |once| function.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_space_aux:nnnnN #1#2#3#4#5 }
+ {
+ \@@_if_empty:nTF {#1}
+ {
+ \@@_unexpanded:w {#4}
+ \@@_if_mark:nTF {#5}
+ {
+ \etl_act_switch:nnn
+ \@@_act_unexpanded_normal:nN
+ \@@_act_unexpanded_space:n
+ \@@_act_unexpanded_group:nn
+ \etl_act_status:n { {} {} {} {} #5 }
+ }
+ { \etl_act_status:n { {#2} {#2} {} {#4} #5 } }
+ }
+ { \etl_act_status:n { {#1} {#2} { #3 ~ } {#4} #5 } }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_space_false:nnnN #1#2#3#4 }
+ { \@@_replace_put_back:nnnN { #2 ~ } {#1} {#3} {#4} }
+\cs_new:Npn \@@_replace_group:nn #1 { \@@_replace_group:nnnnNn #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_group:nnnnNn #1 }
+ {
+ \@@_if_head_is_group:nTF {#1}
+ {
+ \exp_after:wN \@@_replace_group:nnnnnNn
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ { \@@_replace_group_false:nnnNn }
+ }
+% \end{macrocode}
+% And again, |#1| the next group of the pattern, |#2| the remainder of the
+% pattern, |#3| the full pattern, |#4| the delayed stuff, |#5| the replacement
+% text, |#6| the marker for the |once| function, |#7| the next group in the
+% input.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_group:nnnnnNn #1#2#3#4#5#6#7 }
+ {
+ \etl_if_eq:nnTF {#1} {#7}
+ {
+ \@@_if_empty:nTF {#2}
+ {
+ \@@_unexpanded:w {#5}
+ \@@_if_mark:nTF {#6}
+ {
+ \etl_act_switch:nnn
+ \@@_act_unexpanded_normal:nN
+ \@@_act_unexpanded_space:n
+ \@@_act_unexpanded_group:nn
+ \etl_act_status:n { {} {} {} {} #6 }
+ }
+ { \etl_act_status:n { {#3} {#3} {} {#5} #6 } }
+ }
+ { \etl_act_status:n { {#2} {#3} { #4 {#7} } {#5} #6 } }
+ }
+ { \@@_replace_put_back:nnnN { #4 {#7} } {#3} {#5} #6 }
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_group_false:nnnNn #1#2#3#4#5 }
+ { \@@_replace_put_back:nnnN { #2 {#5} } {#1} {#3} {#4} }
+\cs_new:Npn \@@_replace_final:nn #1 { \@@_replace_final:nnnnNn #1 }
+\cs_new:Npn \@@_replace_final:nnnnNn #1#2#3#4#5#6 { \@@_unexpanded:w { #6#3 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_replace_all_deep:nnn}
+% \begin{macro}[EXP]
+% {
+% \@@_replace_group_deep:nn, \@@_replace_group_deep:nnnnNn,
+% \@@_replace_group_deep:nnnnnNn, \@@_replace_group_deep_false:nnnNn
+% }
+% The |deep| variant works again pretty much the same as the |all| variant,
+% except that it searches groups recursively.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_replace_all_deep:nnn #1#2#3 }
+ {
+ \etl_act:nnnnnn
+ \@@_replace_normal:nN
+ \@@_replace_space:n
+ \@@_replace_group_deep:nn
+ \@@_replace_final:nn
+ { {#2} {#2} {} {#3} \scan_stop: }
+ {#1}
+ }
+\cs_new:Npn \@@_replace_group_deep:nn #1
+ { \@@_replace_group_deep:nnnnNn #1 }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_replace_group_deep:nnnnNn #1 }
+ {
+ \@@_if_head_is_group:nTF {#1}
+ {
+ \exp_after:wN \@@_replace_group_deep:nnnnnNn
+ \@@_expanded:w { \@@_split_first:w #1 }
+ }
+ { \@@_replace_group_deep_false:nnnNn }
+ }
+\exp_args:Nno
+\use:n { \cs_new:Npn \@@_replace_group_deep:nnnnnNn #1#2#3#4#5#6#7 }
+ {
+ \etl_if_eq:nnTF {#1} {#7}
+ {
+ \@@_if_empty:nTF {#2}
+ {
+ \@@_unexpanded:w {#5}
+ \etl_act_status:n { {#3} {#3} {} {#5} #6 }
+ }
+ { \etl_act_status:n { {#2} {#3} { #4 {#7} } {#5} #6 } }
+ }
+ {
+ \@@_if_empty:nTF {#4}
+ {
+ { \etl_replace_all_deep:nnn {#7} {#3} {#5} }
+ \etl_act_status:n { {#3} {#3} {} {#5} #6 }
+ }
+ { \@@_replace_put_back:nnnN { #4 {#7} } {#3} {#5} #6 }
+ }
+ }
+\exp_args:Nno
+\use:n { \cs_new:Npn \@@_replace_group_deep_false:nnnNn #1#2#3#4#5 }
+ {
+ \@@_if_empty:nTF {#2}
+ {
+ { \etl_replace_all_deep:nnn {#5} {#1} {#3} }
+ \etl_act_status:n { {#1} {#1} {} {#3} #4 }
+ }
+ { \@@_replace_put_back:nnnN { #2 {#5} } {#1} {#3} #4 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\etl_replace_once:nnn}
+% And this is the same as the |all| variant except that we now use the
+% \cs{s_@@_mark} marker inside the status.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \etl_replace_once:nnn #1#2#3 }
+ {
+ \etl_act:nnnnnn
+ \@@_replace_normal:nN
+ \@@_replace_space:n
+ \@@_replace_group:nn
+ \@@_replace_final:nn
+ { {#2} {#2} {} {#3} \s_@@_mark }
+ {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\etl_new_if_in:Nnn}
+% \begin{macro}{\@@_new_if_in:NNnn}
+% These tests work essentially in the same way as \cs{tl_if_in:nnTF}, but
+% instead they use a predefined internal macro so that no definition at use
+% time is necessary.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new_protected:Npn \etl_new_if_in:Nnn #1#2 }
+ {
+ \exp_args:Nc \@@_new_if_in:NNnn
+ { @@_user_function ~ \cs_to_str:N #1 ~ \tl_to_str:n {#2} :w }
+ #1 {#2}
+ }
+\cs_new_protected:Npn \@@_new_if_in:NNnn #1#2#3#4
+ {
+ \scan_stop:
+ \if_false: { \fi:
+ \cs_new:Npn #1 ##1 #3 {}
+ \prg_new_conditional:Npnn #2 ##1 {#4}
+ {
+ \if:w
+ \scan_stop:
+ \@@_detokenize:w \exp_after:wN { #1 ##1 {}{} #3 }
+ \scan_stop:
+ \@@_fi_turn_false:w
+ \fi:
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \if_false: } \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</pkg>
+% \end{macrocode}
+%
+% \end{implementation}^^A=<<
+%
+% \clearpage
+% \PrintIndex
+%
+% \Finale
+\endinput
+%
+^^A vim: ft=tex fdm=marker fmr=>>=,=<<