% \iffalse meta-comment
%
%% File: l3tl-build.dtx
%
% Copyright (C) 2018-2026 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{l3tl-build} module\\ Piecewise \texttt{tl} constructions^^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 2026-04-28}
%
% \maketitle
%
% \begin{documentation}
%
% \section{Constructing \meta{tl~var} by accumulation}
%
% When creating a \meta{tl~var} by accumulation of many tokens, the performance
% available using a combination of \cs{tl_set:Nn} and \cs{tl_put_right:Nn} or
% similar begins to become an issue. To address this, a set of functions are
% available to \enquote{build} a \meta{tl~var}. The performance of this approach
% is much more efficient than the standard \cs{tl_put_right:Nn}, but the
% constructed token list cannot be accessed during construction other than
% by methods provided in this section.
%
% Whilst the exact performance difference is dependent on the size of each
% added block of tokens and the total number of blocks, in general, the
% \cs[no-index]{tl_build_(g)put...} functions will out-perform the basic
% \cs[no-index]{tl_(g)put...} equivalent if more than 100 non-empty addition
% operations occur. See
% \url{https://github.com/latex3/latex3/issues/1393#issuecomment-1880164756}
% for a more detailed analysis.
%
% \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.  Until \cs{tl_build_end:N}
%   \meta{tl~var} or \cs{tl_build_gend: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_put_left:Nn, \tl_build_put_left:Ne,
%     \tl_build_gput_left:Nn, \tl_build_gput_left:Ne,
%     \tl_build_put_right:Nn, \tl_build_put_right:Ne,
%     \tl_build_gput_right:Nn, \tl_build_gput_right:Ne
%   }
%   \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_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. After the action of
%   |end|/|gend|, the \meta{tl~var} may be manipulated using standard
%   \texttt{tl} functions.
% \end{function}
%
% \begin{function}[added = 2023-12-14]{\tl_build_get_intermediate:NN}
%   \begin{syntax}
%     \cs{tl_build_get_intermediate: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}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3tl-build} implementation}
%
%    \begin{macrocode}
%<*code>
%    \end{macrocode}
%
%    \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:Npe} if the variable is local,
% and \cs{cs_gset_nopar:Npe} 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:Npe} 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:Npe #1 }
\cs_new_protected:Npn \tl_build_gbegin:N #1
  { \@@_build_begin:NN \cs_gset_nopar:Npe #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_put_right:Nn, \tl_build_put_right:Ne, \tl_build_put_right:Nx,
%     \tl_build_gput_right:Nn, \tl_build_gput_right:Ne, \tl_build_gput_right:Nx
%   }
% \begin{macro}{\@@_build_last:NNn}
% \begin{macro}{\@@_build_put:nn}
% \begin{macro}{\@@_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:Npe} rather than
%   \cs[no-index]{tl_(g)set:Ne} 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
%   |e|-expansion of the second argument.
%    \begin{macrocode}
\cs_new_protected:Npn \tl_build_put_right:Nn #1#2
  {
    \cs_set_nopar:Npe #1
      { \__kernel_exp_not:w \exp_after:wN { \exp:w #1 #2 } }
  }
\cs_generate_variant:Nn \tl_build_put_right:Nn { Ne , Nx }
\cs_new_protected:Npn \tl_build_gput_right:Nn #1#2
  {
    \cs_gset_nopar:Npe #1
      { \__kernel_exp_not:w \exp_after:wN { \exp:w #1 #2 } }
  }
\cs_generate_variant:Nn \tl_build_gput_right:Nn { Ne , Nx }
\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
      {
        \__kernel_exp_not:w \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}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}
%   {
%     \tl_build_put_left:Nn, \tl_build_put_left:Ne, \tl_build_put_left:Nx,
%     \tl_build_gput_left:Nn, \tl_build_gput_left:Ne, \tl_build_gput_left:Nx
%   }
% \begin{macro}{\@@_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
%   adding the \meta{right} material after the \Arg{left} in the
%   |e|-expanding assignment.
%    \begin{macrocode}
\cs_new_protected:Npn \tl_build_put_left:Nn #1
  { \@@_build_put_left:NNn \cs_set_nopar:Npe #1 }
\cs_generate_variant:Nn \tl_build_put_left:Nn { Ne , Nx }
\cs_new_protected:Npn \tl_build_gput_left:Nn #1
  { \@@_build_put_left:NNn \cs_gset_nopar:Npe #1 }
\cs_generate_variant:Nn \tl_build_gput_left:Nn { Ne , Nx }
\cs_new_protected:Npn \@@_build_put_left:NNn #1#2#3
  {
    #1 #2
      {
        \__kernel_exp_not:w \exp_after:wN
          {
            \exp:w \exp_after:wN \@@_build_put:nn
              \exp_after:wN {#2} {#3}
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\tl_build_end:N, \tl_build_gend:N}
% \begin{macro}{\@@_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:Ne} or \cs{tl_gset:Ne}.
%    \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}
% \end{macro}
%
% \begin{macro}{\tl_build_get_intermediate:NN}
%    \begin{macrocode}
\cs_new_protected:Npn \tl_build_get_intermediate:NN
  { \@@_build_get:NNN \__kernel_tl_set:Nx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_build_get:NNN}
% \begin{macro}{\@@_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 |e|-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 \@@_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
  { \__kernel_exp_not:w \exp_after:wN { \if_false: } \fi: }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</code>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
