% \iffalse meta-comment
%
%% File: l3flag.dtx
%
% Copyright (C) 2011-2025 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{l3flag} module\\ Expandable flags^^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 2025-11-06}
%
% \maketitle
%
% \begin{documentation}
%
% Flags are the only data-type that can be modified in expansion-only
% contexts. This module is meant mostly for kernel use: in almost all
% cases, booleans or integers should be preferred to flags because they
% are very significantly faster.
%
% A flag can hold any (small) non-negative value, which we call its
% \meta{height}. In expansion-only contexts, a flag can only be
% \enquote{raised}: this increases the \meta{height} by $1$. The \meta{height}
% can also be queried expandably. However, decreasing it, or setting it
% to zero requires non-expandable assignments.
%
% Flag variables are always local.
%
% A typical use case of flags would be to keep track of whether an
% exceptional condition has occurred during expandable processing, and
% produce a meaningful (non-expandable) message after the end of the
% expandable processing.  This is exemplified by \pkg{l3str-convert},
% which for performance reasons performs conversions of individual
% characters expandably and for readability reasons produces a single
% error message describing incorrect inputs that were encountered.
%
% Flags should not be used without carefully considering the fact that
% raising a flag takes a time and memory proportional to its height and
% that the memory cannot be reclaimed even if the flag is cleared.
% Flags should not be used unless it is unavoidable.
%
% In earlier versions, flags were referenced by an \texttt{n}-type
% \meta{flag name} such as \texttt{fp_overflow}, used as part of
% \cs{use:c} constructions.  All of the commands described below have
% \texttt{n}-type analogues that can still appear in old code, but the
% \texttt{N}-type commands are to be preferred moving forward.  The
% \texttt{n}-type \meta{flag name} is simply mapped to
% \cs[no-index]{l_\meta{flag name}_flag}, which makes it easier for
% packages using public flags (such as \pkg{l3fp}) to retain backwards
% compatibility.
%
% \section{Setting up flags}
%
% \begin{function}[added = 2024-01-12]{\flag_new:N, \flag_new:c}
%   \begin{syntax}
%     \cs{flag_new:N} \meta{flag~var}
%   \end{syntax}
%   Creates a new \meta{flag~var}, or raises an error if the name is
%   already taken. The declaration is global, but flags are always local
%   variables. The \meta{flag~var} initially has zero height.
% \end{function}
%
% \begin{function}[added = 2024-01-12]{\flag_clear:N, \flag_clear:c}
%   \begin{syntax}
%     \cs{flag_clear:N} \meta{flag~var}
%   \end{syntax}
%   Sets the height of the \meta{flag~var} to zero. The assignment is local.
% \end{function}
%
% \begin{function}[added = 2024-01-12]{\flag_clear_new:N, \flag_clear_new:c}
%   \begin{syntax}
%     \cs{flag_clear_new:N} \meta{flag~var}
%   \end{syntax}
%   Ensures that the \meta{flag~var} exists globally by applying
%   \cs{flag_new:N} if necessary, then applies \cs{flag_clear:N}, setting
%   the height to zero locally.
% \end{function}
%
% \begin{function}[added = 2024-01-12]{\flag_show:N, \flag_show:c}
%   \begin{syntax}
%     \cs{flag_show:N} \meta{flag~var}
%   \end{syntax}
%   Displays the height of the \meta{flag~var} in the terminal.
% \end{function}
%
% \begin{function}[added = 2024-01-12]{\flag_log:N, \flag_log:c}
%   \begin{syntax}
%     \cs{flag_log:N} \meta{flag~var}
%   \end{syntax}
%   Writes the height of the \meta{flag~var} in the log file.
% \end{function}
%
% \section{Expandable flag commands}
%
% \begin{function}[EXP, pTF, added = 2024-01-12]{\flag_if_exist:N, \flag_if_exist:c}
%   \begin{syntax}
%     \cs{flag_if_exist_p:N} \meta{flag~var}
%     \cs{flag_if_exist:NTF} \meta{flag~var} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   This function returns \texttt{true} if the \meta{flag~var} is
%   currently defined, and \texttt{false} otherwise. This does not check
%   that the \meta{flag~var} really is a flag variable.
% \end{function}
%
% \begin{function}[EXP, pTF, added = 2024-01-12]{\flag_if_raised:N, \flag_if_raised:c}
%   \begin{syntax}
%     \cs{flag_if_raised_p:N} \meta{flag~var}
%     \cs{flag_if_raised:NTF} \meta{flag~var} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   This function returns \texttt{true} if the \meta{flag~var} has non-zero
%   height, and \texttt{false} if the \meta{flag~var} has zero height.
% \end{function}
%
% \begin{function}[EXP, added = 2024-01-12]{\flag_height:N, \flag_height:c}
%   \begin{syntax}
%     \cs{flag_height:N} \meta{flag~var}
%   \end{syntax}
%   Expands to the height of the \meta{flag~var} as an integer denotation.
% \end{function}
%
% \begin{function}[EXP, added = 2024-01-12]{\flag_raise:N, \flag_raise:c}
%   \begin{syntax}
%     \cs{flag_raise:N} \meta{flag~var}
%   \end{syntax}
%   The height of \meta{flag~var} is increased by $1$ locally.
% \end{function}
%
% \begin{function}[EXP, added = 2024-01-12]{\flag_ensure_raised:N, \flag_ensure_raised:c}
%   \begin{syntax}
%     \cs{flag_ensure_raised:N} \meta{flag~var}
%   \end{syntax}
%   Ensures the \meta{flag~var} is raised by making its height at least~$1$,
%   locally.
% \end{function}
%
% \begin{variable}[added = 2024-01-12]{\l_tmpa_flag, \l_tmpb_flag}
%   Scratch flag for local assignment. These are never used by
%   the kernel code, and so are safe for use with any \LaTeX3-defined
%   function. However, they may be overwritten by other non-kernel
%   code and so should only be used for short-term storage.
% \end{variable}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3flag} implementation}
%
%    \begin{macrocode}
%<*code>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=flag>
%    \end{macrocode}
%
% \TestFiles{m3flag001}
%
% \begin{macro}{\@@_sep:}
%    \begin{macrocode}
\cs_new_eq:NN \@@_sep: \__kernel_int_sep:
%    \end{macrocode}
% \end{macro}
%
% \subsection{Protected flag commands}
%
% The height $h$ of a flag (which is initially zero) is stored by
% setting control sequences of the form \cs[no-index]{\meta{flag
% name}\meta{integer}} to \tn{relax} for $0\leq\meta{integer}<h$.  These
% control sequences are produced by \cs{cs:w} \meta{flag~var}
% \meta{integer} \cs{cs_end:}, namely the \meta{flag~var} is actually a
% (protected) macro expanding to its own csname.
%
% \begin{macro}{\flag_new:N, \flag_new:c}
%   Evaluate the csname of~|#1| for use in constructing the various
%   indexed macros.
%   \begin{macrocode}
\cs_new_protected:Npn \flag_new:N #1
  { \cs_new_protected:Npe #1 { \cs_to_str:N #1 } }
\cs_generate_variant:Nn \flag_new:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_tmpa_flag, \l_tmpb_flag}
%   Two flag variables for scratch use.
%    \begin{macrocode}
\flag_new:N \l_tmpa_flag
\flag_new:N \l_tmpb_flag
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\flag_clear:N, \flag_clear:c}
% \begin{macro}{\@@_clear:wN}
%   Undefine control sequences, starting from the |0| flag, upwards,
%   until reaching an undefined control sequence.  We don't use
%   \cs{cs_undefine:c} because that would act globally.
%    \begin{macrocode}
\cs_new_protected:Npn \flag_clear:N #1
  {
    \@@_clear:wN 0 \@@_sep: #1
    \prg_break_point:
  }
\cs_generate_variant:Nn \flag_clear:N { c }
\cs_new_protected:Npn \@@_clear:wN #1 \@@_sep: #2
  {
    \if_cs_exist:w #2 #1 \cs_end: \else:
      \prg_break:n
    \fi:
    \cs_set_eq:cN { #2 #1 } \tex_undefined:D
    \exp_after:wN \@@_clear:wN
    \int_value:w \int_eval:w \c_one_int + #1 \@@_sep: #2
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\flag_clear_new:N, \flag_clear_new:c}
%   As for other datatypes, clear the \meta{flag~var} or create a new one,
%   as appropriate.
%    \begin{macrocode}
\cs_new_protected:Npn \flag_clear_new:N #1
  { \flag_if_exist:NTF #1 { \flag_clear:N } { \flag_new:N } #1 }
\cs_generate_variant:Nn \flag_clear_new:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\flag_show:N, \flag_show:c, \flag_log:N, \flag_log:c, \@@_show:NN}
%   Show the height (terminal or log file) using appropriate \pkg{l3msg}
%   auxiliaries.
%    \begin{macrocode}
\cs_new_protected:Npn \flag_show:N { \@@_show:NN \tl_show:n }
\cs_generate_variant:Nn \flag_show:N { c }
\cs_new_protected:Npn \flag_log:N { \@@_show:NN \tl_log:n }
\cs_generate_variant:Nn \flag_log:N { c }
\cs_new_protected:Npn \@@_show:NN #1#2
  {
    \__kernel_chk_defined:NT #2
      { \exp_args:Ne #1 { \tl_to_str:n { #2 height } = \flag_height:N #2 } }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Expandable flag commands}
%
% \begin{macro}[EXP, pTF]{\flag_if_exist:N, \flag_if_exist:c}
%   Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
%    \begin{macrocode}
\prg_new_eq_conditional:NNn \flag_if_exist:N \cs_if_exist:N
  { TF , T , F , p }
\prg_new_eq_conditional:NNn \flag_if_exist:c \cs_if_exist:c
  { TF , T , F , p }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP, pTF]{\flag_if_raised:N, \flag_if_raised:c}
%   Test if the flag has a non-zero height, by checking the |0| control sequence.
%    \begin{macrocode}
\prg_new_conditional:Npnn \flag_if_raised:N #1 { p , T , F , TF }
  {
    \if_cs_exist:w #1 0 \cs_end:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
\prg_generate_conditional_variant:Nnn \flag_if_raised:N
  { c } { p , T , F , TF }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\flag_height:N, \flag_height:c}
% \begin{macro}[EXP]{\@@_height_loop:wN, \@@_height_end:wN}
%   Extract the value of the flag by going through all of the
%   control sequences starting from |0|.
%    \begin{macrocode}
\cs_new:Npn \flag_height:N #1 { \@@_height_loop:wN 0 \@@_sep: #1 }
\cs_new:Npn \@@_height_loop:wN #1 \@@_sep: #2
  {
    \if_cs_exist:w #2 #1 \cs_end: \else:
      \exp_after:wN \@@_height_end:wN
    \fi:
    \exp_after:wN \@@_height_loop:wN
    \int_value:w \int_eval:w \c_one_int + #1 \@@_sep: #2
  }
\cs_new:Npn \@@_height_end:wN #1 + #2 \@@_sep: #3 {#2}
\cs_generate_variant:Nn \flag_height:N { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\flag_raise:N, \flag_raise:c}
%   Change the appropriate control sequence to \tn{relax} by expanding a
%   \cs{cs:w} \ldots{} \cs{cs_end:} construction, then pass it to
%   \cs{use_none:n} to avoid leaving anything in the input stream.
%    \begin{macrocode}
\cs_new:Npn \flag_raise:N #1
  { \exp_after:wN \use_none:n \cs:w #1 \flag_height:N #1 \cs_end: }
\cs_generate_variant:Nn \flag_raise:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\flag_ensure_raised:N, \flag_ensure_raised:c}
%   Pass the control sequence with name \meta{flag name}\texttt{0} to
%   \cs{use_none:n}.  Constructing the control sequence ensures that it
%   changes from being undefined (if it was so) to being \tn{relax}.
%    \begin{macrocode}
\cs_new:Npn \flag_ensure_raised:N #1
  { \exp_after:wN \use_none:n \cs:w #1 0 \cs_end: }
\cs_generate_variant:Nn \flag_ensure_raised:N { c }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Old \texttt{n}-type flag commands}
%
% Here we keep the old flag commands since our policy is to no longer
% delete deprecated functions.  The idea is to simply map \meta{flag
% name} to \cs[no-index]{l_\meta{flag name}_flag}.  When the debugging
% code is activated, it checks existence of the \texttt{N}-type flag
% variables that result.
%
% \begin{macro}[no-user-doc]{\flag_new:n, \flag_clear:n, \flag_clear_new:n}
% \begin{macro}[EXP, pTF, no-user-doc]{\flag_if_exist:n, \flag_if_raised:n}
% \begin{macro}[EXP, no-user-doc]{\flag_height:n, \flag_raise:n, \flag_ensure_raised:n}
%   \begin{macrocode}
\cs_new_protected:Npn \flag_new:n #1 { \flag_new:c { l_#1_flag } }
\cs_new_protected:Npn \flag_clear:n #1 { \flag_clear:c { l_#1_flag } }
\cs_new_protected:Npn \flag_clear_new:n #1 { \flag_clear_new:c { l_#1_flag } }
\cs_new:Npn \flag_if_exist_p:n #1 { \flag_if_exist_p:c { l_#1_flag } }
\cs_new:Npn \flag_if_exist:nT #1 { \flag_if_exist:cT { l_#1_flag } }
\cs_new:Npn \flag_if_exist:nF #1 { \flag_if_exist:cF { l_#1_flag } }
\cs_new:Npn \flag_if_exist:nTF #1 { \flag_if_exist:cTF { l_#1_flag } }
\cs_new:Npn \flag_if_raised_p:n #1 { \flag_if_raised_p:c { l_#1_flag } }
\cs_new:Npn \flag_if_raised:nT #1 { \flag_if_raised:cT { l_#1_flag } }
\cs_new:Npn \flag_if_raised:nF #1 { \flag_if_raised:cF { l_#1_flag } }
\cs_new:Npn \flag_if_raised:nTF #1 { \flag_if_raised:cTF { l_#1_flag } }
\cs_new:Npn \flag_height:n #1 { \flag_height:c { l_#1_flag } }
\cs_new:Npn \flag_raise:n #1 { \flag_raise:c { l_#1_flag } }
\cs_new:Npn \flag_ensure_raised:n #1 { \flag_ensure_raised:c { l_#1_flag } }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\flag_show:n, \flag_log:n, \@@_show:Nn}
%   To avoid changing the output here we mostly keep the old code.
%    \begin{macrocode}
\cs_new_protected:Npn \flag_show:n { \@@_show:Nn \tl_show:n }
\cs_new_protected:Npn \flag_log:n { \@@_show:Nn \tl_log:n }
\cs_new_protected:Npn \@@_show:Nn #1#2
  {
    \exp_args:Nc \__kernel_chk_defined:NT { l_#2_flag }
      {
        \exp_args:Ne #1
          { \tl_to_str:n { flag~#2~height } = \flag_height:n {#2} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</code>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
