% \iffalse meta-comment % %% File: l3candidates.dtx % % Copyright (C) 2012-2023 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 2023-06-30} % % \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{l3seq}} % % \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} % % \section{Additions to \pkg{l3tl}} % % \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} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3candidates} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Additions to \pkg{l3seq}} % % \begin{macrocode} %<@@=seq> % \end{macrocode} % % \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} % % \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} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex