diff options
Diffstat (limited to 'macros/latex/required/l3kernel/l3expan.dtx')
-rw-r--r-- | macros/latex/required/l3kernel/l3expan.dtx | 2398 |
1 files changed, 2398 insertions, 0 deletions
diff --git a/macros/latex/required/l3kernel/l3expan.dtx b/macros/latex/required/l3kernel/l3expan.dtx new file mode 100644 index 0000000000..e79b5f32b9 --- /dev/null +++ b/macros/latex/required/l3kernel/l3expan.dtx @@ -0,0 +1,2398 @@ +% \iffalse meta-comment +% +%% File: l3expan.dtx +% +% Copyright (C) 1990-2024 The LaTeX Project +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "l3kernel bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3expan} module\\ Argument expansion^^A +% } +% +% \author{^^A +% The \LaTeX{} Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2024-04-11} +% +% \maketitle +% +% \begin{documentation} +% +% This module provides generic methods for expanding \TeX{} arguments in a +% systematic manner. The functions in this module all have prefix |exp|. +% +% Not all possible variations are implemented for every base +% function. Instead only those that are used within the \LaTeX3 kernel +% or otherwise seem to be of general interest are implemented. +% Consult the module description to find out which functions are +% actually defined. The next section explains how to define missing +% variants. +% +% \section{Defining new variants} +% \label{sec:l3expan:defining-variants} +% +% The definition of variant forms for base functions may be necessary +% when writing new functions or when applying a kernel function in a +% situation that we haven't thought of before. +% +% Internally preprocessing of arguments is done with functions of the form +% \cs[no-index]{exp_\ldots{}}. They all look alike, an example would be +% \cs{exp_args:NNo}. This function has three arguments, the first and the +% second are a single tokens, while the third argument should be given +% in braces. Applying \cs{exp_args:NNo} expands the content of third +% argument once before any expansion of the first and second arguments. +% If \cs{seq_gpush:No} was not defined it could be coded in the following way: +% \begin{verbatim} +% \exp_args:NNo \seq_gpush:Nn +% \g_file_name_stack +% { \l_tmpa_tl } +% \end{verbatim} +% In other words, the first argument to \cs{exp_args:NNo} is the base +% function and the other arguments are preprocessed and then passed to +% this base function. In the example the first argument to the base +% function should be a single token which is left unchanged while the +% second argument is expanded once. From this example we can also see +% how the variants are defined. They just expand into the appropriate +% |\exp_| function followed by the desired base function, \emph{e.g.} +% \begin{quote} +% |\cs_generate_variant:Nn \seq_gpush:Nn { No } | +% \end{quote} +% results in the definition of |\seq_gpush:No| +% \begin{quote} +% |\cs_new:Npn \seq_gpush:No { \exp_args:NNo \seq_gpush:Nn }| +% \end{quote} +% Providing variants in this way in style files is safe as the +% \cs{cs_generate_variant:Nn} function will only create new definitions if +% there is not already one available. Therefore adding +% such definition to later releases of the kernel will not make such +% style files obsolete. +% +% The steps above may be automated by using the function +% \cs{cs_generate_variant:Nn}, described next. +% +% \section{Methods for defining variants} +% \label{sec:l3expan:variants-method} +% +% We recall the set of available argument specifiers. +% \begin{itemize} +% \item |N|~is used for single-token arguments while |c|~constructs a +% control sequence from its name and passes it to a parent function as +% an |N|-type argument. +% \item Many argument types extract or expand some tokens and provide it +% as an |n|-type argument, namely a braced multiple-token argument: +% |V|~extracts the value of a variable, |v|~extracts the value from +% the name of a variable, |n|~uses the argument as it is, |o|~expands +% once, |f|~expands fully the front of the token list, |e| and +% |x|~expand fully all tokens (differences are explained later). +% \item A few odd argument types remain: |T|~and |F|~for conditional +% processing, otherwise identical to |n|-type arguments, +% |p|~for the parameter text +% in definitions, |w|~for arguments with a specific syntax, and |D|~to +% denote primitives that should not be used directly. +% \end{itemize} +% +% \begin{function}[updated = 2017-11-28] +% {\cs_generate_variant:Nn, \cs_generate_variant:cn} +% \begin{syntax} +% \cs{cs_generate_variant:Nn} \meta{parent control sequence} \Arg{variant argument specifiers} +% \end{syntax} +% This function is used to define argument-specifier variants of the +% \meta{parent control sequence} for \LaTeX3 code-level macros. The +% \meta{parent control sequence} is first separated into the +% \meta{base name} and \meta{original argument specifier}. The +% comma-separated list of \meta{variant argument specifiers} is +% then used to define variants of the +% \meta{original argument specifier} if these are not already +% defined; entries which correspond to existing functions are silently +% ingored. For each \meta{variant} given, a function is created +% that expands its arguments as detailed and passes them +% to the \meta{parent control sequence}. So for example +% \begin{verbatim} +% \cs_set:Npn \foo:Nn #1#2 { code here } +% \cs_generate_variant:Nn \foo:Nn { c } +% \end{verbatim} +% creates a new function |\foo:cn| which expands its first +% argument into a control sequence name and passes the result to +% |\foo:Nn|. Similarly +% \begin{verbatim} +% \cs_generate_variant:Nn \foo:Nn { NV , cV } +% \end{verbatim} +% generates the functions |\foo:NV| and |\foo:cV| in the same +% way. The \cs{cs_generate_variant:Nn} function should only be applied if +% the \meta{parent control sequence} is already defined. (This is only +% enforced if debugging support \texttt{check-declarations} is enabled.) +% If the \meta{parent +% control sequence} is protected or if the \meta{variant} involves any +% |x|~argument, then the \meta{variant control sequence} is also +% protected. The \meta{variant} is created globally, as is any +% \cs[no-index]{exp_args:N\meta{variant}} function needed to carry out the +% expansion. There is no need to re-apply \cs{cs_generate_variant:Nn} after +% changing the definition of the parent function: the variant will always +% use the current definition of the parent. Providing variants repeatedly is +% safe as \cs{cs_generate_variant:Nn} will only create new definitions if +% there is not already one available. +% +% Only |n|~and |N| arguments can be changed to other types. The only +% allowed changes are +% \begin{itemize} +% \item |c|~variant of an |N|~parent; +% \item |o|, |V|, |v|, |f|, |e|, or~|x| variant of an |n|~parent; +% \item |N|, |n|, |T|, |F|, or |p| argument unchanged. +% \end{itemize} +% This means the \meta{parent} of a \meta{variant} form is always +% unambiguous, even in cases where both an |n|-type parent and an +% |N|-type parent exist, such as for \cs{tl_count:n} and +% \cs{tl_count:N}. +% +% When creating variants for conditional functions, +% \cs{prg_generate_conditional_variant:Nnn} provides a convenient way +% of handling the related function set. +% +% For backward compatibility it is currently possible to make |n|, +% |o|, |V|, |v|, |f|, |e|, or |x|-type variants of an |N|-type argument or +% |N| or |c|-type variants of an |n|-type argument. Both are +% deprecated. The first because passing more than one token to an +% |N|-type argument will typically break the parent function's code. +% The second because programmers who use that most often want to +% access the value of a variable given its name, hence should use a +% |V|-type or |v|-type variant instead of |c|-type. In those cases, +% using the lower-level \cs{exp_args:No} or \cs{exp_args:Nc} +% functions explicitly is preferred to defining confusing variants. +% \end{function} +% +% \begin{function}[added = 2018-04-04, updated = 2019-02-08] +% {\exp_args_generate:n} +% \begin{syntax} +% \cs{exp_args_generate:n} \Arg{variant argument specifiers} +% \end{syntax} +% Defines \cs[no-index]{exp_args:N\meta{variant}} functions for each +% \meta{variant} given in the comma list \Arg{variant argument +% specifiers}. Each \meta{variant} should consist of the letters |N|, +% |c|, |n|, |V|, |v|, |o|, |f|, |e|, |x|, |p| and the resulting function is +% protected if the letter |x| appears in the \meta{variant}. This is +% only useful for cases where \cs{cs_generate_variant:Nn} is not +% applicable. +% \end{function} +% +% \section{Introducing the variants} +% +% The |V| type returns the value of a register, which can be one of +% |tl|, |clist|, |int|, |skip|, |dim|, |muskip|, or built-in \TeX{} +% registers. The |v| type is the same except it first creates a +% control sequence out of its argument before returning the +% value. +% +% In general, the programmer should not need to be concerned with +% expansion control. When simply using the content of a variable, +% functions with a |V| specifier should be used. For those referred to by +% (cs)name, the |v| specifier is available for the same purpose. Only when +% specific expansion steps are needed, such as when using delimited +% arguments, should the lower-level functions with |o| specifiers be employed. +% +% The |e| type expands all tokens fully, starting from the first. More +% precisely the expansion is identical to that of \TeX{}'s \tn{message} +% (in particular |#| needs not be doubled). It relies on the +% primitive \tn{expanded} hence is fast. +% +% The |x| type expands all tokens fully, starting from the first. In +% contrast to |e|, all macro parameter characters |#| must be doubled, +% and omitting this leads to low-level errors. In addition this type of +% expansion is not expandable, namely functions that have |x| in their +% signature do not themselves expand when appearing inside |e| or |x| +% expansion. +% +% The |f| type is so special that it deserves an example. It is +% typically used in contexts where only expandable commands are allowed. +% Then |x|-expansion cannot be used, and |f|-expansion provides an +% alternative that expands the front of the token list +% as much as can be done in such contexts. For +% instance, say that we want to evaluate the integer expression $3 + 4$ +% and pass the result $7$ as an argument to an expandable function +% |\example:n|. For this, one should define a variant using +% \cs{cs_generate_variant:Nn} |\example:n| |{| |f| |}|, then do +% \begin{quote} +% |\example:f { \int_eval:n { 3 + 4 } }| +% \end{quote} +% Note that |x|-expansion would also expand \cs{int_eval:n} fully to its +% result~$7$, but the variant |\example:x| cannot be expandable. Note +% also that |o|-expansion would not expand \cs{int_eval:n} fully to its +% result since that function requires several expansions. Besides the +% fact that |x|-expansion is protected rather than expandable, another +% difference between |f|-expansion and |x|-expansion is that +% |f|-expansion expands tokens from the beginning and stops as soon as a +% non-expandable token is encountered, while |x|-expansion continues +% expanding further tokens. Thus, for instance +% \begin{quote} +% |\example:f { \int_eval:n { 1 + 2 } , \int_eval:n { 3 + 4 } }| +% \end{quote} +% results in the call +% \begin{quote} +% |\example:n { 3 , \int_eval:n { 3 + 4 } }| +% \end{quote} +% while using |\example:x| or |\example:e| instead results in +% \begin{quote} +% |\example:n { 3 , 7 }| +% \end{quote} +% at the cost of being protected for |x|-type. +% If you use |f| type expansion in conditional processing then +% you should stick to using |TF| type functions only as the expansion +% does not finish any |\if... \fi:| itself! +% +% It is important to note that both \texttt{f}- and \texttt{o}-type +% expansion are concerned with the expansion of tokens from left to +% right in their arguments. In particular, \texttt{o}-type expansion +% applies to the first \emph{token} in the argument it receives: it +% is conceptually similar to +% \begin{verbatim} +% \exp_after:wN <base function> \exp_after:wN { <argument> } +% \end{verbatim} +% At the same time, \texttt{f}-type expansion stops at the \emph{first} +% non-expandable token. This means for example that both +% \begin{verbatim} +% \tl_set:No \l_tmpa_tl { { \g_tmpb_tl } } +% \end{verbatim} +% and +% \begin{verbatim} +% \tl_set:Nf \l_tmpa_tl { { \g_tmpb_tl } } +% \end{verbatim} +% leave |\g_tmpb_tl| unchanged: |{| is the first token in the +% argument and is non-expandable. +% +% It is usually best to keep the following in mind when using variant +% forms. +% \begin{itemize} +% \item +% Variants with |x|-type arguments (that are fully expanded before +% being passed to the |n|-type base function) are never expandable +% even when the base function is. Such variants cannot work +% correctly in arguments that are themselves subject to expansion. +% Consider using |f| or |e| expansion. +% \item +% In contrast, |e|~expansion (full expansion, almost like~|x| except +% for the treatment of~|#|) does not prevent variants from being +% expandable (if the base function is). +% \item +% Finally |f|~expansion only expands the front of the token list, +% stopping at the first non-expandable token. This may fail to +% fully expand the argument. +% \end{itemize} +% +% When speed is essential (for functions that do very little work and +% whose variants are used numerous times in a document) the following +% considerations apply because the speed of internal functions that +% expand the arguments of a base function depend on what needs doing +% with each argument and where this happens in the list of arguments: +% \begin{itemize} +% \item for fastest processing any |c|-type arguments should come first +% followed by all other modified arguments; +% \item unchanged |N|-type args that appear before modified ones have +% a small performance hit; +% \item unchanged |n|-type args that appear before modified ones have +% a relative larger performance hit. +% \end{itemize} +% +% \section{Manipulating the first argument} +% +% These functions are described in detail: expansion of multiple tokens follows +% the same rules but is described in a shorter fashion. +% +% \begin{function}[EXP]{\exp_args:Nc, \exp_args:cc} +% \begin{syntax} +% \cs{exp_args:Nc} \meta{function} \Arg{tokens} +% \end{syntax} +% This function absorbs two arguments (the \meta{function} name and +% the \meta{tokens}). The \meta{tokens} are expanded until only characters +% remain, and are then turned into a control sequence. +% The result is inserted into the input stream \emph{after} reinsertion +% of the \meta{function}. Thus the \meta{function} may take more than +% one argument: all others are left unchanged. +% +% The |:cc| variant constructs the \meta{function} name in the same +% manner as described for the \meta{tokens}. +% \end{function} +% +% \begin{function}[EXP]{\exp_args:No} +% \begin{syntax} +% \cs{exp_args:No} \meta{function} \Arg{tokens} ... +% \end{syntax} +% This function absorbs two arguments (the \meta{function} name and +% the \meta{tokens}). The \meta{tokens} are expanded once, and the result +% is inserted in braces into the input stream \emph{after} reinsertion +% of the \meta{function}. Thus the \meta{function} may take more than +% one argument: all others are left unchanged. +% \end{function} +% +% \begin{function}[EXP]{\exp_args:NV} +% \begin{syntax} +% \cs{exp_args:NV} \meta{function} \meta{variable} +% \end{syntax} +% This function absorbs two arguments (the names of the \meta{function} and +% the \meta{variable}). The content of the \meta{variable} are recovered +% and placed inside braces into the input stream \emph{after} reinsertion +% of the \meta{function}. Thus the \meta{function} may take more than +% one argument: all others are left unchanged. +% \end{function} +% +% \begin{function}[EXP]{\exp_args:Nv} +% \begin{syntax} +% \cs{exp_args:Nv} \meta{function} \Arg{tokens} +% \end{syntax} +% This function absorbs two arguments (the \meta{function} name and +% the \meta{tokens}). The \meta{tokens} are expanded until only characters +% remain, and are then turned into a control sequence. +% This control sequence should +% be the name of a \meta{variable}. The content of the \meta{variable} are +% recovered and placed inside braces into the input stream \emph{after} +% reinsertion of the \meta{function}. Thus the \meta{function} may take more +% than one argument: all others are left unchanged. +% \end{function} +% +% \begin{function}[EXP, added = 2018-05-15]{\exp_args:Ne} +% \begin{syntax} +% \cs{exp_args:Ne} \meta{function} \Arg{tokens} +% \end{syntax} +% This function absorbs two arguments (the \meta{function} name and +% the \meta{tokens}) and exhaustively expands the \meta{tokens}. +% The result is inserted in braces into the input stream +% \emph{after} reinsertion of the \meta{function}. +% Thus the \meta{function} may take more +% than one argument: all others are left unchanged. +% \end{function} +% +% \begin{function}[EXP]{\exp_args:Nf} +% \begin{syntax} +% \cs{exp_args:Nf} \meta{function} \Arg{tokens} +% \end{syntax} +% This function absorbs two arguments (the \meta{function} name and +% the \meta{tokens}). The \meta{tokens} are fully expanded until the +% first non-expandable token is found (if that is a space it is +% removed), and the result +% is inserted in braces into the input stream \emph{after} reinsertion +% of the \meta{function}. Thus the \meta{function} may take more than +% one argument: all others are left unchanged. +% \end{function} +% +% \begin{function}{\exp_args:Nx} +% \begin{syntax} +% \cs{exp_args:Nx} \meta{function} \Arg{tokens} +% \end{syntax} +% This function absorbs two arguments (the \meta{function} name and +% the \meta{tokens}) and exhaustively expands the \meta{tokens}. +% The result is inserted in braces into the input stream +% \emph{after} reinsertion of the \meta{function}. +% Thus the \meta{function} may take more +% than one argument: all others are left unchanged. +% \end{function} +% +% \section{Manipulating two arguments} +% +% \begin{function}[EXP, updated = 2018-05-15] +% { +% \exp_args:NNc, +% \exp_args:NNo, +% \exp_args:NNV, +% \exp_args:NNv, +% \exp_args:NNe, +% \exp_args:NNf, +% \exp_args:Ncc, +% \exp_args:Nco, +% \exp_args:NcV, +% \exp_args:Ncv, +% \exp_args:Ncf, +% \exp_args:NVV +% } +% \begin{syntax} +% \cs{exp_args:NNc} \meta{token_1} \meta{token_2} \Arg{tokens} +% \end{syntax} +% These optimized functions absorb three arguments and expand the second and +% third as detailed by their argument specifier. The first argument +% of the function is then the next item on the input stream, followed +% by the expansion of the second and third arguments. +% \end{function} +% +% \begin{function}[EXP, updated = 2018-05-15] +% { +% \exp_args:Nnc, +% \exp_args:Nno, +% \exp_args:NnV, +% \exp_args:Nnv, +% \exp_args:Nne, +% \exp_args:Nnf, +% \exp_args:Noc, +% \exp_args:Noo, +% \exp_args:Nof, +% \exp_args:NVo, +% \exp_args:Nfo, +% \exp_args:Nff, +% \exp_args:Nee, +% } +% \begin{syntax} +% \cs{exp_args:Noo} \meta{token} \Arg{tokens_1} \Arg{tokens_2} +% \end{syntax} +% These functions absorb three arguments and expand the second and +% third as detailed by their argument specifier. The first argument +% of the function is then the next item on the input stream, followed +% by the expansion of the second and third arguments. +% \end{function} +% +% \begin{function} +% { +% \exp_args:NNx, +% \exp_args:Ncx, +% \exp_args:Nnx, +% \exp_args:Nox, +% \exp_args:Nxo, +% \exp_args:Nxx +% } +% \begin{syntax} +% \cs{exp_args:NNx} \meta{token_1} \meta{token_2} \Arg{tokens} +% \end{syntax} +% These functions absorb three arguments and expand the second and +% third as detailed by their argument specifier. The first argument +% of the function is then the next item on the input stream, followed +% by the expansion of the second and third arguments. These functions +% are not expandable due to their |x|-type argument. +% \end{function} +% +% \section{Manipulating three arguments} +% +% \begin{function}[EXP] +% { +% \exp_args:NNNo, +% \exp_args:NNNV, +% \exp_args:NNNv, +% \exp_args:NNNe, +% \exp_args:Nccc, +% \exp_args:NcNc, +% \exp_args:NcNo, +% \exp_args:Ncco +% } +% \begin{syntax} +% \cs{exp_args:NNNo} \meta{token_1} \meta{token_2} \meta{token_3} \Arg{tokens} +% \end{syntax} +% These optimized functions absorb four arguments and expand the second, third +% and fourth as detailed by their argument specifier. The first +% argument of the function is then the next item on the input stream, +% followed by the expansion of the second argument, \emph{etc}. +% \end{function} +% +% \begin{function}[EXP] +% { +% \exp_args:NNcf, +% \exp_args:NNno, +% \exp_args:NNnV, +% \exp_args:NNoo, +% \exp_args:NNVV, +% \exp_args:Ncno, +% \exp_args:NcnV, +% \exp_args:Ncoo, +% \exp_args:NcVV, +% \exp_args:Nnnc, +% \exp_args:Nnno, +% \exp_args:Nnnf, +% \exp_args:Nnff, +% \exp_args:Nooo, +% \exp_args:Noof, +% \exp_args:Nffo, +% \exp_args:Neee +% } +% \begin{syntax} +% \cs{exp_args:NNoo} \meta{token_1} \meta{token_2} \Arg{token_3} \Arg{tokens} +% \end{syntax} +% These functions absorb four arguments and expand the second, third +% and fourth as detailed by their argument specifier. The first +% argument of the function is then the next item on the input stream, +% followed by the expansion of the second argument, \emph{etc}. +% \end{function} +% +% \begin{function}[added = 2015-08-12] +% { +% \exp_args:NNNx, +% \exp_args:NNnx, +% \exp_args:NNox, +% \exp_args:Nccx, +% \exp_args:Ncnx, +% \exp_args:Nnnx, +% \exp_args:Nnox, +% \exp_args:Noox, +% } +% \begin{syntax} +% \cs{exp_args:NNnx} \meta{token_1} \meta{token_2} \Arg{tokens_1} \Arg{tokens_2} +% \end{syntax} +% These functions absorb four arguments and expand the second, third +% and fourth as detailed by their argument specifier. The first +% argument of the function is then the next item on the input stream, +% followed by the expansion of the second argument, \emph{etc.} +% \end{function} +% +% \section{Unbraced expansion} +% +% \begin{function}[EXP, updated = 2018-05-15] +% { +% \exp_last_unbraced:No, +% \exp_last_unbraced:NV, +% \exp_last_unbraced:Nv, +% \exp_last_unbraced:Ne, +% \exp_last_unbraced:Nf, +% \exp_last_unbraced:NNo, +% \exp_last_unbraced:NNV, +% \exp_last_unbraced:NNf, +% \exp_last_unbraced:Nco, +% \exp_last_unbraced:NcV, +% \exp_last_unbraced:Nno, +% \exp_last_unbraced:Nnf, +% \exp_last_unbraced:Noo, +% \exp_last_unbraced:Nfo, +% \exp_last_unbraced:NNNo, +% \exp_last_unbraced:NNNV, +% \exp_last_unbraced:NNNf, +% \exp_last_unbraced:NnNo, +% \exp_last_unbraced:NNNNo, +% \exp_last_unbraced:NNNNf, +% } +% \begin{syntax} +% \cs{exp_last_unbraced:Nno} \meta{token} \Arg{tokens_1} \Arg{tokens_2} +% \end{syntax} +% These functions absorb the number of arguments given by their +% specification, carry out the expansion +% indicated and leave the results in the input stream, with the +% last argument not surrounded by the usual braces. +% Of these, the |:Nno|, |:Noo|, |:Nfo| and |:NnNo| +% variants need slower processing. +% \begin{texnote} +% As an optimization, the last argument is unbraced by some +% of those functions before expansion. This can cause problems +% if the argument is empty: for instance, +% \cs{exp_last_unbraced:Nf} |\foo_bar:w| |{ }| \cs{q_stop} +% leads to an infinite loop, as the quark is \texttt{f}-expanded. +% \end{texnote} +% \end{function} +% +% \begin{function}{\exp_last_unbraced:Nx} +% \begin{syntax} +% \cs{exp_last_unbraced:Nx} \meta{function} \Arg{tokens} +% \end{syntax} +% This function fully expands the \meta{tokens} and leaves the result +% in the input stream after reinsertion of the \meta{function}. +% This function is not expandable. +% \end{function} +% +% \begin{function}[EXP]{\exp_last_two_unbraced:Noo} +% \begin{syntax} +% \cs{exp_last_two_unbraced:Noo} \meta{token} \Arg{tokens_1} \Arg{tokens_2} +% \end{syntax} +% This function absorbs three arguments and expands the second and third +% once. The first argument of the function is then the next item on the +% input stream, followed by the expansion of the second and third arguments, +% which are not wrapped in braces. +% This function needs special (slower) processing. +% \end{function} +% +% \begin{function}[EXP]{\exp_after:wN} +% \begin{syntax} +% \cs{exp_after:wN} \meta{token_1} \meta{token_2} +% \end{syntax} +% Carries out a single expansion of \meta{token_2} (which may consume +% arguments) prior to the expansion of \meta{token_1}. If \meta{token_2} has +% no expansion (for example, if it is a character) then it is left +% unchanged. It is important to notice that \meta{token_1} may be +% \emph{any} single token, including group-opening and -closing +% tokens (|{| or |}| assuming normal \TeX{} category codes). Unless +% specifically required this should be avoided: expansion should be carried out using an +% appropriate argument specifier variant or the appropriate +% \cs[no-index]{exp_args:N\meta{variant}} function. +% \begin{texnote} +% This is the \TeX{} primitive \tn{expandafter}. +% \end{texnote} +% \end{function} +% +% +% \section{Preventing expansion} +% +% Despite the fact that the following functions are all about preventing +% expansion, they're designed to be used in an expandable context and hence +% are all marked as being `expandable' since they themselves disappear +% after the expansion has completed. +% +% \begin{function}[EXP]{\exp_not:N} +% \begin{syntax} +% \cs{exp_not:N} \meta{token} +% \end{syntax} +% Prevents expansion of the \meta{token} in a context where it would +% otherwise be expanded, for example an |e|-type or |x|-type argument or +% the first token in an |o|-type or |f|-type argument. +% \begin{texnote} +% This is the \TeX{} primitive \tn{noexpand}. It only prevents +% expansion. At the beginning of an |f|-type argument, a space +% \meta{token} is removed even if it appears as \cs{exp_not:N} +% \cs{c_space_token}. In an |e|-expanding definition +% (\cs{cs_new:Npe}), a macro parameter introduces an argument even +% if it appears as \cs{exp_not:N} |#| |1|. This differs from +% \cs{exp_not:n}. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\exp_not:c} +% \begin{syntax} +% \cs{exp_not:c} \Arg{tokens} +% \end{syntax} +% Expands the \meta{tokens} until only characters remain, and then +% converts this into a control sequence. +% Further expansion of this control sequence is then inhibited using +% \cs{exp_not:N}. +% \end{function} +% +% \begin{function}[EXP]{\exp_not:n} +% \begin{syntax} +% \cs{exp_not:n} \Arg{tokens} +% \end{syntax} +% Prevents expansion of the \meta{tokens} in an |e|-type or |x|-type argument. +% In all other cases the \meta{tokens} continue to be expanded, for +% example in the input stream or in other types of arguments such as +% \texttt{c}, \texttt{f}, \texttt{v}. The argument of \cs{exp_not:n} +% \emph{must} be surrounded by braces. +% \begin{texnote} +% This is the \eTeX{} primitive \tn{unexpanded}. In an +% |e|-expanding definition (\cs{cs_new:Npe}), \cs{exp_not:n}~|{#1}| +% is equivalent to |##1| rather than to~|#1|, namely it inserts the +% two characters |#| and~|1|, and +% \cs{exp_not:n}~|{#}| is equivalent to |#|, namely it inserts the +% character~|#|. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\exp_not:o} +% \begin{syntax} +% \cs{exp_not:o} \Arg{tokens} +% \end{syntax} +% Expands the \meta{tokens} once, then prevents any further expansion +% in |e|-type or |x|-type arguments using \cs{exp_not:n}. +% \end{function} +% +% \begin{function}[EXP]{\exp_not:V} +% \begin{syntax} +% \cs{exp_not:V} \meta{variable} +% \end{syntax} +% Recovers the content of the \meta{variable}, then prevents expansion +% of this material in |e|-type or |x|-type arguments using \cs{exp_not:n}. +% \end{function} +% +% \begin{function}[EXP]{\exp_not:v} +% \begin{syntax} +% \cs{exp_not:v} \Arg{tokens} +% \end{syntax} +% Expands the \meta{tokens} until only characters remains, and then +% converts this into a control sequence which should be a \meta{variable} +% name. +% The content of the \meta{variable} is recovered, and further +% expansion in |e|-type or |x|-type arguments is prevented using \cs{exp_not:n}. +% \end{function} +% +% \begin{function}[EXP]{\exp_not:e} +% \begin{syntax} +% \cs{exp_not:e} \Arg{tokens} +% \end{syntax} +% Expands \meta{tokens} exhaustively, then protects the result of the +% expansion (including any tokens which were not expanded) from +% further expansion in |e|-type or |x|-type arguments using \cs{exp_not:n}. +% This is very rarely useful but is provided for consistency. +% \end{function} +% +% \begin{function}[EXP]{\exp_not:f} +% \begin{syntax} +% \cs{exp_not:f} \Arg{tokens} +% \end{syntax} +% Expands \meta{tokens} fully until the first unexpandable token is +% found (if it is a space it is removed). Expansion then stops, and +% the result of the expansion (including any tokens which were not +% expanded) is protected from further expansion in |e|-type or |x|-type arguments +% using \cs{exp_not:n}. +% \end{function} +% +% \begin{function}[updated = 2011-06-03, EXP]{\exp_stop_f:} +% \begin{syntax} +% |\foo_bar:f| \{ \meta{tokens} \cs{exp_stop_f:} \meta{more tokens} \} +% \end{syntax} +% This function terminates an \texttt{f}-type expansion. Thus if +% a function |\foo_bar:f| starts an \texttt{f}-type expansion +% and all of \meta{tokens} are expandable \cs{exp_stop_f:} +% terminates the expansion of tokens even if \meta{more tokens} +% are also expandable. The function itself is an implicit space +% token. Inside an \texttt{e}-type or \texttt{x}-type expansion, it retains its +% form, but when typeset it produces the underlying space (\verb*| |). +% \end{function} +% +% +% \section{Controlled expansion} +% +% The \pkg{expl3} language makes all efforts to hide the complexity of +% \TeX{} expansion from the programmer by providing concepts that +% evaluate/expand arguments of functions prior to calling the \enquote{base} +% functions. Thus, instead of using many \tn{expandafter} calls and +% other trickery it is usually a matter of choosing the right variant +% of a function to achieve a desired result. +% +% Of course, deep down \TeX{} is using expansion as always and there +% are cases where a programmer needs to control that expansion +% directly; typical situations are basic data manipulation tools. This +% section documents the functions for that level. These +% commands are used throughout the kernel code, but we hope that outside +% the kernel there will be little need to resort to them. Instead the +% argument manipulation methods document above should usually be sufficient. +% +% While \cs{exp_after:wN} expands one token (out of order) it is +% sometimes necessary to expand several tokens in one go. The next set +% of commands provide this functionality. Be aware that it is +% absolutely required that the programmer has full control over the +% tokens to be expanded, i.e., it is not possible to use these +% functions to expand unknown input as part of +% \meta{expandable-tokens} as that will break badly if unexpandable +% tokens are encountered in that place! +% +% \begin{function}[added=2015-08-23,EXP] +% { +% \exp:w , +% \exp_end: +% } +% \begin{syntax} +% \cs{exp:w} \meta{expandable tokens} \cs{exp_end:} \\ +% \end{syntax} +% Expands \meta{expandable-tokens} until reaching \cs{exp_end:} at +% which point expansion stops. +% The full expansion of \meta{expandable tokens} has to be empty. +% If any token in \meta{expandable tokens} or any token generated by +% expanding the tokens therein is not expandable the expansion will end +% prematurely and as a result \cs{exp_end:} will be misinterpreted +% later on.\footnotemark +% +% In typical use cases the \cs{exp_end:} is hidden somewhere +% in the replacement text of \meta{expandable-tokens} rather than +% being on the same expansion level than \cs{exp:w}, e.g., you may +% see code such as +%\begin{verbatim} +% \exp:w \@@_case:NnTF #1 {#2} { } { } +%\end{verbatim} +% where somewhere during the expansion of |\@@_case:NnTF| the +% \cs{exp_end:} gets generated. +% \begin{texnote} +% The current implementation uses \tn{romannumeral} hence ignores +% space tokens and explicit signs |+| and |-| in the expansion of the +% \meta{expandable tokens}, but this should not be relied upon. +% \end{texnote} +% \end{function} +% \footnotetext{Due to the implementation you might get the character +% in position 0 in the current font (typically \enquote{\texttt{`}}) +% in the output without any error message!} +% +% \begin{function}[added=2015-08-23, EXP, label = \exp_end_continue_f:w] +% { +% \exp:w , +% \exp_end_continue_f:w +% } +% \begin{syntax} +% \cs{exp:w} \meta{expandable-tokens} \cs{exp_end_continue_f:w} \meta{further-tokens} +% \end{syntax} +% Expands \meta{expandable-tokens} until reaching \cs{exp_end_continue_f:w} at +% which point expansion continues as an \texttt{f}-type expansion expanding +% \meta{further-tokens} until an unexpandable token is encountered (or +% the \texttt{f}-type expansion is explicitly terminated by +% \cs{exp_stop_f:}). As with all \texttt{f}-type expansions a space ending +% the expansion gets removed. +% +% The full expansion of \meta{expandable-tokens} has to be empty. +% If any token in \meta{expandable-tokens} or any token generated by +% expanding the tokens therein is not expandable the expansion will end +% prematurely and as a result \cs{exp_end_continue_f:w} will be misinterpreted +% later on.\footnotemark +% +% +% In typical use cases \meta{expandable-tokens} contains no tokens at all, +% e.g., you will see code such as +%\begin{verbatim} +% \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } +%\end{verbatim} +% where the \cs{exp_after:wN} triggers an \texttt{f}-expansion of the tokens +% in |#2|. For technical reasons this has to happen using two tokens +% (if they would be hidden inside another command \cs{exp_after:wN} +% would only expand the command but not trigger any additional +% |f|-expansion). +% +% You might wonder why there are two different approaches available, +% after all the effect of +% \begin{quote} +% \cs{exp:w} \meta{expandable-tokens} \cs{exp_end:} +% \end{quote} +% can be alternatively achieved through an \texttt{f}-type expansion by using +% \cs{exp_stop_f:}, i.e. +% \begin{quote} +% \cs{exp:w} \cs{exp_end_continue_f:w} \meta{expandable-tokens} \cs{exp_stop_f:} +% \end{quote} +% The reason is simply that the first approach is slightly faster +% (one less token to parse and less expansion internally) +% so in places where such performance really matters and where we +% want to explicitly stop the expansion at a defined point the first +% form is preferable. +% \end{function} +% \footnotetext{In this particular case you may get a character into +% the output as well as an error message.} +% +% \begin{function}[added=2015-08-23, EXP, label = \exp_end_continue_f:nw] +% { +% \exp:w , +% \exp_end_continue_f:nw +% } +% \begin{syntax} +% \cs{exp:w} \meta{expandable-tokens} \cs{exp_end_continue_f:nw} \meta{further-tokens} +% \end{syntax} +% The difference to \cs{exp_end_continue_f:w} is that we first we pick +% up an argument which is then returned to the input stream. If +% \meta{further-tokens} starts with space tokens then these space +% tokens are removed while searching for the argument. If it starts +% with a brace group then the braces are removed. Thus such spaces or +% braces will not terminate the \texttt{f}-type expansion. +% \end{function} +% +% \section{Internal functions} +% +% \begin{function}{\::n, \::N, \::p, \::c, \::o, \::e, \::f, \::x, \::v, \::V, \:::} +% \begin{syntax} +% |\cs_new:Npn \exp_args:Ncof { \::c \::o \::f \::: }| +% \end{syntax} +% Internal forms for the base expansion types. These names do \emph{not} +% conform to the general \LaTeX3 approach as this makes them more readily +% visible in the log and so forth. They should not be used outside this module. +% \end{function} +% +% \begin{function} +% {\::o_unbraced, \::e_unbraced, \::f_unbraced, \::x_unbraced, \::v_unbraced, \::V_unbraced} +% \begin{syntax} +% |\cs_new:Npn \exp_last_unbraced:Nno { \::n \::o_unbraced \::: }| +% \end{syntax} +% Internal forms for the expansion types which leave the terminal argument +% unbraced. These names do \emph{not} +% conform to the general \LaTeX3 approach as this makes them more readily +% visible in the log and so forth. They should not be used outside this module. +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3expan} implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=exp> +% \end{macrocode} +% +% \begin{variable}{\l_@@_internal_tl} +% The |\exp_| module has its private variable to temporarily store the +% result of |x|-type argument expansion. This is done to avoid interference +% with other functions using temporary variables. +% \end{variable} +% +% \begin{macro}{\exp_after:wN} +% \begin{macro}{\exp_not:N} +% \begin{macro}{\exp_not:n} +% These are defined in \pkg{l3basics}, as they are needed +% \enquote{early}. This is just a reminder of that fact! +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{General expansion} +% +% In this section a general mechanism for defining functions that handle +% arguments is defined. These general expansion functions are +% expandable unless |x| is used. (Any version of |x| is going to have +% to use one of the \LaTeX3 names for \cs{cs_set:Npx} at some +% point, and so is never going to be expandable.) +% +% The definition of expansion functions with this technique happens +% in section~\ref{sec:l3expan:gendef}. +% In section~\ref{sec:l3expan:handtune} some common cases are coded by a more direct +% method for efficiency, typically using calls to \cs{exp_after:wN}. +% +% \begin{variable}{\l_@@_internal_tl} +% This scratch token list variable is defined in \pkg{l3basics}. +% \end{variable} +% +% This code uses internal functions with names that start with |\::| to +% perform the expansions. All macros are |long| since the tokens +% undergoing expansion may be arbitrary user input. +% +% An argument manipulator |\::|\meta{Z} always has signature |#1\:::#2#3| +% where |#1| holds the remaining argument manipulations to be performed, +% \cs{:::} serves as an end marker for the list of manipulations, |#2| +% is the carried over result of the previous expansion steps and |#3| is +% the argument about to be processed. +% One exception to this rule is \cs{::p}, which has to grab an argument +% delimited by a left brace. +% +% \begin{macro}[EXP]{\@@_arg_next:nnn} +% \begin{macro}[EXP]{\@@_arg_next:Nnn} +% |#1| is the result of an expansion step, |#2| is the remaining +% argument manipulations and |#3| is the current result of the +% expansion chain. This auxiliary function moves |#1| back after +% |#3| in the input stream and checks if any expansion is left to +% be done by calling |#2|. In by far the most cases we need +% to add a set of braces to the result of an argument manipulation +% so it is more effective to do it directly here. Actually, so far +% only the |c| of the final argument manipulation variants does not +% require a set of braces. +% \begin{macrocode} +\cs_new:Npn \@@_arg_next:nnn #1#2#3 { #2 \::: { #3 {#1} } } +\cs_new:Npn \@@_arg_next:Nnn #1#2#3 { #2 \::: { #3 #1 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\:::} +% The end marker is just another name for the identity function. +% \begin{macrocode} +\cs_new:Npn \::: #1 {#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::n} +% This function is used to skip an argument that doesn't need to +% be expanded. +% \begin{macrocode} +\cs_new:Npn \::n #1 \::: #2#3 { #1 \::: { #2 {#3} } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::N} +% This function is used to skip an argument that consists of a +% single token and doesn't need to be expanded. +% \begin{macrocode} +\cs_new:Npn \::N #1 \::: #2#3 { #1 \::: {#2#3} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::p} +% This function is used to skip an argument that is delimited by a +% left brace and doesn't need to be expanded. It is not +% wrapped in braces in the result. +% \begin{macrocode} +\cs_new:Npn \::p #1 \::: #2#3# { #1 \::: {#2#3} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::c} +% This function is used to skip an argument that is turned into +% a control sequence without expansion. +% \begin{macrocode} +\cs_new:Npn \::c #1 \::: #2#3 + { \exp_after:wN \@@_arg_next:Nnn \cs:w #3 \cs_end: {#1} {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::o} +% This function is used to expand an argument once. +% \begin{macrocode} +\cs_new:Npn \::o #1 \::: #2#3 + { \exp_after:wN \@@_arg_next:nnn \exp_after:wN {#3} {#1} {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::e} +% With the \tn{expanded} primitive available, just expand. +% \begin{macrocode} +\cs_new:Npn \::e #1 \::: #2#3 + { \tex_expanded:D { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::f} +% \begin{macro}{\exp_stop_f:} +% This function is used to expand a token list until the first +% unexpandable token is found. This is achieved through \cs{exp:w} +% \cs{exp_end_continue_f:w} that expands everything in its way +% following it. This scanning procedure is terminated once the +% expansion hits something non-expandable (if that is a space it is +% removed). We introduce \cs{exp_stop_f:} to mark such an +% end-of-expansion marker. For example, |f|-expanding +% |\cs_set_eq:Nc \aaa { b \l_tmpa_tl b }| where |\l_tmpa_tl| contains +% the characters |lur| gives |\tex_let:D \aaa = \blurb| which then +% turns out to start with the non-expandable token |\tex_let:D|. +% Since the expansion of \cs{exp:w} \cs{exp_end_continue_f:w} is +% empty, we wind up with a fully expanded list, only \TeX{} has not +% tried to execute any of the non-expandable tokens. This is what +% differentiates this function from the |e| and |x| argument type. +% \begin{macrocode} +\cs_new:Npn \::f #1 \::: #2#3 + { + \exp_after:wN \@@_arg_next:nnn + \exp_after:wN { \exp:w \exp_end_continue_f:w #3 } + {#1} {#2} + } +\use:nn { \cs_new_eq:NN \exp_stop_f: } { ~ } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\::x} +% This function is used to expand an argument fully. +% We build in the expansion of \cs{@@_arg_next:nnn}. +% \begin{macrocode} +\cs_new_protected:Npn \::x #1 \::: #2#3 + { + \cs_set_nopar:Npe \l_@@_internal_tl + { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } } + \l_@@_internal_tl + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\::v} +% \begin{macro}[EXP]{\::V} +% These functions return the value of a register, i.e., one of +% |tl|, |clist|, |int|, |skip|, |dim|, |muskip|, or built-in +% \TeX{} register. The |V| version +% expects a single token whereas |v| like |c| creates a csname from +% its argument given in braces and then evaluates it as if it was a +% |V|. The \cs{exp:w} sets off an expansion +% similar to an |f|-type expansion, which we terminate using +% \cs{exp_end:}. The argument is returned in braces. +% \begin{macrocode} +\cs_new:Npn \::V #1 \::: #2#3 + { + \exp_after:wN \@@_arg_next:nnn + \exp_after:wN { \exp:w \@@_eval_register:N #3 } + {#1} {#2} +} +\cs_new:Npn \::v #1 \::: #2#3 + { + \exp_after:wN \@@_arg_next:nnn + \exp_after:wN { \exp:w \@@_eval_register:c {#3} } + {#1} {#2} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_eval_register:N, \@@_eval_register:c} +% \begin{macro}[EXP]{\@@_eval_error_msg:w} +% This function evaluates a register. Now a register might exist as +% one of two things: A parameter-less macro or a built-in \TeX{} +% register such as |\count|. For the \TeX{} registers we have to +% utilize a \tn{the} whereas for the macros we merely have to +% expand them once. The trick is to find out when to use +% \tn{the} and when not to. What we want here is to find out +% whether the token expands to something else when hit with +% \cs{exp_after:wN}. The technique is to compare the meaning of the +% token in question when it has been prefixed with \cs{exp_not:N} +% and the token itself. If it is a macro, the prefixed +% \cs{exp_not:N} temporarily turns it into the primitive +% \cs{scan_stop:}. +% \begin{macrocode} +\cs_new:Npn \@@_eval_register:N #1 + { + \exp_after:wN \if_meaning:w \exp_not:N #1 #1 +% \end{macrocode} +% If the token was not a macro it may be a malformed variable from a +% |c| expansion in which case it is equal to the primitive +% \cs{scan_stop:}. In that case we throw an error. We could let \TeX{} +% do it for us but that would result in the rather obscure +% \begin{quote} +% |! You can't use `\relax' after \the.| +% \end{quote} +% which while quite true doesn't give many hints as to what actually +% went wrong. We provide something more sensible. +% \begin{macrocode} + \if_meaning:w \scan_stop: #1 + \@@_eval_error_msg:w + \fi: +% \end{macrocode} +% The next bit requires some explanation. The function must be +% initiated by \cs{exp:w} and we want to +% terminate this expansion chain by inserting the \cs{exp_end:} +% token. +% However, we have to expand the register |#1| before we do +% that. If it is a \TeX{} register, we need to execute the sequence +% |\exp_after:wN \exp_end: \tex_the:D #1| and if it is a macro we +% need to execute |\exp_after:wN \exp_end: #1|. We therefore issue +% the longer of the two sequences and if the register is a macro, we +% remove the \cs{tex_the:D}. +% \begin{macrocode} + \else: + \exp_after:wN \use_i_ii:nnn + \fi: + \exp_after:wN \exp_end: \tex_the:D #1 + } +\cs_new:Npn \@@_eval_register:c #1 + { \exp_after:wN \@@_eval_register:N \cs:w #1 \cs_end: } +% \end{macrocode} +% Clean up nicely, then call the undefined control sequence. The +% result is an error message looking like this: +% \begin{verbatim} +% ! Undefined control sequence. +% <argument> \LaTeX3 error: +% Erroneous variable used! +% l.55 \tl_set:Nv \l_tmpa_tl {undefined_tl} +% \end{verbatim} +% \begin{macrocode} +\cs_new:Npn \@@_eval_error_msg:w #1 \tex_the:D #2 + { + \fi: + \fi: + \msg_expandable_error:nnn { kernel } { bad-variable } {#2} + \exp_end: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Hand-tuned definitions} +% \label{sec:l3expan:handtune} +% +% One of the most important features of these functions is that they +% are fully expandable. +% +% \begin{macro}[EXP]{\exp_args:Nc, \exp_args:cc} +% In \pkg{l3basics}. +% \end{macro} +% +% \begin{macro}[EXP]{\exp_args:NNc, \exp_args:Ncc, \exp_args:Nccc} +% Here are the functions that turn their argument into csnames but are +% expandable. +% \begin{macrocode} +\cs_new:Npn \exp_args:NNc #1#2#3 + { \exp_after:wN #1 \exp_after:wN #2 \cs:w # 3\cs_end: } +\cs_new:Npn \exp_args:Ncc #1#2#3 + { \exp_after:wN #1 \cs:w #2 \exp_after:wN \cs_end: \cs:w #3 \cs_end: } +\cs_new:Npn \exp_args:Nccc #1#2#3#4 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \cs:w #3 \exp_after:wN \cs_end: + \cs:w #4 \cs_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\exp_args:No} +% \begin{macro}[EXP]{\exp_args:NNo} +% \begin{macro}[EXP]{\exp_args:NNNo} +% Those lovely runs of expansion! +% \begin{macrocode} +\cs_new:Npn \exp_args:No #1#2 { \exp_after:wN #1 \exp_after:wN {#2} } +\cs_new:Npn \exp_args:NNo #1#2#3 + { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN {#3} } +\cs_new:Npn \exp_args:NNNo #1#2#3#4 + { \exp_after:wN #1 \exp_after:wN#2 \exp_after:wN #3 \exp_after:wN {#4} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\exp_args:Ne} +% When the \tn{expanded} primitive is available, use it. +% \begin{macrocode} +\cs_new:Npn \exp_args:Ne #1#2 + { \exp_after:wN #1 \tex_expanded:D { {#2} } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\exp_args:Nf, \exp_args:NV, \exp_args:Nv} +% \begin{macrocode} +\cs_new:Npn \exp_args:Nf #1#2 + { \exp_after:wN #1 \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } } +\cs_new:Npn \exp_args:Nv #1#2 + { + \exp_after:wN #1 \exp_after:wN + { \exp:w \@@_eval_register:c {#2} } + } +\cs_new:Npn \exp_args:NV #1#2 + { + \exp_after:wN #1 \exp_after:wN + { \exp:w \@@_eval_register:N #2 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP] +% { +% \exp_args:NNV, \exp_args:NNv, \exp_args:NNe, \exp_args:NNf, +% \exp_args:Nco, \exp_args:NcV, \exp_args:Ncv, \exp_args:Ncf, +% \exp_args:NVV, +% } +% Some more hand-tuned function with three arguments. +% If we forced that an |o| argument always has braces, +% we could implement \cs{exp_args:Nco} with less tokens +% and only two arguments. +% \begin{macrocode} +\cs_new:Npn \exp_args:NNV #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN { \exp:w \@@_eval_register:N #3 } + } +\cs_new:Npn \exp_args:NNv #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN { \exp:w \@@_eval_register:c {#3} } + } +\cs_new:Npn \exp_args:NNe #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN #2 + \tex_expanded:D { {#3} } + } +\cs_new:Npn \exp_args:NNf #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN { \exp:w \exp_end_continue_f:w #3 } + } +\cs_new:Npn \exp_args:Nco #1#2#3 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp_after:wN {#3} + } +\cs_new:Npn \exp_args:NcV #1#2#3 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp_after:wN { \exp:w \@@_eval_register:N #3 } + } +\cs_new:Npn \exp_args:Ncv #1#2#3 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp_after:wN { \exp:w \@@_eval_register:c {#3} } + } +\cs_new:Npn \exp_args:Ncf #1#2#3 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp_after:wN { \exp:w \exp_end_continue_f:w #3 } + } +\cs_new:Npn \exp_args:NVV #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN { \exp:w \exp_after:wN + \@@_eval_register:N \exp_after:wN #2 \exp_after:wN } + \exp_after:wN { \exp:w \@@_eval_register:N #3 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP] +% { +% \exp_args:NNNV, \exp_args:NNNv, \exp_args:NNNe, +% \exp_args:NcNc, \exp_args:NcNo, +% \exp_args:Ncco, +% } +% A few more that we can hand-tune. +% \begin{macrocode} +\cs_new:Npn \exp_args:NNNV #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN #3 + \exp_after:wN { \exp:w \@@_eval_register:N #4 } + } +\cs_new:Npn \exp_args:NNNv #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN #3 + \exp_after:wN { \exp:w \@@_eval_register:c {#4} } + } +\cs_new:Npn \exp_args:NNNe #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN #3 + \tex_expanded:D { {#4} } + } +\cs_new:Npn \exp_args:NcNc #1#2#3#4 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp_after:wN #3 + \cs:w #4 \cs_end: + } +\cs_new:Npn \exp_args:NcNo #1#2#3#4 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp_after:wN #3 + \exp_after:wN {#4} + } +\cs_new:Npn \exp_args:Ncco #1#2#3#4 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \cs:w #3 \exp_after:wN \cs_end: + \exp_after:wN {#4} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\exp_args:Nx} +% \begin{macrocode} +\cs_new_protected:Npn \exp_args:Nx #1#2 + { \use:x { \exp_not:N #1 {#2} } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Last-unbraced versions} +% +% \begin{macro}[EXP]{\@@_arg_last_unbraced:nn} +% \begin{macro}[EXP]{\::o_unbraced} +% \begin{macro}[EXP]{\::V_unbraced} +% \begin{macro}[EXP]{\::v_unbraced} +% \begin{macro}[EXP]{\::e_unbraced} +% \begin{macro}[EXP]{\::f_unbraced} +% \begin{macro}[EXP]{\::x_unbraced} +% There are a few places where the last argument needs to be available +% unbraced. First some helper macros. +% \begin{macrocode} +\cs_new:Npn \@@_arg_last_unbraced:nn #1#2 { #2#1 } +\cs_new:Npn \::o_unbraced \::: #1#2 + { \exp_after:wN \@@_arg_last_unbraced:nn \exp_after:wN {#2} {#1} } +\cs_new:Npn \::V_unbraced \::: #1#2 + { + \exp_after:wN \@@_arg_last_unbraced:nn + \exp_after:wN { \exp:w \@@_eval_register:N #2 } {#1} + } +\cs_new:Npn \::v_unbraced \::: #1#2 + { + \exp_after:wN \@@_arg_last_unbraced:nn + \exp_after:wN { \exp:w \@@_eval_register:c {#2} } {#1} + } +\cs_new:Npn \::e_unbraced \::: #1#2 + { \tex_expanded:D { \exp_not:n {#1} #2 } } +\cs_new:Npn \::f_unbraced \::: #1#2 + { + \exp_after:wN \@@_arg_last_unbraced:nn + \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } {#1} + } +\cs_new_protected:Npn \::x_unbraced \::: #1#2 + { + \cs_set_nopar:Npe \l_@@_internal_tl { \exp_not:n {#1} #2 } + \l_@@_internal_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP] +% { +% \exp_last_unbraced:No, +% \exp_last_unbraced:NV, +% \exp_last_unbraced:Nv, +% \exp_last_unbraced:Ne, +% \exp_last_unbraced:Nf, +% \exp_last_unbraced:NNo, +% \exp_last_unbraced:NNV, +% \exp_last_unbraced:NNf, +% \exp_last_unbraced:Nco, +% \exp_last_unbraced:NcV, +% \exp_last_unbraced:NNNo, +% \exp_last_unbraced:NNNV, +% \exp_last_unbraced:NNNf, +% \exp_last_unbraced:Nno, +% \exp_last_unbraced:Nnf, +% \exp_last_unbraced:Noo, +% \exp_last_unbraced:Nfo, +% \exp_last_unbraced:NnNo, +% \exp_last_unbraced:NNNNo, +% \exp_last_unbraced:NNNNf, +% } +% \begin{macro}{\exp_last_unbraced:Nx} +% Now the business end: most of these are hand-tuned for speed, but the +% general system is in place. +% \begin{macrocode} +\cs_new:Npn \exp_last_unbraced:No #1#2 { \exp_after:wN #1 #2 } +\cs_new:Npn \exp_last_unbraced:NV #1#2 + { \exp_after:wN #1 \exp:w \@@_eval_register:N #2 } +\cs_new:Npn \exp_last_unbraced:Nv #1#2 + { \exp_after:wN #1 \exp:w \@@_eval_register:c {#2} } +\cs_new:Npn \exp_last_unbraced:Ne #1#2 + { \exp_after:wN #1 \tex_expanded:D {#2} } +\cs_new:Npn \exp_last_unbraced:Nf #1#2 + { \exp_after:wN #1 \exp:w \exp_end_continue_f:w #2 } +\cs_new:Npn \exp_last_unbraced:NNo #1#2#3 + { \exp_after:wN #1 \exp_after:wN #2 #3 } +\cs_new:Npn \exp_last_unbraced:NNV #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp:w \@@_eval_register:N #3 + } +\cs_new:Npn \exp_last_unbraced:NNf #1#2#3 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp:w \exp_end_continue_f:w #3 + } +\cs_new:Npn \exp_last_unbraced:Nco #1#2#3 + { \exp_after:wN #1 \cs:w #2 \exp_after:wN \cs_end: #3 } +\cs_new:Npn \exp_last_unbraced:NcV #1#2#3 + { + \exp_after:wN #1 + \cs:w #2 \exp_after:wN \cs_end: + \exp:w \@@_eval_register:N #3 + } +\cs_new:Npn \exp_last_unbraced:NNNo #1#2#3#4 + { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 #4 } +\cs_new:Npn \exp_last_unbraced:NNNV #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN #3 + \exp:w \@@_eval_register:N #4 + } +\cs_new:Npn \exp_last_unbraced:NNNf #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN #3 + \exp:w \exp_end_continue_f:w #4 + } +\cs_new:Npn \exp_last_unbraced:Nno { \::n \::o_unbraced \::: } +\cs_new:Npn \exp_last_unbraced:Nnf { \::n \::f_unbraced \::: } +\cs_new:Npn \exp_last_unbraced:Noo { \::o \::o_unbraced \::: } +\cs_new:Npn \exp_last_unbraced:Nfo { \::f \::o_unbraced \::: } +\cs_new:Npn \exp_last_unbraced:NnNo { \::n \::N \::o_unbraced \::: } +\cs_new:Npn \exp_last_unbraced:NNNNo #1#2#3#4#5 + { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 \exp_after:wN #4 #5 } +\cs_new:Npn \exp_last_unbraced:NNNNf #1#2#3#4#5 + { + \exp_after:wN #1 + \exp_after:wN #2 + \exp_after:wN #3 + \exp_after:wN #4 + \exp:w \exp_end_continue_f:w #5 + } +\cs_new_protected:Npn \exp_last_unbraced:Nx { \::x_unbraced \::: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\exp_last_two_unbraced:Noo} +% \begin{macro}[EXP]{\@@_last_two_unbraced:noN} +% If |#2| is a single token then this can be implemented as +% \begin{verbatim} +% \cs_new:Npn \exp_last_two_unbraced:Noo #1 #2 #3 +% { \exp_after:wN \exp_after:wN \exp_after:wN #1 \exp_after:wN #2 #3 } +% \end{verbatim} +% However, for robustness this is not suitable. Instead, a bit of a +% shuffle is used to ensure that |#2| can be multiple tokens. +% \begin{macrocode} +\cs_new:Npn \exp_last_two_unbraced:Noo #1#2#3 + { \exp_after:wN \@@_last_two_unbraced:noN \exp_after:wN {#3} {#2} #1 } +\cs_new:Npn \@@_last_two_unbraced:noN #1#2#3 + { \exp_after:wN #3 #2 #1 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Preventing expansion} +% +% \begin{macro}{\__kernel_exp_not:w} +% At the kernel level, we need the primitive behaviour to allow expansion +% \emph{before} the brace group. +% \begin{macrocode} +\cs_new_eq:NN \__kernel_exp_not:w \tex_unexpanded:D +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\exp_not:c} +% \begin{macro}[EXP]{\exp_not:o} +% \begin{macro}[EXP]{\exp_not:e} +% \begin{macro}[EXP]{\exp_not:f} +% \begin{macro}[EXP]{\exp_not:V} +% \begin{macro}[EXP]{\exp_not:v} +% All these except \cs{exp_not:c} call the kernel-internal +% \cs{__kernel_exp_not:w} namely \cs{tex_unexpanded:D}. +% \begin{macrocode} +\cs_new:Npn \exp_not:c #1 { \exp_after:wN \exp_not:N \cs:w #1 \cs_end: } +\cs_new:Npn \exp_not:o #1 { \__kernel_exp_not:w \exp_after:wN {#1} } +\cs_new:Npn \exp_not:e #1 + { \__kernel_exp_not:w \tex_expanded:D { {#1} } } +\cs_new:Npn \exp_not:f #1 + { \__kernel_exp_not:w \exp_after:wN { \exp:w \exp_end_continue_f:w #1 } } +\cs_new:Npn \exp_not:V #1 + { + \__kernel_exp_not:w \exp_after:wN + { \exp:w \@@_eval_register:N #1 } + } +\cs_new:Npn \exp_not:v #1 + { + \__kernel_exp_not:w \exp_after:wN + { \exp:w \@@_eval_register:c {#1} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Controlled expansion} +% +% \begin{macro}{\exp:w} +% \begin{macro}{\exp_end:} +% \begin{macro}{\exp_end_continue_f:w} +% \begin{macro}{\exp_end_continue_f:nw} +% To trigger a sequence of \enquote{arbitrarily} many expansions we +% need a method to invoke \TeX's expansion mechanism in such a way +% that (a) we are able to stop it in a controlled manner and (b) the +% result of what triggered the expansion in the first place is null, +% i.e.\@, that we do not get any unwanted side effects. There aren't +% that many possibilities in \TeX{}; in fact the one explained below +% might well be the only one (as normally the result of expansion is +% not null). +% +% The trick here is to make use of the fact that +% \cs{tex_romannumeral:D} expands the tokens following it when looking +% for a number and that its expansion is null if that number turns out +% to be zero or negative. So we use that to start the expansion +% sequence: \cs{exp:w} is set equal to \cs{tex_romannumeral:D} in +% \pkg{l3basics}. To stop the expansion sequence in a controlled way +% all we need to provide is a constant integer zero as part of +% expanded tokens. As this is an integer constant it immediately stops +% \cs{tex_romannumeral:D}'s search for a number. Again, the +% definition of \cs{exp_end:} as the integer constant zero is in +% \pkg{l3basics}. (Note that according to our specification all +% tokens we expand initiated by \cs{exp:w} are supposed to be +% expandable (as well as their replacement text in the expansion) so +% we will not encounter a \enquote{number} that actually result in a +% roman numeral being generated. Or if we do then the programmer made +% a mistake.) +% +% If on the other hand we want to stop the initial expansion sequence +% but continue with an \texttt{f}-type expansion we provide the +% alphabetic constant |`^^@| that also represents |0| but this time +% \TeX's syntax for a \meta{number} continues searching for an +% optional space (and it continues expansion doing that) --- see +% \TeX{}book page~269 for details. +% \begin{macrocode} +\group_begin: + \tex_catcode:D `\^^@ = 13 + \cs_new_protected:Npn \exp_end_continue_f:w { `^^@ } +% \end{macrocode} +% If the above definition ever appears outside its proper context +% the active character |^^@| will be executed so we turn this into an +% error. The test for existence covers the (unlikely) case that some +% other code has already defined |^^@|: this is true for example for +% \texttt{xmltex.tex}. +% \begin{macrocode} + \if_cs_exist:N ^^@ + \else: + \cs_new:Npn ^^@ + { \msg_expandable_error:nn { kernel } { bad-exp-end-f } } + \fi: +% \end{macrocode} +% The same but grabbing an argument to remove spaces and braces. +% \begin{macrocode} + \cs_new:Npn \exp_end_continue_f:nw #1 { `^^@ #1 } +\group_end: +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Defining function variants} +% +% \begin{macrocode} +%<@@=cs> +% \end{macrocode} +% +% \begin{variable}{\s_@@_mark,\s_@@_stop} +% Internal scan marks. No \pkg{l3quark} yet, so do things by hand. +% \begin{macrocode} +\cs_new_eq:NN \s_@@_mark \scan_stop: +\cs_new_eq:NN \s_@@_stop \scan_stop: +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\q_@@_recursion_stop} +% Internal recursion quarks. No \pkg{l3quark} yet, so do things by hand. +% \begin{macrocode} +\cs_new:Npn \q_@@_recursion_stop { \q_@@_recursion_stop } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[EXP]{ +% \@@_use_none_delimit_by_s_stop:w, +% \@@_use_i_delimit_by_s_stop:nw, +% \@@_use_none_delimit_by_q_recursion_stop:w +% } +% Internal scan marks. +% \begin{macrocode} +\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { } +\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1} +\cs_new:Npn \@@_use_none_delimit_by_q_recursion_stop:w + #1 \q_@@_recursion_stop { } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\cs_generate_variant:Nn, \cs_generate_variant:cn} +% \begin{arguments} +% \item Base form of a function; \emph{e.g.},~\cs{tl_set:Nn} +% \item One or more variant argument specifiers; e.g., |{Nx,c,cx}| +% \end{arguments} +% After making sure that the base form exists, test whether it is +% protected or not and define \cs{@@_tmp:w} as either +% \cs{cs_new:Npe} or \cs{cs_new_protected:Npe}, which is +% then used to define all the variants (except those involving +% \texttt{x}-expansion, always protected). Split up the original base +% function only once, to grab its name and signature. Then we wish to +% iterate through the comma list of variant argument specifiers, which +% we first convert to a string: the reason is explained later. +% \begin{macrocode} +\cs_new_protected:Npn \cs_generate_variant:Nn #1#2 + { + \@@_generate_variant:N #1 + \use:e + { + \@@_generate_variant:nnNN + \cs_split_function:N #1 + \exp_not:N #1 + \tl_to_str:n {#2} , + \exp_not:N \scan_stop: , + \exp_not:N \q_@@_recursion_stop + } + } +\cs_new_protected:Npn \cs_generate_variant:cn + { \exp_args:Nc \cs_generate_variant:Nn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_generate_variant:N} +% \begin{macro}{\@@_generate_variant:ww, \@@_generate_variant:wwNw} +% The goal here is to pick up protected parent functions. There are +% four cases: the parent function can be a primitive or a macro, and +% can be expandable or not. For non-expandable primitives, all +% variants should be protected; skipping the \cs{else:} branch is safe +% because non-expandable primitives cannot be \TeX{} conditionals. +% +% The other case where variants should be protected is when the parent +% function is a protected macro: then |protected| appears in the +% meaning before the first occurrence of |macro|. The |ww| auxiliary +% removes everything in the meaning string after the first |ma|. We +% use |ma| rather than the full |macro| because the meaning of the +% \tn{firstmark} primitive (and four others) can contain an arbitrary +% string after a leading |firstmark:|. Then, look for |pr| in the +% part we extracted: no need to look for anything longer: the only +% strings we can have are an empty string, \verb*|\long |, +% \verb*|\protected |, \verb*|\protected\long |, |\first|, |\top|, +% |\bot|, |\splittop|, or |\splitbot|, with |\| replaced by the +% appropriate escape character. If |pr| appears in the part before +% |ma|, the first \cs{s_@@_mark} is taken as an argument of the |wwNw| +% auxiliary, and |#3| is \cs{cs_new_protected:Npe}, otherwise it +% is \cs{cs_new:Npe}. +% \begin{macrocode} +\cs_new_protected:Npe \@@_generate_variant:N #1 + { + \exp_not:N \exp_after:wN \exp_not:N \if_meaning:w + \exp_not:N \exp_not:N #1 #1 + \cs_set_eq:NN \exp_not:N \@@_tmp:w \cs_new_protected:Npe + \exp_not:N \else: + \exp_not:N \exp_after:wN \exp_not:N \@@_generate_variant:ww + \exp_not:N \token_to_meaning:N #1 \tl_to_str:n { ma } + \s_@@_mark + \s_@@_mark \cs_new_protected:Npe + \tl_to_str:n { pr } + \s_@@_mark \cs_new:Npe + \s_@@_stop + \exp_not:N \fi: + } +\exp_last_unbraced:NNNNo + \cs_new_protected:Npn \@@_generate_variant:ww + #1 { \tl_to_str:n { ma } } #2 \s_@@_mark + { \@@_generate_variant:wwNw #1 } +\exp_last_unbraced:NNNNo + \cs_new_protected:Npn \@@_generate_variant:wwNw + #1 { \tl_to_str:n { pr } } #2 \s_@@_mark #3 #4 \s_@@_stop + { \cs_set_eq:NN \@@_tmp:w #3 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_generate_variant:nnNN} +% \begin{arguments} +% \item Base name. +% \item Base signature. +% \item Boolean. +% \item Base function. +% \end{arguments} +% If the boolean is \cs{c_false_bool}, the base function has no colon +% and we abort with an error; otherwise, set off a loop through the +% desired variant forms. The original function is retained as |#4| for +% efficiency. +% \begin{macrocode} +\cs_new_protected:Npn \@@_generate_variant:nnNN #1#2#3#4 + { + \if_meaning:w \c_false_bool #3 + \msg_error:nne { kernel } { missing-colon } + { \token_to_str:c {#1} } + \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w + \fi: + \@@_generate_variant:Nnnw #4 {#1}{#2} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_generate_variant:Nnnw} +% \begin{arguments} +% \item Base function. +% \item Base name. +% \item Base signature. +% \item Beginning of variant signature. +% \end{arguments} +% First check whether to terminate the loop over variant forms. Then, +% for each variant form, construct a new function name using the +% original base name, the variant signature consisting of $l$ letters +% and the last $k-l$ letters of the base signature (of length $k$). +% For example, for a base function \cs{prop_put:Nnn} which needs a +% |cV| variant form, we want the new signature to be |cVn|. +% +% There are further subtleties: +% \begin{itemize} +% \item In \cs{cs_generate_variant:Nn} |\foo:nnTF| |{xxTF}|, we must define +% |\foo:xxTF| using |\exp_args:Nxx|, +% rather than a hypothetical |\exp_args:NxxTF|. Thus, we wish to +% trim a common trailing part from the base signature and the +% variant signature. +% \item In \cs{cs_generate_variant:Nn} |\foo:on| |{ox}|, the +% function |\foo:ox| must be defined using |\exp_args:Nnx|, not +% |\exp_args:Nox|, to avoid double |o| expansion. +% \item Lastly, \cs{cs_generate_variant:Nn} |\foo:on| |{xn}| must +% trigger an error, because we do not have a means to replace +% |o|-expansion by |x|-expansion. +% More generally, we can only convert |N| to |c|, or convert |n| +% to |V|, |v|, |o|, |e|, |f|, or |x|. +% \end{itemize} +% All this boils down to a few rules. Only |n| and |N|-type +% arguments can be replaced by \cs{cs_generate_variant:Nn}. Other +% argument types are allowed to be passed unchanged from the base +% form to the variant: in the process they are changed to |n| +% except for |N| and |p|-type arguments. A common trailing +% part is ignored. +% +% We compare the base and variant signatures one character at a time +% within |e|-expansion. The result is given to +% \cs{@@_generate_variant:wwNN} (defined later) in the form +% \meta{processed variant signature} \cs{s_@@_mark} \meta{errors} +% \cs{s_@@_stop} \meta{base function} \meta{new function}. If all went +% well, \meta{errors} is empty; otherwise, it is a kernel error +% message and some clean-up code. +% +% Note the space after |#3| and after the following brace group. +% Those are ignored by \TeX{} when fetching the last argument for +% \cs{@@_generate_variant_loop:nNwN}, but can be used as a delimiter +% for \cs{@@_generate_variant_loop_end:nwwwNNnn}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_generate_variant:Nnnw #1#2#3#4 , + { + \if_meaning:w \scan_stop: #4 + \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w + \fi: + \use:e + { + \exp_not:N \@@_generate_variant:wwNN + \@@_generate_variant_loop:nNwN { } + #4 + \@@_generate_variant_loop_end:nwwwNNnn + \s_@@_mark + #3 ~ + { ~ { } \fi: \@@_generate_variant_loop_long:wNNnn } ~ + { } + \s_@@_stop + \exp_not:N #1 {#2} {#4} + } + \@@_generate_variant:Nnnw #1 {#2} {#3} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP] +% { +% \@@_generate_variant_loop:nNwN, +% \@@_generate_variant_loop_base:N, +% \@@_generate_variant_loop_same:w, +% \@@_generate_variant_loop_end:nwwwNNnn, +% \@@_generate_variant_loop_long:wNNnn, +% \@@_generate_variant_loop_invalid:NNwNNnn, +% \@@_generate_variant_loop_special:NNwNNnn +% } +% \begin{arguments} +% \item Last few consecutive letters common between the base and +% variant (more precisely, \cs{@@_generate_variant_same:N} +% \meta{letter} for each letter). +% \item Next variant letter. +% \item Remainder of variant form. +% \item Next base letter. +% \end{arguments} +% The first argument is populated by +% \cs{@@_generate_variant_loop_same:w} when a variant letter and a +% base letter match. It is flushed into the input stream whenever the +% two letters are different: if the loop ends before, the argument is +% dropped, which means that trailing common letters are ignored. +% +% The case where the two letters are different is only allowed if the +% base is |N| and the variant is |c|, or when the base is |n| and the +% variant is |V|, |v|, |o|, |e|, |f|, or |x|. Otherwise, call +% \cs{@@_generate_variant_loop_invalid:NNwNNnn} to remove the end of +% the loop, get arguments at the end of the loop, and place an +% appropriate error message as a second argument of +% \cs{@@_generate_variant:wwNN}. If the letters are distinct and the +% base letter is indeed |n| or |N|, leave in the input stream whatever +% argument |#1| was collected, and the next variant letter |#2|, then +% loop by calling \cs{@@_generate_variant_loop:nNwN}. +% +% The loop can stop in three ways. +% \begin{itemize} +% \item If the end of the variant form is encountered first, |#2| is +% \cs{@@_generate_variant_loop_end:nwwwNNnn} (expanded by the +% conditional \cs{if:w}), which inserts some tokens to end the +% conditional; grabs the \meta{base name} as |#7|, the +% \meta{variant signature} |#8|, the \meta{next base letter} |#1| +% and the part |#3| of the base signature that wasn't read yet; +% and combines those into the \meta{new function} to be defined. +% \item If the end of the base form is encountered first, |#4| is +% |~{}\fi:| which ends the conditional (with an empty expansion), +% followed by \cs{@@_generate_variant_loop_long:wNNnn}, which +% places an error as the second argument of +% \cs{@@_generate_variant:wwNN}. +% \item The loop can be interrupted early if the requested expansion +% is unavailable, namely when the variant and base letters differ +% and the base is not the right one (|n| or |N| to support the +% variant). In that case too an error is placed as the second +% argument of \cs{@@_generate_variant:wwNN}. +% \end{itemize} +% Note that if the variant form has the same length as the base form, +% |#2| is as described in the first point, and |#4| as described in +% the second point above. The \cs{@@_generate_variant_loop_end:nwwwNNnn} +% breaking function takes the empty brace group in |#4| as its first +% argument: this empty brace group produces the correct signature for +% the full variant. +% \begin{macrocode} +\cs_new:Npn \@@_generate_variant_loop:nNwN #1#2#3 \s_@@_mark #4 + { + \if:w #2 #4 + \exp_after:wN \@@_generate_variant_loop_same:w + \else: + \if:w #4 \@@_generate_variant_loop_base:N #2 \else: + \if:w 0 + \if:w N #4 \else: \if:w n #4 \else: 1 \fi: \fi: + \if:w \scan_stop: \@@_generate_variant_loop_base:N #2 1 \fi: + 0 + \@@_generate_variant_loop_special:NNwNNnn #4#2 + \else: + \@@_generate_variant_loop_invalid:NNwNNnn #4#2 + \fi: + \fi: + \fi: + #1 + \prg_do_nothing: + #2 + \@@_generate_variant_loop:nNwN { } #3 \s_@@_mark + } +\cs_new:Npn \@@_generate_variant_loop_base:N #1 + { + \if:w c #1 N \else: + \if:w o #1 n \else: + \if:w V #1 n \else: + \if:w v #1 n \else: + \if:w f #1 n \else: + \if:w e #1 n \else: + \if:w x #1 n \else: + \if:w n #1 n \else: + \if:w N #1 N \else: + \scan_stop: + \fi: + \fi: + \fi: + \fi: + \fi: + \fi: + \fi: + \fi: + \fi: + } +\cs_new:Npn \@@_generate_variant_loop_same:w + #1 \prg_do_nothing: #2#3#4 + { #3 { #1 \@@_generate_variant_same:N #2 } } +\cs_new:Npn \@@_generate_variant_loop_end:nwwwNNnn + #1#2 \s_@@_mark #3 ~ #4 \s_@@_stop #5#6#7#8 + { + \scan_stop: \scan_stop: \fi: + \s_@@_mark \s_@@_stop + \exp_not:N #6 + \exp_not:c { #7 : #8 #1 #3 } + } +\cs_new:Npn \@@_generate_variant_loop_long:wNNnn #1 \s_@@_stop #2#3#4#5 + { + \exp_not:n + { + \s_@@_mark + \msg_error:nnee { kernel } { variant-too-long } + {#5} { \token_to_str:N #3 } + \use_none:nnn + \s_@@_stop + #3 + #3 + } + } +\cs_new:Npn \@@_generate_variant_loop_invalid:NNwNNnn + #1#2 \fi: \fi: \fi: #3 \s_@@_stop #4#5#6#7 + { + \fi: \fi: \fi: + \exp_not:n + { + \s_@@_mark + \msg_error:nneeee { kernel } { invalid-variant } + {#7} { \token_to_str:N #5 } {#1} {#2} + \use_none:nnn + \s_@@_stop + #5 + #5 + } + } +\cs_new:Npn \@@_generate_variant_loop_special:NNwNNnn + #1#2#3 \s_@@_stop #4#5#6#7 + { + #3 \s_@@_stop #4 #5 {#6} {#7} + \exp_not:n + { + \msg_error:nneeee + { kernel } { deprecated-variant } + {#7} { \token_to_str:N #5 } {#1} {#2} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP]{\@@_generate_variant_same:N} +% When the base and variant letters are identical, don't do any +% expansion. For most argument types, we can use the |n|-type +% no-expansion, but the |N| and |p| types require a slightly different +% behaviour with respect to braces. For |V|-type this function could +% output |N| to avoid adding useless braces but that is not a problem. +% \begin{macrocode} +\cs_new:Npn \@@_generate_variant_same:N #1 + { + \if:w N #1 #1 \else: + \if:w p #1 #1 \else: + \token_to_str:N n + \if:w n #1 \else: + \@@_generate_variant_loop_special:NNwNNnn #1#1 + \fi: + \fi: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_generate_variant:wwNN} +% If the variant form has already been defined, log its existence +% (provided \texttt{log-functions} is active). +% Otherwise, make sure that the |\exp_args:N #3| form is defined, and +% if it contains |x|, change \cs{@@_tmp:w} locally to +% \cs{cs_new_protected:Npe}. Then define the variant by +% combining the |\exp_args:N #3| variant and the base function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_generate_variant:wwNN + #1 \s_@@_mark #2 \s_@@_stop #3#4 + { + #2 + \cs_if_free:NT #4 + { + \group_begin: + \@@_generate_internal_variant:n {#1} + \@@_tmp:w #4 { \exp_not:c { exp_args:N #1 } \exp_not:N #3 } + \group_end: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_generate_internal_variant:n} +% \begin{macro}[rEXP]{\@@_generate_internal_variant_loop:n} +% First test for the presence of |x| (this is where working with +% strings makes our lives easier), as the result should be protected, +% and the next variant to be defined using that internal variant +% should be protected (done by setting \cs{@@_tmp:w}). Then call +% \cs{@@_generate_internal_variant:NNn} with arguments +% \cs{cs_new_protected:cpn} \cs{use:x} (for protected) or +% \cs{cs_new:cpn} \cs{tex_expanded:D} (expandable) and the signature. If |p| +% appears in the signature, or if the function to be defined is +% expandable and the primitive \tn{expanded} is not available, or if there +% are more than $8$ arguments, call +% some fall-back code that just puts the appropriate |\::| commands. +% Otherwise, call \cs{@@_generate_internal_one_go:NNn} to construct +% the \cs[no-index]{exp_args:N\dots} function as a macro taking up to +% $9$~arguments and expanding them using \cs{use:x} or \cs{tex_expanded:D}. +% \begin{macrocode} +\cs_new_protected:Npe \@@_generate_internal_variant:n #1 + { + \exp_not:N \@@_generate_internal_variant:wwnNwn + #1 \s_@@_mark + { \cs_set_eq:NN \exp_not:N \@@_tmp:w \cs_new_protected:Npe } + \cs_new_protected:cpn + \use:x + \token_to_str:N x \s_@@_mark + { } + \cs_new:cpn + \exp_not:N \tex_expanded:D + \s_@@_stop + {#1} + } +\exp_last_unbraced:NNNNo + \cs_new_protected:Npn \@@_generate_internal_variant:wwnNwn #1 + { \token_to_str:N x } #2 \s_@@_mark #3#4#5#6 \s_@@_stop #7 + { + #3 + \cs_if_free:cT { exp_args:N #7 } + { \@@_generate_internal_variant:NNn #4 #5 {#7} } + } +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new_protected:Npn \@@_generate_internal_variant:NNn ##1##2##3 + { + \if_catcode:w X \use_none:nnnnnnnn ##3 + \prg_do_nothing: \prg_do_nothing: \prg_do_nothing: + \prg_do_nothing: \prg_do_nothing: \prg_do_nothing: + \prg_do_nothing: \prg_do_nothing: X + \exp_after:wN \@@_generate_internal_test:Nw \exp_after:wN ##2 + \else: + \exp_after:wN \@@_generate_internal_test_aux:w \exp_after:wN #1 + \fi: + ##3 + \s_@@_mark + { + \use:e + { + ##1 { exp_args:N ##3 } + { \@@_generate_internal_variant_loop:n ##3 { : \use_i:nn } } + } + } + #1 + \s_@@_mark + { \exp_not:n { \@@_generate_internal_one_go:NNn ##1 ##2 {##3} } } + \s_@@_stop + } + \cs_new_protected:Npn \@@_generate_internal_test_aux:w + ##1 #1 ##2 \s_@@_mark ##3 ##4 \s_@@_stop {##3} + \cs_new_eq:NN \@@_generate_internal_test:Nw + \@@_generate_internal_test_aux:w + } +\exp_args:No \@@_tmp:w { \token_to_str:N p } +\cs_new_protected:Npn \@@_generate_internal_one_go:NNn #1#2#3 + { + \@@_generate_internal_loop:nwnnw + { \exp_not:N ##1 } 1 . { } { } + #3 { ? \@@_generate_internal_end:w } X ; + 23456789 { ? \@@_generate_internal_long:w } ; + #1 #2 {#3} + } +\cs_new_protected:Npn \@@_generate_internal_loop:nwnnw #1#2 . #3#4#5#6 ; #7 + { + \use_none:n #5 + \use_none:n #7 + \cs_if_exist_use:cF { @@_generate_internal_#5:NN } + { \@@_generate_internal_other:NN } + #5 #7 + #7 . + { #3 #1 } { #4 ## #2 } + #6 ; + } +\cs_new_protected:Npn \@@_generate_internal_N:NN #1#2 + { \@@_generate_internal_loop:nwnnw { \exp_not:N ###2 } } +\cs_new_protected:Npn \@@_generate_internal_c:NN #1#2 + { \exp_args:No \@@_generate_internal_loop:nwnnw { \exp_not:c {###2} } } +\cs_new_protected:Npn \@@_generate_internal_n:NN #1#2 + { \@@_generate_internal_loop:nwnnw { { \exp_not:n {###2} } } } +\cs_new_protected:Npn \@@_generate_internal_x:NN #1#2 + { \@@_generate_internal_loop:nwnnw { {###2} } } +\cs_new_protected:Npn \@@_generate_internal_other:NN #1#2 + { + \exp_args:No \@@_generate_internal_loop:nwnnw + { + \exp_after:wN + { + \exp:w \exp_args:NNc \exp_after:wN \exp_end: + { exp_not:#1 } {###2} + } + } + } +\cs_new_protected:Npn \@@_generate_internal_end:w #1 . #2#3#4 ; #5 ; #6#7#8 + { #6 { exp_args:N #8 } #3 { #7 {#2} } } +\cs_new_protected:Npn \@@_generate_internal_long:w #1 N #2#3 . #4#5#6# + { + \exp_args:Nx \@@_generate_internal_long:nnnNNn + { \@@_generate_internal_variant_loop:n #2 #6 { : \use_i:nn } } + {#4} {#5} + } +\cs_new:Npn \@@_generate_internal_long:nnnNNn #1#2#3#4 ; ; #5#6#7 + { #5 { exp_args:N #7 } #3 { #6 { \exp_not:n {#1} {#2} } } } +% \end{macrocode} +% This command grabs char by char outputting |\::#1| (not expanded +% further). We avoid tests by putting a trailing |: \use_i:nn|, which +% leaves \cs{cs_end:} and removes the looping macro. The colon is in +% fact also turned into \cs{:::} so that the required structure for +% |\exp_args:N...| commands is correctly terminated. +% \begin{macrocode} +\cs_new:Npn \@@_generate_internal_variant_loop:n #1 + { + \exp_after:wN \exp_not:N \cs:w :: #1 \cs_end: + \@@_generate_internal_variant_loop:n + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \prg_generate_conditional_variant:Nnn, +% \@@_generate_variant:nnNnn, +% \@@_generate_variant:w, +% \@@_generate_variant:n, +% \@@_generate_variant_p_form:nnn, +% \@@_generate_variant_T_form:nnn, +% \@@_generate_variant_F_form:nnn, +% \@@_generate_variant_TF_form:nnn, +% } +% \begin{macrocode} +\cs_new_protected:Npn \prg_generate_conditional_variant:Nnn #1 + { + \use:e + { + \@@_generate_variant:nnNnn + \cs_split_function:N #1 + } + } +\cs_new_protected:Npn \@@_generate_variant:nnNnn #1#2#3#4#5 + { + \if_meaning:w \c_false_bool #3 + \msg_error:nne { kernel } { missing-colon } + { \token_to_str:c {#1} } + \@@_use_i_delimit_by_s_stop:nw + \fi: + \exp_after:wN \@@_generate_variant:w + \tl_to_str:n {#5} , \scan_stop: , \q_@@_recursion_stop + \@@_use_none_delimit_by_s_stop:w \s_@@_mark {#1} {#2} {#4} \s_@@_stop + } +\cs_new_protected:Npn \@@_generate_variant:w + #1 , #2 \s_@@_mark #3#4#5 + { + \if_meaning:w \scan_stop: #1 \scan_stop: + \if_meaning:w \q_@@_nil #1 \q_@@_nil + \use_i:nnn + \fi: + \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w + \else: + \cs_if_exist_use:cTF { @@_generate_variant_#1_form:nnn } + { {#3} {#4} {#5} } + { + \msg_error:nnee + { kernel } { conditional-form-unknown } + {#1} { \token_to_str:c { #3 : #4 } } + } + \fi: + \@@_generate_variant:w #2 \s_@@_mark {#3} {#4} {#5} + } +\cs_new_protected:Npn \@@_generate_variant_p_form:nnn #1#2 + { \cs_generate_variant:cn { #1 _p : #2 } } +\cs_new_protected:Npn \@@_generate_variant_T_form:nnn #1#2 + { \cs_generate_variant:cn { #1 : #2 T } } +\cs_new_protected:Npn \@@_generate_variant_F_form:nnn #1#2 + { \cs_generate_variant:cn { #1 : #2 F } } +\cs_new_protected:Npn \@@_generate_variant_TF_form:nnn #1#2 + { \cs_generate_variant:cn { #1 : #2 TF } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\exp_args_generate:n} +% This function is not used in the kernel hence we can use functions +% that are defined in later modules. It also does not need to be fast +% so use inline mappings. For each requested variant we check that +% there are no characters besides |NnpcofVvx|, in particular that +% there are no spaces. Then we just call the internal function. +% \begin{macrocode} +\cs_new_protected:Npn \exp_args_generate:n #1 + { + \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} } + { + \str_map_inline:nn {##1} + { + \str_if_in:nnF { NnpcofeVvx } {####1} + { + \msg_error:nnnn { kernel } { invalid-exp-args } + {####1} {##1} + \str_map_break:n { \use_none:nn } + } + } + \@@_generate_internal_variant:n {##1} + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Definitions with the automated technique} +% \label{sec:l3expan:gendef} +% +% Some of these could be done more efficiently, but the complexity of +% coding then becomes an issue. Notice that the auto-generated functions +% actually take no arguments themselves. +% +% \begin{macro}[EXP] +% { +% \exp_args:Nnc, \exp_args:Nno, \exp_args:NnV, \exp_args:Nnv, \exp_args:Nne, \exp_args:Nnf, +% \exp_args:Noc, \exp_args:Noo, \exp_args:Nof, +% \exp_args:NVo, \exp_args:Nfo, \exp_args:Nff, \exp_args:Nee +% } +% \begin{macro} +% { +% \exp_args:NNx, \exp_args:Ncx, \exp_args:Nnx, +% \exp_args:Nox, \exp_args:Nxo, \exp_args:Nxx, +% } +% Here are the actual function definitions, using the helper +% functions above. The group is used because +% \cs{@@_generate_internal_variant:n} redefines \cs{@@_tmp:w} locally. +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 + { + \group_begin: + \exp_args:No \@@_generate_internal_variant:n + { \tl_to_str:n {#1} } + \group_end: + } +\@@_tmp:w { nc } +\@@_tmp:w { no } +\@@_tmp:w { nV } +\@@_tmp:w { nv } +\@@_tmp:w { ne } +\@@_tmp:w { nf } +\@@_tmp:w { oc } +\@@_tmp:w { oo } +\@@_tmp:w { of } +\@@_tmp:w { Vo } +\@@_tmp:w { fo } +\@@_tmp:w { ff } +\@@_tmp:w { ee } +\@@_tmp:w { Nx } +\@@_tmp:w { cx } +\@@_tmp:w { nx } +\@@_tmp:w { ox } +\@@_tmp:w { xo } +\@@_tmp:w { xx } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP] +% { +% \exp_args:NNcf, +% \exp_args:NNno, \exp_args:NNnV, \exp_args:NNoo, \exp_args:NNVV, +% \exp_args:Ncno, \exp_args:NcnV, \exp_args:Ncoo, \exp_args:NcVV, +% \exp_args:Nnnc, \exp_args:Nnno, \exp_args:Nnnf, \exp_args:Nnff, +% \exp_args:Nooo, \exp_args:Noof, \exp_args:Nffo, \exp_args:Neee +% } +% \begin{macro} +% { +% \exp_args:NNNx, \exp_args:NNnx, \exp_args:NNox, +% \exp_args:Nccx, \exp_args:Ncnx, +% \exp_args:Nnnx, \exp_args:Nnox, \exp_args:Noox, +% } +% \begin{macrocode} +\@@_tmp:w { Ncf } +\@@_tmp:w { Nno } +\@@_tmp:w { NnV } +\@@_tmp:w { Noo } +\@@_tmp:w { NVV } +\@@_tmp:w { cno } +\@@_tmp:w { cnV } +\@@_tmp:w { coo } +\@@_tmp:w { cVV } +\@@_tmp:w { nnc } +\@@_tmp:w { nno } +\@@_tmp:w { nnf } +\@@_tmp:w { nff } +\@@_tmp:w { ooo } +\@@_tmp:w { oof } +\@@_tmp:w { ffo } +\@@_tmp:w { eee } +\@@_tmp:w { NNx } +\@@_tmp:w { Nnx } +\@@_tmp:w { Nox } +\@@_tmp:w { nnx } +\@@_tmp:w { nox } +\@@_tmp:w { ccx } +\@@_tmp:w { cnx } +\@@_tmp:w { oox } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Held-over variant generation} +% +% \begin{macro}[documented-as = \cs_generate_from_arg_count:NNnn] +% {\cs_generate_from_arg_count:NNno} +% \begin{macro}[documented-as = \cs_replacement_spec:N]{\cs_replacement_spec:c} +% A couple of variants that are from early functions. +% \begin{macrocode} +\cs_generate_variant:Nn \cs_generate_from_arg_count:NNnn { NNno } +\cs_generate_variant:Nn \cs_replacement_spec:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |