% \iffalse meta-comment % %% File: l3candidates.dtx % % Copyright (C) 2012-2022 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} % % \fi % % \title{^^A % The \textsf{l3candidates} package\\ Experimental additions to % \pkg{l3kernel}^^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 2022-12-17} % % \maketitle % % \begin{documentation} % % \section{Important notice} % % This module provides a space in which functions can be added to % \pkg{l3kernel} (\pkg{expl3}) while still being experimental. % \begin{quote} % \bfseries % As such, the % functions here may not remain in their current form, or indeed at all, % in \pkg{l3kernel} in the future. % \end{quote} % In contrast to the material in % \pkg{l3experimental}, the functions here are all \emph{small} additions to % the kernel. We encourage programmers to test them out and report back on % the \texttt{LaTeX-L} mailing list. % % \medskip % % Thus, if you intend to use any of these functions from the candidate module in a public package % offered to others for productive use (e.g., being placed on CTAN) please consider the following points carefully: % \begin{itemize} % \item Be prepared that your public packages might require updating when such functions % are being finalized. % \item Consider informing us that you use a particular function in your public package, e.g., by % discussing this on the \texttt{LaTeX-L} % mailing list. This way it becomes easier to coordinate any updates necessary without issues % for the users of your package. % \item Discussing and understanding use cases for a particular addition or concept also helps to % ensure that we provide the right interfaces in the final version so please give us feedback % if you consider a certain candidate function useful (or not). % \end{itemize} % We only add functions in this space if we consider them being serious candidates for a final inclusion % into the kernel. However, real use sometimes leads to better ideas, so functions from this module are % \textbf{not necessarily stable} and we may have to adjust them! % % \section{Additions to \pkg{l3box}} % % \begin{function}[updated = 2019-01-23] % {\box_clip:N, \box_clip:c, \box_gclip:N, \box_gclip:c} % \begin{syntax} % \cs{box_clip:N} \meta{box} % \end{syntax} % Clips the \meta{box} in the output so that only material inside the % bounding box is displayed in the output. The updated \meta{box} is an % hbox, irrespective of the nature of the \meta{box} before the clipping is % applied. % % \textbf{These functions require the \LaTeX3 native drivers: they do % not work with the \LaTeXe{} \pkg{graphics} drivers!} % % \begin{texnote} % Clipping is implemented by the driver, and as such the full content of % the box is placed in the output file. Thus clipping does not remove % any information from the raw output, and hidden material can therefore % be viewed by direct examination of the file. % \end{texnote} % \end{function} % % \begin{function}[added = 2019-01-23] % { % \box_set_trim:Nnnnn, \box_set_trim:cnnnn, % \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn % } % \begin{syntax} % \cs{box_set_trim:Nnnnn} \meta{box} \Arg{left} \Arg{bottom} \Arg{right} \Arg{top} % \end{syntax} % Adjusts the bounding box of the \meta{box} \meta{left} is removed from % the left-hand edge of the bounding box, \meta{right} from the right-hand % edge and so fourth. All adjustments are \meta{dimension expressions}. % Material outside of the bounding box is still displayed in the output % unless \cs{box_clip:N} is subsequently applied. % The updated \meta{box} is an % hbox, irrespective of the nature of the \meta{box} before the trim % operation is applied. % The behavior of the operation where the trims requested is % greater than the size of the box is undefined. % \end{function} % % \begin{function}[added = 2019-01-23] % { % \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn, % \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn % } % \begin{syntax} % \cs{box_set_viewport:Nnnnn} \meta{box} \Arg{llx} \Arg{lly} \Arg{urx} \Arg{ury} % \end{syntax} % Adjusts the bounding box of the \meta{box} such that it has lower-left % co-ordinates (\meta{llx}, \meta{lly}) and upper-right co-ordinates % (\meta{urx}, \meta{ury}). All four co-ordinate positions are % \meta{dimension expressions}. Material outside of the bounding box is % still displayed in the output unless \cs{box_clip:N} is % subsequently applied. % The updated \meta{box} is an % hbox, irrespective of the nature of the \meta{box} before the viewport % operation is applied. % \end{function} % % \section{Additions to \pkg{l3expan}} % % \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{Additions to \pkg{l3fp}} % % \begin{function}[pTF, added = 2019-08-25]{\fp_if_nan:n} % \begin{syntax} % \cs{fp_if_nan:n} \Arg{fpexpr} % \end{syntax} % Evaluates the \meta{fpexpr} and tests whether the result is exactly % \nan{}. The test returns \texttt{false} for any other result, even % a tuple containing \nan{}. % \end{function} % % \section{Additions to \pkg{l3file}} % % \begin{function}[added = 2018-12-29]{\iow_allow_break:} % \begin{syntax} % \cs{iow_allow_break:} % \end{syntax} % In the first argument of \cs{iow_wrap:nnnN} (for instance in % messages), inserts a break-point that allows a line break. % In other words this is a zero-width breaking space. % \end{function} % % \begin{function}[added = 2019-03-23]{\ior_get_term:nN, \ior_str_get_term:nN} % \begin{syntax} % \cs{ior_get_term:nN} \meta{prompt} \meta{token list variable} % \end{syntax} % Function that reads one or more lines (until an equal number of left % and right braces are found) from the terminal and stores % the result locally in the \meta{token list} variable. Tokenization % occurs as described for \cs{ior_get:NN} or \cs{ior_str_get:NN}, respectively. % When the \meta{prompt} % is empty, \TeX{} will wait for input without any other indication: % typically the programmer will have provided a suitable text using % e.g.~\cs{iow_term:n}. Where the \meta{prompt} is given, it will appear % in the terminal followed by an |=|, e.g. % \begin{verbatim} % prompt= % \end{verbatim} % \end{function} % % \begin{function}[added = 2019-05-08]{\ior_shell_open:Nn} % \begin{syntax} % \cs{ior_shell_open:Nn} \meta{stream} \Arg{shell~command} % \end{syntax} % Opens the \emph{pseudo}-file created by the output of the % \meta{shell command} for reading using \meta{stream} as the % control sequence for access. If the \meta{stream} was already % open it is closed before the new operation begins. The % \meta{stream} is available for access immediately and will remain % allocated to \meta{shell command} until a \cs{ior_close:N} instruction % is given or the \TeX{} run ends. % If piped system calls are disabled an error is raised. % % For details of handling of the \meta{shell command}, see % \cs{sys_get_shell:nnNTF}. % \end{function} % % \section{Additions to \pkg{l3flag}} % % \begin{function}[EXP, added = 2018-04-02]{\flag_raise_if_clear:n} % \begin{syntax} % \cs{flag_raise_if_clear:n} \Arg{flag name} % \end{syntax} % Ensures the \meta{flag} is raised by making its height at least~$1$, % locally. % \end{function} % % \section{Additions to \pkg{l3intarray}} % % \begin{function}[added = 2018-05-05] % { % \intarray_gset_rand:Nnn, \intarray_gset_rand:cnn, % \intarray_gset_rand:Nn, \intarray_gset_rand:cn % } % \begin{syntax} % \cs{intarray_gset_rand:Nnn} \meta{intarray~var} \Arg{minimum} \Arg{maximum} % \cs{intarray_gset_rand:Nn} \meta{intarray~var} \Arg{maximum} % \end{syntax} % Evaluates the integer expressions \meta{minimum} and \meta{maximum} % then sets each entry (independently) of the \meta{integer array % variable} to a pseudo-random number between the two (with bounds % included). If the absolute value of either bound is bigger than % $2^{30}-1$, an error occurs. Entries are generated in the same way % as repeated calls to \cs{int_rand:nn} or \cs{int_rand:n} % respectively, in particular for the second function the % \meta{minimum} is $1$. % Assignments are always global. % This is not available in older versions of \XeTeX{}. % \end{function} % % \begin{function}[added = 2018-05-04, rEXP]{\intarray_to_clist:N} % \begin{syntax} % \cs{intarray_to_clist:N} \meta{intarray~var} % \end{syntax} % Converts the \meta{intarray} to integer denotations separated by % commas. All tokens have category code other. If the % \meta{intarray} has no entry the result is empty; otherwise the % result has one fewer comma than the number of items. % \end{function} % % \section{Additions to \pkg{l3msg}} % % \begin{function}[added = 2017-12-04]{\msg_show_eval:Nn, \msg_log_eval:Nn} % \begin{syntax} % \cs{msg_show_eval:Nn} \meta{function} \Arg{expression} % \end{syntax} % Shows or logs the \meta{expression} (turned into a string), an equal % sign, and the result of applying the \meta{function} to the % \Arg{expression} (with \texttt{f}-expansion). For instance, if the % \meta{function} is \cs{int_eval:n} and the \meta{expression} is % |1+2| then this logs |> 1+2=3.| % \end{function} % % \begin{function}[EXP, added = 2017-12-04] % {\msg_show_item:n, \msg_show_item_unbraced:n, \msg_show_item:nn, \msg_show_item_unbraced:nn} % \begin{syntax} % \cs{seq_map_function:NN} \meta{seq} \cs{msg_show_item:n} % \cs{prop_map_function:NN} \meta{prop} \cs{msg_show_item:nn} % \end{syntax} % Used in the text of messages for \cs{msg_show:nnxxxx} to show or log % a list of items or key--value pairs. The one-argument functions are % used for sequences, clist or token lists and the others for property % lists. These functions turn their arguments to strings. % \end{function} % % \section{Additions to \pkg{l3prg}} % % \begin{function}[added = 2018-05-10] % { % \bool_set_inverse:N , \bool_set_inverse:c , % \bool_gset_inverse:N, \bool_gset_inverse:c % } % \begin{syntax} % \cs{bool_set_inverse:N} \meta{boolean} % \end{syntax} % Toggles the \meta{boolean} from \texttt{true} to \texttt{false} and % conversely: sets it to the inverse of its current value. % \end{function} % % \begin{function}[added = 2019-02-10, EXP, noTF] % {\bool_case_true:n, \bool_case_false:n} % \begin{syntax} % \cs{bool_case_true:nTF} \\ % ~~|{| \\ % ~~~~\Arg{boolexpr case_1} \Arg{code case_1} \\ % ~~~~\Arg{boolexpr case_2} \Arg{code case_2} \\ % ~~~~\ldots \\ % ~~~~\Arg{boolexpr case_n} \Arg{code case_n} \\ % ~~|}| \\ % ~~\Arg{true code} % ~~\Arg{false code} % \end{syntax} % Evaluates in turn each of the \meta{boolean expression cases} until % the first one that evaluates to \texttt{true} or to \texttt{false}, % for \cs{bool_case_true:n} and \cs{bool_case_false:n}, respectively. % The \meta{code} associated to this first case is left in the input % stream, followed by the \meta{true code}, and other cases are % discarded. If none of the cases match then only the \meta{false % code} is inserted. The functions \cs{bool_case_true:n} and % \cs{bool_case_false:n}, which do nothing if there is no match, are % also available. For example % \begin{verbatim} % \bool_case_true:nF % { % { \dim_compare_p:n { \l__mypkg_wd_dim <= 10pt } } % { Fits } % { \int_compare_p:n { \l__mypkg_total_int >= 10 } } % { Many } % { \l__mypkg_special_bool } % { Special } % } % { No idea! } % \end{verbatim} % leaves \enquote{\texttt{Fits}} or \enquote{\texttt{Many}} or % \enquote{\texttt{Special}} or \enquote{\texttt{No idea!}} in the % input stream, in a way similar to some other language's % \enquote{\texttt{if} \ldots\ \texttt{elseif} \ldots\ \texttt{elseif} \ldots\ % \texttt{else} \ldots}. % \end{function} % % \section{Additions to \pkg{l3prop}} % % \begin{function}[EXP, added = 2016-12-06] % {\prop_rand_key_value:N, \prop_rand_key_value:c} % \begin{syntax} % \cs{prop_rand_key_value:N} \meta{property list} % \end{syntax} % Selects a pseudo-random key--value pair from the \meta{property list} % and returns \Arg{key} and \Arg{value}. If the \meta{property list} is % empty the result is empty. % This is not available in older versions of \XeTeX{}. % \begin{texnote} % The result is returned within the \tn{unexpanded} % primitive (\cs{exp_not:n}), which means that the \meta{value} % does not expand further when appearing in an \texttt{x}-type % or \texttt{e}-type argument expansion. % \end{texnote} % \end{function} % % \section{Additions to \pkg{l3seq}} % % \begin{function}[rEXP] % { % \seq_mapthread_function:NNN, \seq_mapthread_function:NcN, % \seq_mapthread_function:cNN, \seq_mapthread_function:ccN % } % \begin{syntax} % \cs{seq_mapthread_function:NNN} \meta{seq_1} \meta{seq_2} \meta{function} % \end{syntax} % Applies \meta{function} to every pair of items % \meta{seq_1-item}--\meta{seq_2-item} from the two sequences, returning % items from both sequences from left to right. The \meta{function} % receives two \texttt{n}-type arguments for each iteration. The mapping % terminates when % the end of either sequence is reached (\emph{i.e.}~whichever sequence has % fewer items determines how many iterations % occur). % \end{function} % % \begin{function}{\seq_set_filter:NNn, \seq_gset_filter:NNn} % \begin{syntax} % \cs{seq_set_filter:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline boolexpr} % \end{syntax} % Evaluates the \meta{inline boolexpr} for every \meta{item} stored % within the \meta{sequence_2}. The \meta{inline boolexpr} % receives the \meta{item} as |#1|. The sequence of all \meta{items} % for which the \meta{inline boolexpr} evaluated to \texttt{true} % is assigned to \meta{sequence_1}. % \begin{texnote} % Contrarily to other mapping functions, \cs{seq_map_break:} cannot % be used in this function, and would lead to low-level \TeX{} errors. % \end{texnote} % \end{function} % % \begin{function}[added = 2018-04-06] % {\seq_set_from_function:NnN, \seq_gset_from_function:NnN} % \begin{syntax} % \cs{seq_set_from_function:NnN} \meta{seq~var} \Arg{loop~code} \meta{function} % \end{syntax} % Sets the \meta{seq~var} equal to a sequence whose items are obtained % by \texttt{x}-expanding \meta{loop~code} \meta{function}. This % expansion must result in successive calls to the \meta{function} % with no nonexpandable tokens in between. More precisely the % \meta{function} is replaced by a wrapper function that inserts the % appropriate separators between items in the sequence. The % \meta{loop~code} must be expandable; it can be for example % \cs{tl_map_function:NN} \meta{tl~var} or \cs{clist_map_function:nN} % \Arg{clist} or \cs{int_step_function:nnnN} \Arg{initial value} % \Arg{step} \Arg{final value}. % \end{function} % % \begin{function}[added = 2018-04-06] % {\seq_set_from_inline_x:Nnn, \seq_gset_from_inline_x:Nnn} % \begin{syntax} % \cs{seq_set_from_inline_x:Nnn} \meta{seq~var} \Arg{loop~code} \Arg{inline~code} % \end{syntax} % Sets the \meta{seq~var} equal to a sequence whose items are obtained % by \texttt{x}-expanding \meta{loop~code} applied to a % \meta{function} derived from the \meta{inline~code}. A % \meta{function} is defined, that takes one argument, % \texttt{x}-expands the \meta{inline~code} with that argument % as~|#1|, then adds appropriate separators to turn the result into an % item of the sequence. The \texttt{x}-expansion of \meta{loop~code} % \meta{function} must result in successive calls to the % \meta{function} with no nonexpandable tokens in between. The % \meta{loop~code} must be expandable; it can be for example % \cs{tl_map_function:NN} \meta{tl~var} or \cs{clist_map_function:nN} % \Arg{clist} or \cs{int_step_function:nnnN} \Arg{initial value} % \Arg{step} \Arg{final value}, but not the analogous \enquote{inline} % mappings. % \end{function} % % \begin{function}[added = 2021-04-29, noTF] % {\seq_set_item:Nnn, \seq_set_item:cnn, \seq_gset_item:Nnn, \seq_gset_item:cnn} % \begin{syntax} % \cs{seq_set_item:Nnn} \meta{seq~var} \Arg{intexpr} \Arg{item} % \cs{seq_set_item:NnnTF} \meta{seq~var} \Arg{intexpr} \Arg{item} \Arg{true code} \Arg{false code} % \end{syntax} % Removes the item of \meta{sequence} at the position given by % evaluating the \meta{integer expression} and replaces it by % \meta{item}. Items are indexed from $1$ on the left/top of the % \meta{sequence}, or from $-1$ on the right/bottom. If the % \meta{integer expression} is zero or is larger (in absolute value) % than the number of items in the sequence, the \meta{sequence} is not % modified. In these cases, \cs{seq_set_item:Nnn} raises an error % while \cs{seq_set_item:NnnTF} runs the \meta{false code}. In cases % where the assignment was successful, \meta{true code} is run % afterwards. % \end{function} % % \begin{function}[added = 2021-04-28, noTF] % {\seq_pop_item:NnN, \seq_pop_item:cnN, \seq_gpop_item:NnN, \seq_gpop_item:cnN} % \begin{syntax} % \cs{seq_pop_item:NnN} \meta{seq~var} \Arg{intexpr} \Arg{tl~var} % \cs{seq_pop_item:NnNTF} \meta{seq~var} \Arg{intexpr} \Arg{tl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Removes the \meta{item} at position \meta{integer expression} in the % \meta{sequence}, and places it in the \meta{token list variable}. % Items are indexed from $1$ on the left/top of the \meta{sequence}, % or from $-1$ on the right/bottom. If the position is zero or is % larger (in absolute value) than the number of items in the sequence, % the \meta{seq~var} is not modified, the \meta{token list} is set to % the special marker \cs{q_no_value}, and the \meta{false code} is % left in the input stream; otherwise the \meta{true code} is. The % \meta{token list} assignment is local while the \meta{sequence} is % assigned locally for |pop| or globally for |gpop| functions. % \end{function} % % \section{Additions to \pkg{l3sys}} % % \begin{variable}[added = 2018-05-02]{\c_sys_engine_version_str} % The version string of the current engine, in the same form as % given in the banner issued when running a job. For \pdfTeX{} % and \LuaTeX{} this is of the form % \begin{quote} % \meta{major}.\meta{minor}.\meta{revision} % \end{quote} % For \XeTeX{}, the form is % \begin{quote} % \meta{major}.\meta{minor} % \end{quote} % For \pTeX{} and \upTeX{}, only releases since \TeX{} Live 2018 % make the data available, and the form is more complex, as it comprises % the \pTeX{} version, the \upTeX{} version and the e-\pTeX{} version. % \begin{quote} % p\meta{major}.\meta{minor}.\meta{revision}-u\meta{major}.\meta{minor}^^A % -\meta{epTeX} % \end{quote} % where the |u| part is only present for \upTeX{}. % \end{variable} % % \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_rand_exist:} % \begin{syntax} % \cs{sys_if_rand_exist_p:} % \cs{sys_if_rand_exist:TF} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the engine has a pseudo-random number generator. Currently % this is the case in \pdfTeX{}, \LuaTeX{}, \pTeX{}, \upTeX{} and recent % releases of \XeTeX{}. % \end{function} % % \section{Additions to \pkg{l3tl}} % % \begin{function}[EXP, added = 2017-07-15] % { % \tl_range_braced:Nnn, \tl_range_braced:cnn, \tl_range_braced:nnn, % \tl_range_unbraced:Nnn, \tl_range_unbraced:cnn, \tl_range_unbraced:nnn % } % \begin{syntax} % \cs{tl_range_braced:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index} % \cs{tl_range_braced:nnn} \Arg{token list} \Arg{start index} \Arg{end index} % \cs{tl_range_unbraced:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index} % \cs{tl_range_unbraced:nnn} \Arg{token list} \Arg{start index} \Arg{end index} % \end{syntax} % Leaves in the input stream the items from the \meta{start index} to % the \meta{end index} inclusive, using the same indexing as % \cs{tl_range:nnn}. Spaces are ignored. Regardless of whether items % appear with or without braces in the \meta{token list}, the % \cs{tl_range_braced:nnn} function wraps each item in braces, while % \cs{tl_range_unbraced:nnn} does not (overall it removes an outer set % of braces). For instance, % \begin{verbatim} % \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { 2 } { 5 } } % \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { -4 } { -1 } } % \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { -2 } { -1 } } % \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { 0 } { -1 } } % \end{verbatim} % prints \verb*|{b}{c}{d}{e{}}|, \verb*|{c}{d}{e{}}{f}|, \verb*|{e{}}{f}|, and an empty % line to the terminal, while % \begin{verbatim} % \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { 2 } { 5 } } % \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { -4 } { -1 } } % \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { -2 } { -1 } } % \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { 0 } { -1 } } % \end{verbatim} % prints \verb*|bcde{}|, \verb*|cde{}f|, \verb*|e{}f|, and an empty % line to the terminal. Because braces are removed, the result of % \cs{tl_range_unbraced:nnn} may have a different number of items as % for \cs{tl_range:nnn} or \cs{tl_range_braced:nnn}. In cases where % preserving spaces is important, consider the slower function % \cs{tl_range:nnn}. % \begin{texnote} % The result is returned within the \tn{unexpanded} % primitive (\cs{exp_not:n}), which means that the \meta{item} % does not expand further when appearing in an \texttt{x}-type % or \texttt{e}-type argument expansion. % \end{texnote} % \end{function} % % \begin{function}[added = 2018-04-01]{\tl_build_begin:N, \tl_build_gbegin:N} % \begin{syntax} % \cs{tl_build_begin:N} \meta{tl~var} % \end{syntax} % Clears the \meta{tl~var} and sets it up to support other % \cs[no-index]{tl_build_\ldots{}} functions, which allow accumulating % large numbers of tokens piece by piece much more efficiently than % standard \pkg{l3tl} functions. Until \cs{tl_build_end:N} % \meta{tl~var} is called, applying any function from \pkg{l3tl} other % than \cs[no-index]{tl_build_\ldots{}} will lead to incorrect % results. The |begin| and |gbegin| functions must be used for local % and global \meta{tl~var} respectively. % \end{function} % % \begin{function}[added = 2018-04-01]{\tl_build_clear:N, \tl_build_gclear:N} % \begin{syntax} % \cs{tl_build_clear:N} \meta{tl~var} % \end{syntax} % Clears the \meta{tl~var} and sets it up to support other % \cs[no-index]{tl_build_\ldots{}} functions. The |clear| and % |gclear| functions must be used for local and global \meta{tl~var} % respectively. % \end{function} % % \begin{function}[added = 2018-04-01] % { % \tl_build_put_left:Nn, \tl_build_put_left:Nx, % \tl_build_gput_left:Nn, \tl_build_gput_left:Nx, % \tl_build_put_right:Nn, \tl_build_put_right:Nx, % \tl_build_gput_right:Nn, \tl_build_gput_right:Nx, % } % \begin{syntax} % \cs{tl_build_put_left:Nn} \meta{tl~var} \Arg{tokens} % \cs{tl_build_put_right:Nn} \meta{tl~var} \Arg{tokens} % \end{syntax} % Adds \meta{tokens} to the left or right side of the current contents % of \meta{tl~var}. The \meta{tl~var} must have been set up with % \cs{tl_build_begin:N} or \cs{tl_build_gbegin:N}. The |put| and % |gput| functions must be used for local and global \meta{tl~var} % respectively. The |right| functions are about twice faster than the % |left| functions. % \end{function} % % \begin{function}[added = 2018-04-01]{\tl_build_get:NN} % \begin{syntax} % \cs{tl_build_get:NN} \meta{tl~var_1} \meta{tl~var_2} % \end{syntax} % Stores the contents of the \meta{tl~var_1} in the \meta{tl~var_2}. % The \meta{tl~var_1} must have been set up with \cs{tl_build_begin:N} % or \cs{tl_build_gbegin:N}. The \meta{tl~var_2} is a % \enquote{normal} token list variable, assigned locally using % \cs{tl_set:Nn}. % \end{function} % % \begin{function}[added = 2018-04-01]{\tl_build_end:N, \tl_build_gend:N} % \begin{syntax} % \cs{tl_build_end:N} \meta{tl~var} % \end{syntax} % Gets the contents of \meta{tl~var} and stores that into the % \meta{tl~var} using \cs{tl_set:Nn} or \cs{tl_gset:Nn}. % The \meta{tl~var} must have % been set up with \cs{tl_build_begin:N} or \cs{tl_build_gbegin:N}. % The |end| and |gend| functions must be used for local and global % \meta{tl~var} respectively. These functions completely remove the % setup code that enabled \meta{tl~var} to be used for other % \cs[no-index]{tl_build_\ldots{}} functions. % \end{function} % % \section{Additions to \pkg{l3token}} % % \begin{variable}[added = 2017-08-07]{\c_catcode_active_space_tl} % Token list containing one character with category code $13$, % (\enquote{active}), and character code $32$ (space). % \end{variable} % % \begin{function}[added = 2018-09-23] % { % \peek_catcode_collect_inline:Nn, % \peek_charcode_collect_inline:Nn, % \peek_meaning_collect_inline:Nn % } % \begin{syntax} % \cs{peek_catcode_collect_inline:Nn} \meta{test token} \Arg{inline code} % \cs{peek_charcode_collect_inline:Nn} \meta{test token} \Arg{inline code} % \cs{peek_meaning_collect_inline:Nn} \meta{test token} \Arg{inline code} % \end{syntax} % Collects and removes tokens from the input stream until finding a % token that does not match the \meta{test token} (as defined by the % test \cs{token_if_eq_catcode:NNTF} or \cs{token_if_eq_charcode:NNTF} % or \cs{token_if_eq_meaning:NNTF}). The collected tokens are passed % to the \meta{inline code} as~|#1|. When begin-group or end-group % tokens (usually |{| or~|}|) are collected they are replaced by % implicit \cs{c_group_begin_token} and \cs{c_group_end_token}, and % when spaces (including \cs{c_space_token}) are collected they are % replaced by explicit spaces. % % For example the following code prints ``Hello'' to the terminal and % leave ``, world!'' in the input stream. % \begin{verbatim} % \peek_catcode_collect_inline:Nn A { \iow_term:n {#1} } Hello,~world! % \end{verbatim} % Another example is that the following code tests if the next token is |*|, ignoring intervening spaces, but putting them back using |#1| if there is no~|*|. % \begin{verbatim} % \peek_meaning_collect_inline:Nn \c_space_token % { \peek_charcode:NTF * { star } { no~star #1 } } % \end{verbatim} % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3candidates} Implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Additions to \pkg{l3box}} % % \begin{macrocode} %<@@=box> % \end{macrocode} % % \subsubsection{Viewing part of a box} % % \begin{macro}{\box_clip:N, \box_clip:c, \box_gclip:N, \box_gclip:c} % A wrapper around the driver-dependent code. % \begin{macrocode} \cs_new_protected:Npn \box_clip:N #1 { \hbox_set:Nn #1 { \@@_backend_clip:N #1 } } \cs_generate_variant:Nn \box_clip:N { c } \cs_new_protected:Npn \box_gclip:N #1 { \hbox_gset:Nn #1 { \@@_backend_clip:N #1 } } \cs_generate_variant:Nn \box_gclip:N { c } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \box_set_trim:Nnnnn, \box_set_trim:cnnnn, % \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn % } % \begin{macro}{\@@_set_trim:NnnnnN} % Trimming from the left- and right-hand edges of the box is easy: kern the % appropriate parts off each side. % \begin{macrocode} \cs_new_protected:Npn \box_set_trim:Nnnnn #1#2#3#4#5 { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN } \cs_generate_variant:Nn \box_set_trim:Nnnnn { c } \cs_new_protected:Npn \box_gset_trim:Nnnnn #1#2#3#4#5 { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN } \cs_generate_variant:Nn \box_gset_trim:Nnnnn { c } \cs_new_protected:Npn \@@_set_trim:NnnnnN #1#2#3#4#5#6 { \hbox_set:Nn \l_@@_internal_box { \__kernel_kern:n { -#2 } \box_use:N #1 \__kernel_kern:n { -#4 } } % \end{macrocode} % For the height and depth, there is a need to watch the baseline is % respected. Material always has to stay on the correct side, so trimming % has to check that there is enough material to trim. First, the bottom % edge. If there is enough depth, simply set the depth, or if not move % down so the result is zero depth. \cs{box_move_down:nn} is used in both % cases so the resulting box always contains a \tn{lower} primitive. % The internal box is used here as it allows safe use of \cs{box_set_dp:Nn}. % \begin{macrocode} \dim_compare:nNnTF { \box_dp:N #1 } > {#3} { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn \c_zero_dim { \box_use_drop:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box { \box_dp:N #1 - (#3) } } { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn { (#3) - \box_dp:N #1 } { \box_use_drop:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box \c_zero_dim } % \end{macrocode} % Same thing, this time from the top of the box. % \begin{macrocode} \dim_compare:nNnTF { \box_ht:N \l_@@_internal_box } > {#5} { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn \c_zero_dim { \box_use_drop:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box { \box_ht:N \l_@@_internal_box - (#5) } } { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn { (#5) - \box_ht:N \l_@@_internal_box } { \box_use_drop:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box \c_zero_dim } #6 #1 \l_@@_internal_box } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn, % \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn % } % \begin{macro}{\@@_viewport:NnnnnN} % The same general logic as for the trim operation, but with absolute % dimensions. As a result, there are some things to watch out for in the % vertical direction. % \begin{macrocode} \cs_new_protected:Npn \box_set_viewport:Nnnnn #1#2#3#4#5 { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN } \cs_generate_variant:Nn \box_set_viewport:Nnnnn { c } \cs_new_protected:Npn \box_gset_viewport:Nnnnn #1#2#3#4#5 { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN } \cs_generate_variant:Nn \box_gset_viewport:Nnnnn { c } \cs_new_protected:Npn \@@_set_viewport:NnnnnN #1#2#3#4#5#6 { \hbox_set:Nn \l_@@_internal_box { \__kernel_kern:n { -#2 } \box_use:N #1 \__kernel_kern:n { #4 - \box_wd:N #1 } } \dim_compare:nNnTF {#3} < \c_zero_dim { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn \c_zero_dim { \box_use_drop:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box { - \@@_dim_eval:n {#3} } } { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn {#3} { \box_use_drop:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box \c_zero_dim } \dim_compare:nNnTF {#5} > \c_zero_dim { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn \c_zero_dim { \box_use_drop:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box { (#5) \dim_compare:nNnT {#3} > \c_zero_dim { - (#3) } } } { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn { - \@@_dim_eval:n {#5} } { \box_use_drop:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box \c_zero_dim } #6 #1 \l_@@_internal_box } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3flag}} % % \begin{macrocode} %<@@=flag> % \end{macrocode} % % \begin{macro}[EXP]{\flag_raise_if_clear:n} % It might be faster to just call the \enquote{trap} function in all % cases but conceptually the function name suggests we should only run % it if the flag is zero in case the \enquote{trap} made customizable % in the future. % \begin{macrocode} \cs_new:Npn \flag_raise_if_clear:n #1 { \if_cs_exist:w flag~#1~0 \cs_end: \else: \cs:w flag~#1 \cs_end: 0 ; \fi: } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3msg}} % % \begin{macrocode} %<@@=msg> % \end{macrocode} % % \begin{macro}{\msg_show_eval:Nn, \msg_log_eval:Nn, \@@_show_eval:nnN} % A short-hand used for \cs{int_show:n} and similar functions that % passes to \cs{tl_show:n} the result of applying |#1| (a % function such as \cs{int_eval:n}) to the expression |#2|. The use of % \texttt{f}-expansion ensures that |#1| is expanded in the scope in which the % show command is called, rather than in the group created by % \cs{iow_wrap:nnnN}. This is only important for expressions % involving the \tn{currentgrouplevel} or \tn{currentgrouptype}. % On the other hand we want the expression to be converted to a string % with the usual escape character, hence within the wrapping code. % \begin{macrocode} \cs_new_protected:Npn \msg_show_eval:Nn #1#2 { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_show:n } \cs_new_protected:Npn \msg_log_eval:Nn #1#2 { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_log:n } \cs_new_protected:Npn \@@_show_eval:nnN #1#2#3 { #3 { #2 = #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\msg_show_item:n} % \begin{macro}[EXP]{\msg_show_item_unbraced:n} % \begin{macro}[EXP]{\msg_show_item:nn} % \begin{macro}[EXP]{\msg_show_item_unbraced:nn} % Each item in the variable is formatted using one of the following % functions. We cannot use |\\| and so on because these short-hands % cannot be used inside the arguments of messages, only when defining % the messages. % \begin{macrocode} \cs_new:Npx \msg_show_item:n #1 { \iow_newline: > ~ \c_space_tl \exp_not:N \tl_to_str:n { {#1} } } \cs_new:Npx \msg_show_item_unbraced:n #1 { \iow_newline: > ~ \c_space_tl \exp_not:N \tl_to_str:n {#1} } \cs_new:Npx \msg_show_item:nn #1#2 { \iow_newline: > \use:nn { ~ } { ~ } \exp_not:N \tl_to_str:n { {#1} } \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ } \exp_not:N \tl_to_str:n { {#2} } } \cs_new:Npx \msg_show_item_unbraced:nn #1#2 { \iow_newline: > \use:nn { ~ } { ~ } \exp_not:N \tl_to_str:n {#1} \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ } \exp_not:N \tl_to_str:n {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3prg}} % % \begin{macrocode} %<@@=bool> % \end{macrocode} % % \begin{macro}[added = 2018-05-10] % {\bool_set_inverse:N, \bool_set_inverse:c, \bool_gset_inverse:N, \bool_gset_inverse:c} % Set to \texttt{false} or \texttt{true} locally or globally. % \begin{macrocode} \cs_new_protected:Npn \bool_set_inverse:N #1 { \bool_if:NTF #1 { \bool_set_false:N } { \bool_set_true:N } #1 } \cs_generate_variant:Nn \bool_set_inverse:N { c } \cs_new_protected:Npn \bool_gset_inverse:N #1 { \bool_if:NTF #1 { \bool_gset_false:N } { \bool_gset_true:N } #1 } \cs_generate_variant:Nn \bool_gset_inverse:N { c } % \end{macrocode} % \end{macro} % % \begin{variable}{\s_@@_mark,\s_@@_stop} % Internal scan marks. % \begin{macrocode} \scan_new:N \s_@@_mark \scan_new:N \s_@@_stop % \end{macrocode} % \end{variable} % % \begin{macro}[EXP, noTF]{\bool_case_true:n, \bool_case_false:n} % \begin{macro}{\@@_case:NnTF} % \begin{macro}{\@@_case_true:w, \@@_case_false:w, \@@_case_end:nw} % For boolean cases the overall idea is the same as for % \cs{tl_case:nnTF} as described in \pkg{l3tl}. % \begin{macrocode} \cs_new:Npn \bool_case_true:nTF { \exp:w \@@_case:NnTF \c_true_bool } \cs_new:Npn \bool_case_true:nT #1#2 { \exp:w \@@_case:NnTF \c_true_bool {#1} {#2} { } } \cs_new:Npn \bool_case_true:nF #1 { \exp:w \@@_case:NnTF \c_true_bool {#1} { } } \cs_new:Npn \bool_case_true:n #1 { \exp:w \@@_case:NnTF \c_true_bool {#1} { } { } } \cs_new:Npn \bool_case_false:nTF { \exp:w \@@_case:NnTF \c_false_bool } \cs_new:Npn \bool_case_false:nT #1#2 { \exp:w \@@_case:NnTF \c_false_bool {#1} {#2} { } } \cs_new:Npn \bool_case_false:nF #1 { \exp:w \@@_case:NnTF \c_false_bool {#1} { } } \cs_new:Npn \bool_case_false:n #1 { \exp:w \@@_case:NnTF \c_false_bool {#1} { } { } } \cs_new:Npn \@@_case:NnTF #1#2#3#4 { \bool_if:NTF #1 \@@_case_true:w \@@_case_false:w #2 #1 { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop } \cs_new:Npn \@@_case_true:w #1#2 { \bool_if:nTF {#1} { \@@_case_end:nw {#2} } { \@@_case_true:w } } \cs_new:Npn \@@_case_false:w #1#2 { \bool_if:nTF {#1} { \@@_case_false:w } { \@@_case_end:nw {#2} } } \cs_new:Npn \@@_case_end:nw #1#2#3 \s_@@_mark #4#5 \s_@@_stop { \exp_end: #1 #4 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3prop}} % % \begin{macrocode} %<@@=prop> % \end{macrocode} % % \begin{macro}[EXP]{\@@_use_i_delimit_by_s_stop:nw} % Functions to gobble up to a scan mark. % \begin{macrocode} \cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1} % \end{macrocode} % \end{macro} % % \begin{macro}[EXP] % {\prop_rand_key_value:N, \prop_rand_key_value:c} % \begin{macro}[EXP]{\@@_rand_item:w} % Contrarily to |clist|, |seq| and |tl|, there is no function to get % an item of a |prop| given an integer between $1$ and the number of % items, so we write the appropriate code. There is no bounds % checking because \cs{int_rand:nn} is always within bounds. The % initial \cs{int_value:w} is stopped by the first \cs{s_@@} in~|#1|. % \begin{macrocode} \cs_new:Npn \prop_rand_key_value:N #1 { \prop_if_empty:NF #1 { \exp_after:wN \@@_rand_item:w \int_value:w \int_rand:nn { 1 } { \prop_count:N #1 } #1 \s_@@_stop } } \cs_generate_variant:Nn \prop_rand_key_value:N { c } \cs_new:Npn \@@_rand_item:w #1 \s_@@ \@@_pair:wn #2 \s_@@ #3 { \int_compare:nNnF {#1} > 1 { \@@_use_i_delimit_by_s_stop:nw { \exp_not:n { {#2} {#3} } } } \exp_after:wN \@@_rand_item:w \int_value:w \int_eval:n { #1 - 1 } \s_@@ } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3seq}} % % \begin{macrocode} %<@@=seq> % \end{macrocode} % % \begin{macro} % { % \seq_mapthread_function:NNN, \seq_mapthread_function:NcN, % \seq_mapthread_function:cNN, \seq_mapthread_function:ccN % } % \begin{macro} % { % \@@_mapthread_function:wNN, \@@_mapthread_function:wNw, % \@@_mapthread_function:Nnnwnn % } % The idea is to first expand both sequences, adding the % usual |{ ? \prg_break: } { }| to the end of each one. This is % most conveniently done in two steps using an auxiliary function. % The mapping then throws away the first tokens of |#2| and |#5|, % which for items in both sequences are \cs{s_@@} % \cs{@@_item:n}. The function to be mapped are then be applied to % the two entries. When the code hits the end of one of the % sequences, the break material stops the entire loop and tidy up. % This avoids needing to find the count of the two sequences, or % worrying about which is longer. % \begin{macrocode} \cs_new:Npn \seq_mapthread_function:NNN #1#2#3 { \exp_after:wN \@@_mapthread_function:wNN #2 \s_@@_stop #1 #3 } \cs_new:Npn \@@_mapthread_function:wNN \s_@@ #1 \s_@@_stop #2#3 { \exp_after:wN \@@_mapthread_function:wNw #2 \s_@@_stop #3 #1 { ? \prg_break: } { } \prg_break_point: } \cs_new:Npn \@@_mapthread_function:wNw \s_@@ #1 \s_@@_stop #2 { \@@_mapthread_function:Nnnwnn #2 #1 { ? \prg_break: } { } \s_@@_stop } \cs_new:Npn \@@_mapthread_function:Nnnwnn #1#2#3#4 \s_@@_stop #5#6 { \use_none:n #2 \use_none:n #5 #1 {#3} {#6} \@@_mapthread_function:Nnnwnn #1 #4 \s_@@_stop } \cs_generate_variant:Nn \seq_mapthread_function:NNN { Nc , c , cc } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_set_filter:NNn, \seq_gset_filter:NNn} % \begin{macro}{\@@_set_filter:NNNn} % Similar to \cs{seq_map_inline:Nn}, without a % \cs{prg_break_point:} because the user's code % is performed within the evaluation of a boolean expression, % and skipping out of that would break horribly. % The \cs{@@_wrap_item:n} function inserts the relevant % \cs{@@_item:n} without expansion in the input stream, % hence in the \texttt{x}-expanding assignment. % \begin{macrocode} \cs_new_protected:Npn \seq_set_filter:NNn { \@@_set_filter:NNNn \__kernel_tl_set:Nx } \cs_new_protected:Npn \seq_gset_filter:NNn { \@@_set_filter:NNNn \__kernel_tl_gset:Nx } \cs_new_protected:Npn \@@_set_filter:NNNn #1#2#3#4 { \@@_push_item_def:n { \bool_if:nT {#4} { \@@_wrap_item:n {##1} } } #1 #2 { #3 } \@@_pop_item_def: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_set_from_inline_x:Nnn, \seq_gset_from_inline_x:Nnn} % \begin{macro}{\@@_set_from_inline_x:NNnn} % Set \cs{@@_item:n} then map it using the loop code. % \begin{macrocode} \cs_new_protected:Npn \seq_set_from_inline_x:Nnn { \@@_set_from_inline_x:NNnn \__kernel_tl_set:Nx } \cs_new_protected:Npn \seq_gset_from_inline_x:Nnn { \@@_set_from_inline_x:NNnn \__kernel_tl_gset:Nx } \cs_new_protected:Npn \@@_set_from_inline_x:NNnn #1#2#3#4 { \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} } #1 #2 { \s_@@ #3 \@@_item:n } \@@_pop_item_def: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_set_from_function:NnN, \seq_gset_from_function:NnN} % Reuse \cs{seq_set_from_inline_x:Nnn}. % \begin{macrocode} \cs_new_protected:Npn \seq_set_from_function:NnN #1#2#3 { \seq_set_from_inline_x:Nnn #1 {#2} { #3 {##1} } } \cs_new_protected:Npn \seq_gset_from_function:NnN #1#2#3 { \seq_gset_from_inline_x:Nnn #1 {#2} { #3 {##1} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_int_eval:w} % Useful to more quickly go through items. % \begin{macrocode} \cs_new_eq:NN \@@_int_eval:w \tex_numexpr:D % \end{macrocode} % \end{macro} % % \begin{macro}[noTF]{\seq_set_item:Nnn, \seq_set_item:cnn, \seq_gset_item:Nnn, \seq_gset_item:cnn} % \begin{macro}{\@@_set_item:NnnNN, \@@_set_item:nnNNNN, \@@_set_item_false:nnNNNN, \@@_set_item:nNnnNNNN} % \begin{macro}[rEXP]{\@@_set_item:wn, \@@_set_item_end:w} % The conditionals are distinguished from the |Nnn| versions by the % last argument \cs{use_ii:nn} vs \cs{use_i:nn}. % \begin{macrocode} \cs_new_protected:Npn \seq_set_item:Nnn #1#2#3 { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_i:nn } \cs_new_protected:Npn \seq_gset_item:Nnn #1#2#3 { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_i:nn } \cs_generate_variant:Nn \seq_set_item:Nnn { c } \cs_generate_variant:Nn \seq_gset_item:Nnn { c } \prg_new_protected_conditional:Npnn \seq_set_item:Nnn #1#2#3 { TF , T , F } { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_ii:nn } \prg_new_protected_conditional:Npnn \seq_gset_item:Nnn #1#2#3 { TF , T , F } { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_ii:nn } \prg_generate_conditional_variant:Nnn \seq_set_item:Nnn { c } { TF , T , F } \prg_generate_conditional_variant:Nnn \seq_gset_item:Nnn { c } { TF , T , F } % \end{macrocode} % Save the item to be stored and evaluate the position and the sequence % length only once. Then depending on the sign of the position, check % that it is not bigger than the length (in absolute value) nor zero. % \begin{macrocode} \cs_new_protected:Npn \@@_set_item:NnnNN #1#2#3 { \tl_set:Nn \l_@@_internal_a_tl { \@@_item:n {#3} } \exp_args:Nff \@@_set_item:nnNNNN { \int_eval:n {#2} } { \seq_count:N #1 } #1 \use_none:nn } \cs_new_protected:Npn \@@_set_item:nnNNNN #1#2 { \int_compare:nNnTF {#1} > 0 { \int_compare:nNnF {#1} > {#2} { \@@_set_item:nNnnNNNN { #1 - 1 } } } { \int_compare:nNnF {#1} < {-#2} { \int_compare:nNnF {#1} = 0 { \@@_set_item:nNnnNNNN { #2 + #1 } } } } \@@_set_item_false:nnNNNN {#1} {#2} } % \end{macrocode} % If the position is not ok, \cs{@@_set_item_false:nnNNNN} calls an % error or returns \texttt{false} (depending on the \cs{use_i:nn} vs % \cs{use_ii:nn} argument mentioned above). % \begin{macrocode} \cs_new_protected:Npn \@@_set_item_false:nnNNNN #1#2#3#4#5#6 { #6 { \msg_error:nnxxx { seq } { item-too-large } { \token_to_str:N #3 } {#2} {#1} } { \prg_return_false: } } \msg_new:nnnn { seq } { item-too-large } { Sequence~'#1'~does~not~have~an~item~#3 } { An~attempt~was~made~to~push~or~pop~the~item~at~position~#3~ of~'#1',~but~this~ \int_compare:nTF { #3 = 0 } { position~does~not~exist. } { sequence~only~has~#2~item \int_compare:nF { #2 = 1 } {s}. } } % \end{macrocode} % If the position is ok, \cs{@@_set_item:nNnnNNNN} makes the assignment % and returns \texttt{true} (in the case of conditionnals). Here |#1| % is an integer expression (position minus one), it needs to be % evaluated. The sequence |#5| starts with \cs{s_@@} (even if empty), % which stops the integer expression and is absorbed by it. The % \cs{if_meaning:w} test is slightly faster than an integer test (but % only works when testing against zero, hence the offset we chose in % the position). When we are done skipping items, insert the saved % item \cs{l_@@_internal_a_tl}. For |put| functions the last argument % of \cs{@@_set_item_end:w} is \cs{use_none:nn} and it absorbs the % item |#2| that we are removing: this is only useful for the |pop| % functions. % \begin{macrocode} \cs_new_protected:Npn \@@_set_item:nNnnNNNN #1#2#3#4#5#6#7#8 { #7 #5 { \s_@@ \exp_after:wN \@@_set_item:wn \int_value:w \@@_int_eval:w #1 #5 \s_@@_stop #6 } #8 { } { \prg_return_true: } } \cs_new:Npn \@@_set_item:wn #1 \@@_item:n #2 { \if_meaning:w 0 #1 \@@_set_item_end:w \fi: \exp_not:n { \@@_item:n {#2} } \exp_after:wN \@@_set_item:wn \int_value:w \@@_int_eval:w #1 - 1 \s_@@ } \cs_new:Npn \@@_set_item_end:w #1 \exp_not:n #2 #3 \s_@@ #4 \s_@@_stop #5 { #1 \exp_not:o \l_@@_internal_a_tl \exp_not:n {#4} #5 #2 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[noTF]{\seq_pop_item:NnN, \seq_pop_item:cnN, \seq_gpop_item:NnN, \seq_gpop_item:cnN} % \begin{macro}{\@@_pop_item:NnNNN} % The |NnN| versions simply call the conditionals, for which we will % rely on the internals of \cs{seq_set_item:Nnn}. % \begin{macrocode} \cs_new_protected:Npn \seq_pop_item:NnN #1#2#3 { \seq_pop_item:NnNTF #1 {#2} #3 { } { } } \cs_new_protected:Npn \seq_gpop_item:NnN #1#2#3 { \seq_gpop_item:NnNTF #1 {#2} #3 { } { } } \cs_generate_variant:Nn \seq_pop_item:NnN { c } \cs_generate_variant:Nn \seq_gpop_item:NnN { c } \prg_new_protected_conditional:Npnn \seq_pop_item:NnN #1#2#3 { TF , T , F } { \@@_pop_item:NnNN #1 {#2} #3 \__kernel_tl_set:Nx } \prg_new_protected_conditional:Npnn \seq_gpop_item:NnN #1#2#3 { TF , T , F } { \@@_pop_item:NnNN #1 {#2} #3 \__kernel_tl_gset:Nx } \prg_generate_conditional_variant:Nnn \seq_pop_item:NnN { c } { TF , T , F } \prg_generate_conditional_variant:Nnn \seq_gpop_item:NnN { c } { TF , T , F } % \end{macrocode} % Save in \cs{l_@@_internal_b_tl} the token list variable |#3| in % which we will store the item. The \cs{@@_set_item:nnNNNN} auxiliary % eventually inserts \cs{l_@@_internal_a_tl} in place of the item % found in the sequence, so we empty that. Instead of the last % argument \cs{use_i:nn} or \cs{use_ii:nn} used for |put| functions, % we introduce \cs{@@_pop_item:nn}, which stores \cs{q_no_value} % before calling its second argument % (\cs{prg_return_true:}/\texttt{false:}) to end the conditional. The % item found is passed to \cs{@@_pop_item_aux:w}, which interrupts the % |x|-expanding sequence assignment and stores the item using the % assignment function in \cs{l_@@_internal_b_tl}. % \begin{macrocode} \cs_new_protected:Npn \@@_pop_item:NnNN #1#2#3#4 { \tl_clear:N \l_@@_internal_a_tl \tl_set:Nn \l_@@_internal_b_tl { \__kernel_tl_set:Nx #3 } \exp_args:Nff \@@_set_item:nnNNNN { \int_eval:n {#2} } { \seq_count:N #1 } #1 \@@_pop_item_aux:w #4 \@@_pop_item:nn } \cs_new_protected:Npn \@@_pop_item:nn #1#2 { \if_meaning:w \prg_return_false: #2 \l_@@_internal_b_tl { \exp_not:N \q_no_value } \fi: #2 } \cs_new:Npn \@@_pop_item_aux:w \@@_item:n #1 { \if_false: { \fi: } \l_@@_internal_b_tl { \if_false: } \fi: \exp_not:n {#1} } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3sys}} % % \begin{macrocode} %<@@=sys> % \end{macrocode} % % \begin{variable}{\c_sys_engine_version_str} % Various different engines, various different ways to extract the % data! % \begin{macrocode} \str_const:Nx \c_sys_engine_version_str { \str_case:on \c_sys_engine_str { { pdftex } { \fp_eval:n { round(\int_use:N \tex_pdftexversion:D / 100 , 2) } . \tex_pdftexrevision:D } { ptex } { \cs_if_exist:NT \tex_ptexversion:D { p \int_use:N \tex_ptexversion:D . \int_use:N \tex_ptexminorversion:D \tex_ptexrevision:D - \int_use:N \tex_epTeXversion:D } } { luatex } { \fp_eval:n { round(\int_use:N \tex_luatexversion:D / 100, 2) } . \tex_luatexrevision:D } { uptex } { \cs_if_exist:NT \tex_ptexversion:D { p \int_use:N \tex_ptexversion:D . \int_use:N \tex_ptexminorversion:D \tex_ptexrevision:D - u \int_use:N \tex_uptexversion:D \tex_uptexrevision:D - \int_use:N \tex_epTeXversion:D } } { xetex } { \int_use:N \tex_XeTeXversion:D \tex_XeTeXrevision:D } } } % \end{macrocode} % \end{variable} % % \subsection{Additions to \pkg{l3file}} % % \begin{macrocode} %<@@=ior> % \end{macrocode} % % \begin{macro}{\ior_shell_open:Nn} % \begin{macro}{\@@_shell_open:nN} % Actually much easier than either the standard open or input versions! % When calling \cs{__kernel_ior_open:Nn} the file the pipe is added to % signal a shell command, but the quotes are not added yet---they are % added later by \cs{__kernel_file_name_quote:n}. % \begin{macrocode} \cs_new_protected:Npn \ior_shell_open:Nn #1#2 { \sys_if_shell:TF { \exp_args:No \@@_shell_open:nN { \tl_to_str:n {#2} } #1 } { \msg_error:nn { ior } { pipe-failed } } } \cs_new_protected:Npn \@@_shell_open:nN #1#2 { \tl_if_in:nnTF {#1} { " } { \msg_error:nnx { kernel } { quote-in-shell } {#1} } { \__kernel_ior_open:Nn #2 { |#1 } } } \msg_new:nnnn { ior } { pipe-failed } { Cannot~run~piped~system~commands. } { LaTeX~tried~to~call~a~system~process~but~this~was~not~possible.\\ Try~the~"--shell-escape"~(or~"--enable-pipes")~option. } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3tl}} % % \subsubsection{Building a token list} % % \begin{macrocode} %<@@=tl> % \end{macrocode} % % Between \cs{tl_build_begin:N} \meta{tl~var} and \cs{tl_build_end:N} % \meta{tl~var}, the \meta{tl~var} has the structure % \begin{quote} % \cs{exp_end:} \ldots{} \cs{exp_end:} \cs{@@_build_last:NNn} % \meta{assignment} \meta{next~tl} \Arg{left} \meta{right} % \end{quote} % where \meta{right} is not braced. The \enquote{data} it represents is % \meta{left} followed by the \enquote{data} of \meta{next~tl} followed % by \meta{right}. The \meta{next~tl} is a token list variable whose % name is that of \meta{tl~var} followed by~|'|. There are between $0$ % and $4$ \cs{exp_end:} to keep track of when \meta{left} and % \meta{right} should be put into the \meta{next~tl}. The % \meta{assignment} is \cs{cs_set_nopar:Npx} if the variable is local, % and \cs{cs_gset_nopar:Npx} if it is global. % % \begin{macro}{\tl_build_begin:N, \tl_build_gbegin:N} % \begin{macro}{\@@_build_begin:NN, \@@_build_begin:NNN} % First construct the \meta{next~tl}: using a prime here conflicts % with the usual \pkg{expl3} convention but we need a name that can be % derived from |#1| without any external data such as a counter. % Empty that \meta{next~tl} and setup the structure. The local and % global versions only differ by a single function % \cs[no-index]{cs_(g)set_nopar:Npx} used for all assignments: this is % important because only that function is stored in the \meta{tl~var} % and \meta{next~tl} for subsequent assignments. In principle % \cs{@@_build_begin:NNN} could use \cs[no-index]{tl_(g)clear_new:N} % to empty |#1| and make sure it is defined, but logging the % definition does not seem useful so we just do |#3| |#1| |{}| to % clear it locally or globally as appropriate. % \begin{macrocode} \cs_new_protected:Npn \tl_build_begin:N #1 { \@@_build_begin:NN \cs_set_nopar:Npx #1 } \cs_new_protected:Npn \tl_build_gbegin:N #1 { \@@_build_begin:NN \cs_gset_nopar:Npx #1 } \cs_new_protected:Npn \@@_build_begin:NN #1#2 { \exp_args:Nc \@@_build_begin:NNN { \cs_to_str:N #2 ' } #2 #1 } \cs_new_protected:Npn \@@_build_begin:NNN #1#2#3 { #3 #1 { } #3 #2 { \exp_not:n { \exp_end: \exp_end: \exp_end: \exp_end: } \exp_not:n { \@@_build_last:NNn #3 #1 { } } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_build_clear:N, \tl_build_gclear:N} % The |begin| and |gbegin| functions already clear enough to make the % token list variable effectively empty. Eventually the |begin| and % |gbegin| functions should check that |#1'| is empty or undefined, % while the |clear| and |gclear| functions ought to empty |#1'|, % |#1''| and so on, similar to \cs{tl_build_end:N}. This only affects % memory usage. % \begin{macrocode} \cs_new_eq:NN \tl_build_clear:N \tl_build_begin:N \cs_new_eq:NN \tl_build_gclear:N \tl_build_gbegin:N % \end{macrocode} % \end{macro} % % \begin{macro} % { % \tl_build_put_right:Nn, \tl_build_put_right:Nx, % \tl_build_gput_right:Nn, \tl_build_gput_right:Nx, % \@@_build_last:NNn, \@@_build_put:nn, \@@_build_put:nw % } % Similar to \cs{tl_put_right:Nn}, but apply \cs{exp:w} to |#1|. Most % of the time this just removes one \cs{exp_end:}. When there are % none left, \cs{@@_build_last:NNn} is expanded instead. It resets % the definition of the \meta{tl~var} by ending the \cs{exp_not:n} and % the definition early. Then it makes sure the \meta{next~tl} (its % argument |#1|) is set-up and starts a new definition. Then % \cs{@@_build_put:nn} and \cs{@@_build_put:nw} place the \meta{left} % part of the original \meta{tl~var} as appropriate for the definition % of the \meta{next~tl} (the \meta{right} part is left in the right % place without ever becoming a macro argument). We use % \cs{exp_after:wN} rather than some \cs{exp_args:No} to avoid reading % arguments that are likely very long token lists. We use % \cs[no-index]{cs_(g)set_nopar:Npx} rather than % \cs[no-index]{tl_(g)set:Nx} partly for the same reason and partly % because the assignments are interrupted by brace tricks, which % implies that the assignment does not simply set the token list to an % |x|-expansion of the second argument. % \begin{macrocode} \cs_new_protected:Npn \tl_build_put_right:Nn #1#2 { \cs_set_nopar:Npx #1 { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 #2 } } } \cs_new_protected:Npn \tl_build_put_right:Nx #1#2 { \cs_set_nopar:Npx #1 { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 } #2 } } \cs_new_protected:Npn \tl_build_gput_right:Nn #1#2 { \cs_gset_nopar:Npx #1 { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 #2 } } } \cs_new_protected:Npn \tl_build_gput_right:Nx #1#2 { \cs_gset_nopar:Npx #1 { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 } #2 } } \cs_new_protected:Npn \@@_build_last:NNn #1#2 { \if_false: { { \fi: \exp_end: \exp_end: \exp_end: \exp_end: \exp_end: \@@_build_last:NNn #1 #2 { } } } \if_meaning:w \c_empty_tl #2 \@@_build_begin:NN #1 #2 \fi: #1 #2 { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w \if_false: } } \fi: \exp_after:wN \@@_build_put:nn \exp_after:wN {#2} } \cs_new_protected:Npn \@@_build_put:nn #1#2 { \@@_build_put:nw {#2} #1 } \cs_new_protected:Npn \@@_build_put:nw #1#2 \@@_build_last:NNn #3#4#5 { #2 \@@_build_last:NNn #3 #4 { #1 #5 } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \tl_build_put_left:Nn, \tl_build_put_left:Nx, % \tl_build_gput_left:Nn, \tl_build_gput_left:Nx, \@@_build_put_left:NNn % } % See \cs{tl_build_put_right:Nn} for all the machinery. We could % easily provide \cs[no-index]{tl_build_put_left_right:Nnn}, by just % add the \meta{right} material after the \Arg{left} in the % |x|-expanding assignment. % \begin{macrocode} \cs_new_protected:Npn \tl_build_put_left:Nn #1 { \@@_build_put_left:NNn \cs_set_nopar:Npx #1 } \cs_generate_variant:Nn \tl_build_put_left:Nn { Nx } \cs_new_protected:Npn \tl_build_gput_left:Nn #1 { \@@_build_put_left:NNn \cs_gset_nopar:Npx #1 } \cs_generate_variant:Nn \tl_build_gput_left:Nn { Nx } \cs_new_protected:Npn \@@_build_put_left:NNn #1#2#3 { #1 #2 { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w \exp_after:wN \@@_build_put:nn \exp_after:wN {#2} {#3} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_build_get:NN} % \begin{macro}{\@@_build_get:NNN, \@@_build_get:w, \@@_build_get_end:w} % The idea is to expand the \meta{tl~var} then the \meta{next~tl} and % so on, all within an |x|-expanding assignment, and wrap as % appropriate in \cs{exp_not:n}. The various \meta{left} parts are % left in the assignment as we go, which enables us to expand the % \meta{next~tl} at the right place. The various \meta{right} parts % are eventually picked up in one last \cs{exp_not:n}, with a brace % trick to wrap all the \meta{right} parts together. % \begin{macrocode} \cs_new_protected:Npn \tl_build_get:NN { \@@_build_get:NNN \__kernel_tl_set:Nx } \cs_new_protected:Npn \@@_build_get:NNN #1#2#3 { #1 #3 { \if_false: { \fi: \exp_after:wN \@@_build_get:w #2 } } } \cs_new:Npn \@@_build_get:w #1 \@@_build_last:NNn #2#3#4 { \exp_not:n {#4} \if_meaning:w \c_empty_tl #3 \exp_after:wN \@@_build_get_end:w \fi: \exp_after:wN \@@_build_get:w #3 } \cs_new:Npn \@@_build_get_end:w #1#2#3 { \exp_after:wN \exp_not:n \exp_after:wN { \if_false: } \fi: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_build_end:N, \tl_build_gend:N, \@@_build_end_loop:NN} % Get the data then clear the \meta{next~tl} recursively until finding % an empty one. It is perhaps wasteful to repeatedly use % \cs{cs_to_str:N}. The local/global scope is checked by % \cs{tl_set:Nx} or \cs{tl_gset:Nx}. % \begin{macrocode} \cs_new_protected:Npn \tl_build_end:N #1 { \@@_build_get:NNN \__kernel_tl_set:Nx #1 #1 \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_clear:N } \cs_new_protected:Npn \tl_build_gend:N #1 { \@@_build_get:NNN \__kernel_tl_gset:Nx #1 #1 \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_gclear:N } \cs_new_protected:Npn \@@_build_end_loop:NN #1#2 { \if_meaning:w \c_empty_tl #1 \exp_after:wN \use_none:nnnnnn \fi: #2 #1 \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } #2 } % \end{macrocode} % \end{macro} % % \subsubsection{Other additions to \pkg{l3tl}} % % \begin{macro}{\tl_range_braced:Nnn, \tl_range_braced:cnn, \tl_range_braced:nnn} % \begin{macro} % {\tl_range_unbraced:Nnn, \tl_range_unbraced:cnn, \tl_range_unbraced:nnn} % \begin{macro} % { % \@@_range_braced:w, \@@_range_collect_braced:w, % \@@_range_unbraced:w, \@@_range_collect_unbraced:w, % } % For the braced version \cs{@@_range_braced:w} sets up % \cs{@@_range_collect_braced:w} which stores items one by one in an % argument after the semicolon. The unbraced version is almost % identical. The version preserving braces and spaces starts by % deleting spaces before the argument to avoid collecting them, and % sets up \cs{@@_range_collect:nn} with a first argument of the form % |{| \Arg{collected} \meta{tokens} |}|, whose head is the collected % tokens and whose tail is what remains of the original token list. % This form makes it easier to move tokens to the \meta{collected} % tokens. % \begin{macrocode} \cs_new:Npn \tl_range_braced:Nnn { \exp_args:No \tl_range_braced:nnn } \cs_generate_variant:Nn \tl_range_braced:Nnn { c } \cs_new:Npn \tl_range_braced:nnn { \@@_range:Nnnn \@@_range_braced:w } \cs_new:Npn \tl_range_unbraced:Nnn { \exp_args:No \tl_range_unbraced:nnn } \cs_generate_variant:Nn \tl_range_unbraced:Nnn { c } \cs_new:Npn \tl_range_unbraced:nnn { \@@_range:Nnnn \@@_range_unbraced:w } \cs_new:Npn \@@_range_braced:w #1 ; #2 { \@@_range_collect_braced:w #1 ; { } #2 } \cs_new:Npn \@@_range_unbraced:w #1 ; #2 { \@@_range_collect_unbraced:w #1 ; { } #2 } \cs_new:Npn \@@_range_collect_braced:w #1 ; #2#3 { \if_int_compare:w #1 > \c_one_int \exp_after:wN \@@_range_collect_braced:w \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ; \fi: { #2 {#3} } } \cs_new:Npn \@@_range_collect_unbraced:w #1 ; #2#3 { \if_int_compare:w #1 > \c_one_int \exp_after:wN \@@_range_collect_unbraced:w \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ; \fi: { #2 #3 } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3token}} % % \begin{variable}{\c_catcode_active_space_tl} % While \cs{char_generate:nn} can produce active characters in some % engines it cannot in general. It would be possible to simply change % the catcode of space but then the code would need to avoid all % spaces, making it quite unreadable. Instead we use the primitive % \cs{tex_lowercase:D} trick. % \begin{macrocode} \group_begin: \char_set_catcode_active:N * \char_set_lccode:nn { `* } { `\ } \tex_lowercase:D { \tl_const:Nn \c_catcode_active_space_tl { * } } \group_end: % \end{macrocode} % \end{variable} % % \begin{macrocode} %<@@=peek> % \end{macrocode} % % \begin{variable}{\l_@@_collect_tl} % \begin{macrocode} \tl_new:N \l_@@_collect_tl % \end{macrocode} % \end{variable} % % \begin{macro} % { % \peek_catcode_collect_inline:Nn, % \peek_charcode_collect_inline:Nn, % \peek_meaning_collect_inline:Nn % } % \begin{macro} % { % \@@_collect:NNn, \@@_collect_true:w, % \@@_collect_remove:nw, \@@_collect:N % } % Most of the work is done by \cs{@@_execute_branches_\ldots{}:}, % which calls either \cs{@@_true:w} or \cs{@@_false:w} according to % whether the next token \cs{l_peek_token} matches the search token % (stored in \cs{l_@@_search_token} and \cs{l_@@_search_tl}). % Here, in the \texttt{true} case we run \cs{@@_collect_true:w}, % which generally calls \cs{@@_collect:N} to store the peeked token % into \cs{l_@@_collect_tl}, except in special non-\texttt{N}-type % cases (begin-group, end-group, or space), where a frozen token is % stored. The \texttt{true} branch calls % \cs{@@_execute_branches_\ldots{}:} to fetch more matching tokens. % Once there are no more, \cs{@@_false_aux:n} closes the safe-align % group and runs the user's inline code. % \begin{macrocode} \cs_new_protected:Npn \peek_catcode_collect_inline:Nn { \@@_collect:NNn \@@_execute_branches_catcode: } \cs_new_protected:Npn \peek_charcode_collect_inline:Nn { \@@_collect:NNn \@@_execute_branches_charcode: } \cs_new_protected:Npn \peek_meaning_collect_inline:Nn { \@@_collect:NNn \@@_execute_branches_meaning: } \cs_new_protected:Npn \@@_collect:NNn #1#2#3 { \group_align_safe_begin: \cs_set_eq:NN \l_@@_search_token #2 \tl_set:Nn \l_@@_search_tl {#2} \tl_clear:N \l_@@_collect_tl \cs_set:Npn \@@_false:w { \exp_args:No \@@_false_aux:n \l_@@_collect_tl } \cs_set:Npn \@@_false_aux:n ##1 { \group_align_safe_end: #3 } \cs_set_eq:NN \@@_true:w \@@_collect_true:w \cs_set:Npn \@@_true_aux:w { \peek_after:Nw #1 } \@@_true_aux:w } \cs_new_protected:Npn \@@_collect_true:w { \if_case:w \if_catcode:w \exp_not:N \l_peek_token { 1 \exp_stop_f: \fi: \if_catcode:w \exp_not:N \l_peek_token } 2 \exp_stop_f: \fi: \if_meaning:w \l_peek_token \c_space_token 3 \exp_stop_f: \fi: 0 \exp_stop_f: \exp_after:wN \@@_collect:N \or: \@@_collect_remove:nw { \c_group_begin_token } \or: \@@_collect_remove:nw { \c_group_end_token } \or: \@@_collect_remove:nw { ~ } \fi: } \cs_new_protected:Npn \@@_collect:N #1 { \tl_put_right:Nn \l_@@_collect_tl {#1} \@@_true_aux:w } \cs_new_protected:Npn \@@_collect_remove:nw #1 { \tl_put_right:Nn \l_@@_collect_tl {#1} \exp_after:wN \@@_true_remove:w } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex