From b11ef0810f2b6a651ce97d7e639f9c85efeedbd5 Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Sat, 28 Aug 2021 03:00:47 +0000
Subject: CTAN sync 202108280300

---
 macros/latex/contrib/etl/etl.dtx | 1699 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 1699 insertions(+)
 create mode 100644 macros/latex/contrib/etl/etl.dtx

(limited to 'macros/latex/contrib/etl/etl.dtx')

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=>>=,=<<
-- 
cgit v1.2.3

