% \iffalse meta-comment
%
%% File: l3prop.dtx
%
% Copyright (C) 1990-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}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3prop} package\\ Property lists^^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-08-11}
%
% \maketitle
%
% \begin{documentation}
%
% \pkg{expl3} implements a \meta{property list} data type, which contain
% an unordered list of entries each of which consists of a \meta{key} and
% an associated \meta{value}. The \meta{key} and \meta{value} may both
% be any \meta{balanced text}, the \meta{key} is processed using
% \cs{tl_to_str:n}, meaning that category codes are ignored. It is possible to
% map functions to property lists such that the function is applied to every
% key--value pair within the list.
%
% Each entry in a property list must have a unique \meta{key}: if an entry is
% added to a property list which already contains the \meta{key} then the new
% entry overwrites the existing one. The \meta{keys} are compared on a
% string basis, using the same method as \cs{str_if_eq:nn}.
%
% Property lists are intended for storing key-based information for use within
% code.  This is in contrast to key--value lists, which are a form of
% \emph{input} parsed by the \pkg{l3keys} module.
%
% \section{Creating and initialising property lists}
%
% \begin{function}{\prop_new:N, \prop_new:c}
%   \begin{syntax}
%     \cs{prop_new:N} \meta{property list}
%   \end{syntax}
%   Creates a new \meta{property list} or raises an error if the name is
%   already taken. The declaration is global. The \meta{property list}
%   initially contains no entries.
% \end{function}
%
% \begin{function}
%   {\prop_clear:N, \prop_clear:c, \prop_gclear:N, \prop_gclear:c}
%   \begin{syntax}
%     \cs{prop_clear:N} \meta{property list}
%   \end{syntax}
%   Clears all entries from the \meta{property list}.
% \end{function}
%
% \begin{function}
%   {
%     \prop_clear_new:N,  \prop_clear_new:c,
%     \prop_gclear_new:N, \prop_gclear_new:c
%   }
%   \begin{syntax}
%     \cs{prop_clear_new:N} \meta{property list}
%   \end{syntax}
%   Ensures that the \meta{property list} exists globally by applying
%   \cs{prop_new:N} if necessary, then applies
%   \cs[index=prop_clear:N]{prop_(g)clear:N} to leave
%   the list empty.
% \end{function}
%
% \begin{function}
%   {
%     \prop_set_eq:NN,  \prop_set_eq:cN,  \prop_set_eq:Nc,  \prop_set_eq:cc,
%     \prop_gset_eq:NN, \prop_gset_eq:cN, \prop_gset_eq:Nc, \prop_gset_eq:cc
%   }
%   \begin{syntax}
%     \cs{prop_set_eq:NN} \meta{property list_1} \meta{property list_2}
%   \end{syntax}
%   Sets the content of \meta{property list_1} equal to that of
%   \meta{property list_2}.
% \end{function}
%
% \begin{function}[added = 2017-11-28, updated = 2021-11-07]
%   {
%     \prop_set_from_keyval:Nn, \prop_set_from_keyval:cn,
%     \prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn,
%   }
%   \begin{syntax}
%     \cs{prop_set_from_keyval:Nn} \meta{property list}
%       \{
%         \meta{key1} |=| \meta{value1} |,|
%         \meta{key2} |=| \meta{value2} |,| \ldots{}
%       \}
%   \end{syntax}
%   Sets \meta{property list} to contain key--value pairs given in the second
%   argument.  If duplicate keys appear only the last of the values is kept.
%
%   Spaces are trimmed around every \meta{key} and every \meta{value},
%   and if the result of trimming spaces consists of a single brace
%   group then a set of outer braces is removed.  This enables both the
%   \meta{key} and the \meta{value} to contain spaces, commas or equal
%   signs.  The \meta{key} is then processed by \cs{tl_to_str:n}.
%   This function correctly detects the |=| and |,| signs provided they
%   have the standard category code~$12$ or they are active.
%
%   Notice that in contrast to most keyval lists (\emph{e.g.}~those in
%   \pkg{l3keys}), each key here \emph{must} be followed with an \texttt{=}
%   sign.
% \end{function}
%
% \begin{function}[added = 2017-11-28, updated = 2021-11-07]
%   {\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
%   \begin{syntax}
%     \cs{prop_const_from_keyval:Nn} \meta{property list}
%       \{
%         \meta{key1} |=| \meta{value1} |,|
%         \meta{key2} |=| \meta{value2} |,| \ldots{}
%       \}
%   \end{syntax}
%   Creates a new constant \meta{property list} or raises an error if the
%   name is already taken. The \meta{property list} is set globally to
%   contain key--value pairs given in the second argument, processed in
%   the way described for \cs{prop_set_from_keyval:Nn}.  If duplicate
%   keys appear only the last of the values is kept.
%   This function correctly detects the |=| and |,| signs provided they
%   have the standard category code~$12$ or they are active.
%
%   Notice that in contrast to most keyval lists (\emph{e.g.}~those in
%   \pkg{l3keys}), each key here \emph{must} be followed with an \texttt{=}
%   sign.
% \end{function}
%
% \section{Adding and updating property list entries}
%
% \begin{function}[updated = 2012-07-09]
%   {
%     \prop_put:Nnn,  \prop_put:NnV,  \prop_put:Nno,  \prop_put:Nne, \prop_put:Nnx,
%     \prop_put:NVn,  \prop_put:NVV,  \prop_put:NVx,  \prop_put:Nvx,
%     \prop_put:Non,  \prop_put:Noo,  \prop_put:Nxx,
%     \prop_put:cnn,  \prop_put:cnV,  \prop_put:cno,  \prop_put:cne, \prop_put:cnx,
%     \prop_put:cVn,  \prop_put:cVV,  \prop_put:cVx,  \prop_put:cvx,
%     \prop_put:con,  \prop_put:coo,  \prop_put:cxx,
%     \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nno, \prop_gput:Nne, \prop_gput:Nnx,
%     \prop_gput:NVn, \prop_gput:NVV, \prop_gput:NVx, \prop_gput:Nvx,
%     \prop_gput:Non, \prop_gput:Noo, \prop_gput:Nxx,
%     \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cno, \prop_gput:cne, \prop_gput:cnx,
%     \prop_gput:cVn, \prop_gput:cVV, \prop_gput:cVx, \prop_gput:cvx,
%     \prop_gput:con, \prop_gput:coo, \prop_gput:cxx
%   }
%   \begin{syntax}
%     \cs{prop_put:Nnn} \meta{property list} \Arg{key} \Arg{value}
%   \end{syntax}
%   Adds an entry to the \meta{property list} which may be accessed
%   using the \meta{key} and which has \meta{value}. If the \meta{key}
%   is already present in the \meta{property list}, the existing entry
%   is overwritten by the new \meta{value}. Both the \meta{key} and
%   \meta{value} may contain any \meta{balanced text}. The \meta{key} is
%   stored after processing with \cs{tl_to_str:n}, meaning that category
%   codes are ignored.
% \end{function}
%
% \begin{function}
%   {
%     \prop_put_if_new:Nnn,  \prop_put_if_new:cnn,
%     \prop_gput_if_new:Nnn, \prop_gput_if_new:cnn
%   }
%   \begin{syntax}
%     \cs{prop_put_if_new:Nnn} \meta{property list} \Arg{key} \Arg{value}
%   \end{syntax}
%   If the \meta{key} is present in the \meta{property list} then no
%   action is taken. Otherwise, a new entry is added as described for
%   \cs{prop_put:Nnn}.
% \end{function}
%
% \begin{function}[added = 2021-05-16]
%   {
%     \prop_concat:NNN,  \prop_concat:ccc,
%     \prop_gconcat:NNN, \prop_gconcat:ccc
%   }
%   \begin{syntax}
%     \cs{prop_concat:NNN} \meta{property list_1} \meta{property list_2} \meta{property list3}
%   \end{syntax}
%   Combines the key--value pairs of \meta{property list_2} and
%   \meta{property list_3}, and saves the result in \meta{property list_1}.  If a
%   key appears in both \meta{property list_2} and \meta{property list_3} then the
%   last value, namely the value in \meta{property list_3} is kept.
% \end{function}
%
% \begin{function}[added = 2021-05-16, updated = 2021-11-07]
%   {
%     \prop_put_from_keyval:Nn, \prop_put_from_keyval:cn,
%     \prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn,
%   }
%   \begin{syntax}
%     \cs{prop_put_from_keyval:Nn} \meta{property list}
%       \{
%         \meta{key1} |=| \meta{value1} |,|
%         \meta{key2} |=| \meta{value2} |,| \ldots{}
%       \}
%   \end{syntax}
%   Updates the \meta{property list} by adding entries for each key--value
%   pair given in the second argument.  The addition is done through
%   \cs{prop_put:Nnn}, hence if the \meta{property list} already contains
%   some of the keys, the corresponding values are discarded and
%   replaced by those given in the key--value list.  If duplicate keys
%   appear in the key--value list then only the last of the values is kept.
%
%   The function is equivalent to storing the key--value pairs in a
%   temporary property list using \cs{prop_set_from_keyval:Nn}, then
%   combining \meta{property list} with the temporary variable using
%   \cs{prop_concat:NNN}.  In particular, the \meta{keys} and
%   \meta{values} are space-trimmed and unbraced as described in
%   \cs{prop_set_from_keyval:Nn}.
%   This function correctly detects the |=| and |,| signs provided they
%   have the standard category code~$12$ or they are active.
% \end{function}
%
% \section{Recovering values from property lists}
%
% \begin{function}[updated = 2011-08-28]
%   {
%     \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NoN,
%     \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:coN,
%   }
%   \begin{syntax}
%     \cs{prop_get:NnN} \meta{property list} \Arg{key} \meta{tl var}
%   \end{syntax}
%   Recovers the \meta{value} stored with \meta{key} from the
%   \meta{property list}, and places this in the \meta{token list
%   variable}. If the \meta{key} is not found in the
%   \meta{property list} then the \meta{token list variable} is set
%   to the special marker \cs{q_no_value}. The \meta{token list
%     variable} is set within the current \TeX{} group. See also
%   \cs{prop_get:NnNTF}.
% \end{function}
%
% \begin{function}[updated = 2011-08-18]
%   {\prop_pop:NnN, \prop_pop:NoN, \prop_pop:cnN, \prop_pop:coN}
%   \begin{syntax}
%     \cs{prop_pop:NnN} \meta{property list} \Arg{key} \meta{tl var}
%   \end{syntax}
%   Recovers the \meta{value} stored with \meta{key} from the
%   \meta{property list}, and places this in the \meta{token list
%   variable}. If the \meta{key} is not found in the
%   \meta{property list} then the \meta{token list variable} is set
%   to the special marker \cs{q_no_value}. The \meta{key} and
%   \meta{value} are then deleted from the property list. Both
%   assignments are local.  See also \cs{prop_pop:NnNTF}.
% \end{function}
%
% \begin{function}[updated = 2011-08-18]
%   {\prop_gpop:NnN, \prop_gpop:NoN, \prop_gpop:cnN, \prop_gpop:coN}
%   \begin{syntax}
%     \cs{prop_gpop:NnN} \meta{property list} \Arg{key} \meta{tl var}
%   \end{syntax}
%   Recovers the \meta{value} stored with \meta{key} from the
%   \meta{property list}, and places this in the \meta{token list
%   variable}. If the \meta{key} is not found in the
%   \meta{property list} then the \meta{token list variable} is set
%   to the special marker \cs{q_no_value}. The \meta{key} and
%   \meta{value} are then deleted from the property list.
%   The \meta{property list} is modified globally, while the assignment of
%   the \meta{token list variable} is local.  See also \cs{prop_gpop:NnNTF}.
% \end{function}
%
% \begin{function}[added = 2014-07-17, EXP]{\prop_item:Nn, \prop_item:cn}
%   \begin{syntax}
%     \cs{prop_item:Nn} \meta{property list} \Arg{key}
%   \end{syntax}
%   Expands to the \meta{value} corresponding to the \meta{key} in
%   the \meta{property list}. If the \meta{key} is missing, this has
%   an empty expansion.
%   \begin{texnote}
%     This function is slower than the non-expandable analogue
%     \cs{prop_get:NnN}.
%     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}
%
% \begin{function}[EXP]{\prop_count:N, \prop_count:c}
%   \begin{syntax}
%     \cs{prop_count:N} \meta{property list}
%   \end{syntax}
%   Leaves the number of key--value pairs in the \meta{property list} in
%   the input stream as an \meta{integer denotation}.
% \end{function}
%
% \begin{function}[EXP]{\prop_to_keyval:N}
%   \begin{syntax}
%     \cs{prop_to_keyval:N} \meta{property list}
%   \end{syntax}
%   Expands to the \meta{property list} in a key--value notation. Keep in mind
%   that a \meta{property list} is \emph{unordered}, while key--value interfaces
%   don't necessarily are, so this can't be used for arbitrary interfaces.
%   \begin{texnote}
%     The result is returned within the \tn{unexpanded} primitive
%     (\cs{exp_not:n}), which means that the key--value list does not expand
%     further when appearing in an \texttt{x}-type or \texttt{e}-type argument expansion.
%     It also needs exactly two steps of expansion.
%   \end{texnote}
% \end{function}
%
% \section{Modifying property lists}
%
% \begin{function}[added = 2012-05-12]
%   {
%     \prop_remove:Nn,  \prop_remove:NV,  \prop_remove:cn,  \prop_remove:cV,
%     \prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:cn, \prop_gremove:cV
%   }
%   \begin{syntax}
%     \cs{prop_remove:Nn} \meta{property list} \Arg{key}
%   \end{syntax}
%   Removes the entry listed under \meta{key} from the
%   \meta{property list}.  If the \meta{key} is
%   not found in the \meta{property list} no change occurs,
%   \emph{i.e}~there is no need to test for the existence of a key before
%   deleting it.
% \end{function}
%
% \section{Property list conditionals}
%
% \begin{function}[EXP, pTF, added = 2012-03-03]
%   {\prop_if_exist:N, \prop_if_exist:c}
%   \begin{syntax}
%     \cs{prop_if_exist_p:N} \meta{property list}
%     \cs{prop_if_exist:NTF} \meta{property list} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests whether the \meta{property list} is currently defined.  This does not
%   check that the \meta{property list} really is a property list variable.
% \end{function}
%
% \begin{function}[EXP, pTF]{\prop_if_empty:N, \prop_if_empty:c}
%   \begin{syntax}
%     \cs{prop_if_empty_p:N} \meta{property list}
%     \cs{prop_if_empty:NTF} \meta{property list} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests if the \meta{property list} is empty (containing no entries).
% \end{function}
%
% \begin{function}[updated = 2011-09-15, EXP, pTF]
%   {
%     \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:No,
%     \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:co
%   }
%   \begin{syntax}
%     \cs{prop_if_in_p:Nn} \meta{property list} \Arg{key}
%     \cs{prop_if_in:NnTF} \meta{property list} \Arg{key} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests if the \meta{key} is present in the \meta{property list},
%   making the comparison using the method described by \cs{str_if_eq:nnTF}.
%   \begin{texnote}
%     This function iterates through every key--value pair in the
%     \meta{property list} and is therefore slower than using the
%     non-expandable \cs{prop_get:NnNTF}.
%   \end{texnote}
% \end{function}
%
% \section{Recovering values from property lists with branching}
%
% The functions in this section combine tests for the presence of a key
% in a property list with  recovery of the associated valued. This makes them
% useful for cases where different cases follow dependent on the presence
% or absence of a key in a property list. They offer increased readability
% and performance over separate testing and recovery phases.
%
% \begin{function}[updated = 2012-05-19, TF]
%   {
%     \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NoN,
%     \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:coN
%   }
%   \begin{syntax}
%     \cs{prop_get:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \\
%     ~~\Arg{true code} \Arg{false code}
%   \end{syntax}
%   If the \meta{key} is not present in the \meta{property list}, leaves
%   the \meta{false code} in the input stream.  The value of the
%   \meta{token list variable} is not defined in this case and should
%   not be relied upon.  If the \meta{key} is present in the
%   \meta{property list}, stores the corresponding \meta{value} in the
%   \meta{token list variable} without removing it from the
%   \meta{property list}, then leaves the \meta{true code} in the input
%   stream.  The \meta{token list variable} is assigned locally.
% \end{function}
%
% \begin{function}[TF, added = 2011-08-18, updated = 2012-05-19]
%   {\prop_pop:NnN, \prop_pop:cnN}
%   \begin{syntax}
%     \cs{prop_pop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   If the \meta{key} is not present in the \meta{property list}, leaves
%   the \meta{false code} in the input stream.  The value of the
%   \meta{token list variable} is not defined in this case and should
%   not be relied upon.  If the \meta{key} is present in
%   the \meta{property list}, pops the corresponding \meta{value}
%   in the \meta{token list variable}, \emph{i.e.}~removes the item from
%   the \meta{property list}.
%   Both the \meta{property list} and the \meta{token list variable}
%   are assigned locally.
% \end{function}
%
% \begin{function}[TF, added = 2011-08-18, updated = 2012-05-19]
%   {\prop_gpop:NnN, \prop_gpop:cnN}
%   \begin{syntax}
%     \cs{prop_gpop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   If the \meta{key} is not present in the \meta{property list}, leaves
%   the \meta{false code} in the input stream.  The value of the
%   \meta{token list variable} is not defined in this case and should
%   not be relied upon.  If the \meta{key} is present in
%   the \meta{property list}, pops the corresponding \meta{value}
%   in the \meta{token list variable}, \emph{i.e.}~removes the item from
%   the \meta{property list}.
%   The \meta{property list} is modified globally, while the
%   \meta{token list variable} is assigned locally.
% \end{function}
%
% \section{Mapping over property lists}
%
% All mappings are done at the current group level, \emph{i.e.}~any
% local assignments made by the \meta{function} or \meta{code} discussed
% below remain in effect after the loop.
%
% \begin{function}[updated = 2013-01-08, rEXP]
%   {\prop_map_function:NN, \prop_map_function:cN}
%   \begin{syntax}
%     \cs{prop_map_function:NN} \meta{property list} \meta{function}
%   \end{syntax}
%   Applies \meta{function} to every \meta{entry} stored in the
%   \meta{property list}. The \meta{function} receives two arguments for
%   each iteration: the \meta{key} and associated \meta{value}.
%   The order in which \meta{entries} are returned is not defined and
%   should not be relied upon.
%   To pass further arguments to the \meta{function}, see
%   \cs{prop_map_tokens:Nn}.
% \end{function}
%
% \begin{function}[updated = 2013-01-08]
%   {\prop_map_inline:Nn, \prop_map_inline:cn}
%   \begin{syntax}
%     \cs{prop_map_inline:Nn} \meta{property list} \Arg{inline function}
%   \end{syntax}
%   Applies \meta{inline function} to every \meta{entry} stored
%   within the \meta{property list}. The \meta{inline function} should
%   consist of code which receives the \meta{key} as |#1| and the
%   \meta{value} as |#2|.
%   The order in which \meta{entries} are returned is not defined and
%   should not be relied upon.
% \end{function}
%
% \begin{function}[rEXP]
%   {\prop_map_tokens:Nn, \prop_map_tokens:cn}
%   \begin{syntax}
%     \cs{prop_map_tokens:Nn} \meta{property list} \Arg{code}
%   \end{syntax}
%   Analogue of \cs{prop_map_function:NN} which maps several tokens
%   instead of a single function.  The \meta{code} receives each
%   key--value pair in the \meta{property list} as two trailing brace
%   groups. For instance,
%   \begin{verbatim}
%     \prop_map_tokens:Nn \l_my_prop { \str_if_eq:nnT { mykey } }
%   \end{verbatim}
%   expands to the value corresponding to \texttt{mykey}: for each
%   pair in |\l_my_prop| the function \cs{str_if_eq:nnT} receives
%   \texttt{mykey}, the \meta{key} and the \meta{value} as its three
%   arguments.  For that specific task, \cs{prop_item:Nn} is faster.
% \end{function}
%
% \begin{function}[updated = 2012-06-29, rEXP]{\prop_map_break:}
%   \begin{syntax}
%     \cs{prop_map_break:}
%   \end{syntax}
%   Used to terminate a \cs[no-index]{prop_map_\ldots} function before all
%   entries in the \meta{property list} have been processed. This
%   normally takes place within a conditional statement, for example
%   \begin{verbatim}
%     \prop_map_inline:Nn \l_my_prop
%       {
%         \str_if_eq:nnTF { #1 } { bingo }
%           { \prop_map_break: }
%           {
%             % Do something useful
%           }
%       }
%   \end{verbatim}
%   Use outside of a \cs[no-index]{prop_map_\ldots} scenario leads to low
%   level \TeX{} errors.
%   \begin{texnote}
%     When the mapping is broken, additional tokens may be inserted
%     before further items are taken
%     from the input stream. This depends on the design of the mapping
%     function.
%   \end{texnote}
% \end{function}
%
% \begin{function}[updated = 2012-06-29, rEXP]{\prop_map_break:n}
%   \begin{syntax}
%     \cs{prop_map_break:n} \Arg{code}
%   \end{syntax}
%   Used to terminate a \cs[no-index]{prop_map_\ldots} function before all
%   entries in the \meta{property list} have been processed, inserting
%   the \meta{code} after the mapping has ended. This
%   normally takes place within a conditional statement, for example
%   \begin{verbatim}
%     \prop_map_inline:Nn \l_my_prop
%       {
%         \str_if_eq:nnTF { #1 } { bingo }
%           { \prop_map_break:n { <code> } }
%           {
%             % Do something useful
%           }
%       }
%   \end{verbatim}
%   Use outside of a \cs[no-index]{prop_map_\ldots} scenario leads to low
%   level \TeX{} errors.
%   \begin{texnote}
%     When the mapping is broken, additional tokens may be inserted
%     before the \meta{code} is
%     inserted into the input stream.
%     This depends on the design of the mapping function.
%   \end{texnote}
% \end{function}
%
% \section{Viewing property lists}
%
% \begin{function}[updated = 2021-04-29]{\prop_show:N, \prop_show:c}
%   \begin{syntax}
%     \cs{prop_show:N} \meta{property list}
%   \end{syntax}
%   Displays the entries in the \meta{property list} in the terminal.
% \end{function}
%
% \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\prop_log:N, \prop_log:c}
%   \begin{syntax}
%     \cs{prop_log:N} \meta{property list}
%   \end{syntax}
%   Writes the entries in the \meta{property list} in the log file.
% \end{function}
%
% \section{Scratch property lists}
%
% \begin{variable}[added = 2012-06-23]{\l_tmpa_prop, \l_tmpb_prop}
%   Scratch property lists 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}
%
% \begin{variable}[added = 2012-06-23]{\g_tmpa_prop, \g_tmpb_prop}
%   Scratch property lists for global 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}
%
% \section{Constants}
%
% \begin{variable}{\c_empty_prop}
%   A permanently-empty property list used for internal comparisons.
% \end{variable}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3prop} implementation}
%
% \TestFiles{m3prop001, m3prop002, m3prop003, m3prop004, m3show001}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=prop>
%    \end{macrocode}
%
% A property list is a macro whose top-level expansion is of the form
% \begin{quote}
%   \cs{s_@@}
%   \cs{@@_pair:wn} \meta{key_1} \cs{s_@@} \Arg{value_1} \\
%   \ldots{} \\
%   \cs{@@_pair:wn} \meta{key_n} \cs{s_@@} \Arg{value_n} \\
% \end{quote}
% where \cs{s_@@} is a scan mark
% (equal to \cs{scan_stop:}), and \cs{@@_pair:wn} can be used to map
% through the property list.
%
% \begin{variable}{\s_@@}
%   The internal token used at the beginning of property lists.  This is
%   also used after each \meta{key} (see \cs{@@_pair:wn}).
% \end{variable}
%
% \begin{variable}{\@@_pair:wn}
%   \begin{syntax}
%     \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{item}
%   \end{syntax}
%   The internal token used to begin each key--value pair in the
%   property list.  If expanded outside of a mapping or manipulation
%   function, an error is raised.  The definition should always be
%   set globally.
% \end{variable}
%
% \begin{variable}{\l_@@_internal_tl}
%   Token list used to store new key--value pairs to be inserted by
%   functions of the \cs{prop_put:Nnn} family.
% \end{variable}
%
% \begin{function}[updated = 2013-01-08]{\@@_split:NnTF}
%   \begin{syntax}
%     \cs{@@_split:NnTF} \meta{property list} \Arg{key} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Splits the \meta{property list} at the \meta{key}, giving three
%   token lists: the \meta{extract} of \meta{property list} before the
%   \meta{key}, the \meta{value} associated with the \meta{key} and the
%   \meta{extract} of the \meta{property list} after the \meta{value}.
%   Both \meta{extracts} retain the internal structure of a property
%   list, and the concatenation of the two \meta{extracts} is a
%   property list.
%   If the \meta{key} is present in the \meta{property list} then the
%   \meta{true code} is left in the input stream, with |#1|, |#2|, and
%   |#3| replaced by the first \meta{extract}, the \meta{value}, and the
%   second extract.
%   If the \meta{key} is not present in the \meta{property list} then
%   the \meta{false code} is left in the input stream, with no trailing
%   material.
%   Both \meta{true code} and \meta{false code} are used in the
%   replacement text of a macro defined internally, hence macro
%   parameter characters should be doubled, except |#1|, |#2|, and |#3|
%   which stand in the \meta{true code} for the three extracts from the
%   property list.
%   The \meta{key} comparison takes place as described for \cs{str_if_eq:nn}.
% \end{function}
%
% \begin{macro}{\s_@@}
%   A private scan mark is used as a marker after each key, and at the
%   very beginning of the property list.
%    \begin{macrocode}
\scan_new:N \s_@@
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_pair:wn}
%   The delimiter is always defined, but when misused simply triggers an
%   error and removes its argument.
%    \begin{macrocode}
\cs_new:Npn \@@_pair:wn #1 \s_@@ #2
  { \msg_expandable_error:nn { prop } { misused } }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_internal_tl}
%   Token list used to store the new key--value pair inserted by
%   \cs{prop_put:Nnn} and friends.
%    \begin{macrocode}
\tl_new:N \l_@@_internal_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}[tested = m3prop004]{\c_empty_prop}
%   An empty prop.
%    \begin{macrocode}
\tl_const:Nn \c_empty_prop { \s_@@ }
%    \end{macrocode}
% \end{variable}
%
% \subsection{Internal auxiliaries}
%
% \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{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
%   Internal recursion quarks.
%    \begin{macrocode}
\quark_new:N \q_@@_recursion_tail
\quark_new:N \q_@@_recursion_stop
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}[EXP]{\@@_if_recursion_tail_stop:n}
% \begin{macro}[EXP]{\@@_if_recursion_tail_stop:o}
%   Functions to query recursion quarks.
%    \begin{macrocode}
\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:n
\cs_generate_variant:Nn \@@_if_recursion_tail_stop:n { o }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Allocation and initialisation}
%
% \begin{macro}[tested = m3prop001]{\prop_new:N, \prop_new:c}
%   Property lists are initialized with the value \cs{c_empty_prop}.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_new:N #1
  {
    \__kernel_chk_if_free_cs:N #1
    \cs_gset_eq:NN #1 \c_empty_prop
  }
\cs_generate_variant:Nn \prop_new:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[tested = m3prop001]{\prop_clear:N, \prop_clear:c}
% \begin{macro}[tested = m3prop001]{\prop_gclear:N, \prop_gclear:c}
%   The same idea for clearing.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_clear:N  #1
  { \prop_set_eq:NN #1 \c_empty_prop }
\cs_generate_variant:Nn \prop_clear:N  { c }
\cs_new_protected:Npn \prop_gclear:N #1
  { \prop_gset_eq:NN #1 \c_empty_prop }
\cs_generate_variant:Nn \prop_gclear:N { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[tested = m3prop001]{\prop_clear_new:N, \prop_clear_new:c}
% \begin{macro}[tested = m3prop001]{\prop_gclear_new:N, \prop_gclear_new:c}
%   Once again a simple variation of the token list functions.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_clear_new:N  #1
  { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new:N #1 } }
\cs_generate_variant:Nn \prop_clear_new:N  { c }
\cs_new_protected:Npn \prop_gclear_new:N #1
  { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new:N #1 } }
\cs_generate_variant:Nn \prop_gclear_new:N { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[tested = m3prop001]
%   {\prop_set_eq:NN, \prop_set_eq:cN, \prop_set_eq:Nc, \prop_set_eq:cc}
% \begin{macro}[tested = m3prop001]
%   {\prop_gset_eq:NN, \prop_gset_eq:cN, \prop_gset_eq:Nc, \prop_gset_eq:cc}
%   These are simply copies from the token list functions.
%    \begin{macrocode}
\cs_new_eq:NN \prop_set_eq:NN  \tl_set_eq:NN
\cs_new_eq:NN \prop_set_eq:Nc  \tl_set_eq:Nc
\cs_new_eq:NN \prop_set_eq:cN  \tl_set_eq:cN
\cs_new_eq:NN \prop_set_eq:cc  \tl_set_eq:cc
\cs_new_eq:NN \prop_gset_eq:NN \tl_gset_eq:NN
\cs_new_eq:NN \prop_gset_eq:Nc \tl_gset_eq:Nc
\cs_new_eq:NN \prop_gset_eq:cN \tl_gset_eq:cN
\cs_new_eq:NN \prop_gset_eq:cc \tl_gset_eq:cc
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{variable}[tested = m3prop004]{\l_tmpa_prop, \l_tmpb_prop}
% \begin{variable}[tested = m3prop004]{\g_tmpa_prop, \g_tmpb_prop}
%   We can now initialize the scratch variables.
%    \begin{macrocode}
\prop_new:N \l_tmpa_prop
\prop_new:N \l_tmpb_prop
\prop_new:N \g_tmpa_prop
\prop_new:N \g_tmpb_prop
%    \end{macrocode}
% \end{variable}
% \end{variable}
%
% \begin{variable}{\l_@@_internal_prop}
%   Property list used by \cs{prop_concat:NNN},
%   \cs{prop_set_from_keyval:Nn} and others.
%    \begin{macrocode}
\prop_new:N \l_@@_internal_prop
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}
%   {
%     \prop_concat:NNN, \prop_concat:ccc,
%     \prop_gconcat:NNN, \prop_gconcat:ccc, \@@_concat:NNNN
%   }
%   Combine two property lists.  We cannot use a simple
%   \cs{tl_concat:NNN} because there may be some duplicate keys between
%   the two property lists.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_concat:NNN
  { \@@_concat:NNNN \prop_set_eq:NN }
\cs_generate_variant:Nn \prop_concat:NNN { ccc }
\cs_new_protected:Npn \prop_gconcat:NNN
  { \@@_concat:NNNN \prop_gset_eq:NN }
\cs_generate_variant:Nn \prop_gconcat:NNN { ccc }
\cs_new_protected:Npn \@@_concat:NNNN #1#2#3#4
  {
    \prop_set_eq:NN \l_@@_internal_prop #3
    \prop_map_inline:Nn #4 { \prop_put:Nnn \l_@@_internal_prop {##1} {##2} }
    #1 #2 \l_@@_internal_prop
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\prop_set_from_keyval:Nn, \prop_set_from_keyval:cn}
% \begin{macro}{\prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn}
% \begin{macro}{\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
% \begin{macro}{\prop_put_from_keyval:Nn, \prop_put_from_keyval:cn}
% \begin{macro}{\prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn}
% \begin{macro}{\@@_missing_eq:n}
%   To avoid tracking throughout the loop the variable name and whether
%   the assignment is local/global, do everything in a scratch variable
%   and empty it afterwards to avoid wasting memory.  Loop through items
%   separated by commas, with \cs{prg_do_nothing:} to avoid losing
%   braces.  After checking for termination, split the item at the first
%   and then at the second |=| (which ought to be the first of the
%   trailing~|=| that we added).  For both splits trim spaces and call a
%   function (first \cs{@@_from_keyval_key:w} then
%   \cs{@@_from_keyval_value:w}), followed by the trimmed material,
%   \cs{s_@@_mark}, the subsequent part of the item, and the trailing |=|'s
%   and \cs{s_@@_stop}.  After finding the \meta{key} just store it after
%   \cs{s_@@_stop}.  After finding the \meta{value} ignore completely empty
%   items (both trailing~|=| were used as delimiters and all parts are
%   empty); if the remaining part~|#2| consists exactly of the second
%   trailing~|=| (namely there was exactly one |=|~in the item) then
%   output one key--value pair for the property list; otherwise complain
%   about a missing or extra~|=|.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_set_from_keyval:Nn #1
  {
    \prop_clear:N #1
    \prop_put_from_keyval:Nn #1
  }
\cs_generate_variant:Nn \prop_set_from_keyval:Nn { c }
\cs_new_protected:Npn \prop_gset_from_keyval:Nn #1
  {
    \prop_gclear:N #1
    \prop_gput_from_keyval:Nn #1
  }
\cs_generate_variant:Nn \prop_gset_from_keyval:Nn { c }
\cs_new_protected:Npn \prop_const_from_keyval:Nn #1#2
  {
    \prop_set_from_keyval:Nn \l_@@_internal_prop {#2}
    \tl_const:Nx #1 { \exp_not:o \l_@@_internal_prop }
    \prop_clear:N \l_@@_internal_prop
  }
\cs_generate_variant:Nn \prop_const_from_keyval:Nn { c }
\cs_new_protected:Npn \prop_put_from_keyval:Nn
  {
    \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool
      { \@@_keyval_parse:NNNn \c_true_bool }
      { \@@_keyval_parse:NNNn \c_false_bool }
      \prop_put:Nnn
  }
\cs_generate_variant:Nn \prop_put_from_keyval:Nn { c }
\cs_new_protected:Npn \prop_gput_from_keyval:Nn
  {
    \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool
      { \@@_keyval_parse:NNNn \c_true_bool }
      { \@@_keyval_parse:NNNn \c_false_bool }
      \prop_gput:Nnn
  }
\cs_generate_variant:Nn \prop_gput_from_keyval:Nn { c }
\cs_new_protected:Npn \@@_missing_eq:n
  { \msg_error:nnn { prop } { prop-keyval } }
\cs_new_protected:Npn \@@_keyval_parse:NNNn #1#2#3#4
  {
    \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool \c_true_bool
    \keyval_parse:nnn \@@_missing_eq:n { #2 #3 } {#4}
    \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool #1
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Accessing data in property lists}
%
% \begin{macro}{\@@_split:NnTF}
% \begin{macro}{\@@_split_aux:NnTF}
% \begin{macro}[EXP]{\@@_split_aux:w}
%   This function is used by most of the module, and hence must be fast.
%   It receives a \meta{property list}, a \meta{key}, a \meta{true code}
%   and a \meta{false code}.  The aim is to split the \meta{property
%     list} at the given \meta{key} into the \meta{extract_1} before the
%   key--value pair, the \meta{value} associated with the \meta{key} and
%   the \meta{extract_2} after the key--value pair.  This is done using
%   a delimited function, whose definition is as follows, where the
%   \meta{key} is turned into a string.
%   \begin{quote}
%     \cs{cs_set:Npn} \cs{@@_split_aux:w} |#1| \\
%     \quad \cs{@@_pair:wn} \meta{key} \cs{s_@@} |#2| \\
%     \quad |#3| \cs{s_@@_mark} |#4| |#5| \cs{s_@@_stop} \\
%     \quad |{| |#4| \Arg{true code} \Arg{false code} |}|
%   \end{quote}
%
%   If the \meta{key} is present in the property list,
%   \cs{@@_split_aux:w}'s |#1| is the part before the \meta{key}, |#2|
%   is the \meta{value}, |#3| is the part after the \meta{key}, |#4| is
%   \cs{use_i:nn}, and |#5| is additional tokens that we do not care
%   about.  The \meta{true code} is left in the input stream, and can
%   use the parameters |#1|, |#2|, |#3| for the three parts of the
%   property list as desired.  Namely, the original property list is in
%   this case |#1| \cs{@@_pair:wn} \meta{key} \cs{s_@@} |{#2}| |#3|.
%
%   If the \meta{key} is not there, then the \meta{function} is
%   \cs{use_ii:nn}, which keeps the \meta{false code}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_split:NnTF #1#2
  { \exp_args:NNo \@@_split_aux:NnTF #1 { \tl_to_str:n {#2} } }
\cs_new_protected:Npn \@@_split_aux:NnTF #1#2#3#4
  {
    \cs_set:Npn \@@_split_aux:w ##1
      \@@_pair:wn #2 \s_@@ ##2 ##3 \s_@@_mark ##4 ##5 \s_@@_stop
      { ##4 {#3} {#4} }
    \exp_after:wN \@@_split_aux:w #1 \s_@@_mark \use_i:nn
      \@@_pair:wn #2 \s_@@ { } \s_@@_mark \use_ii:nn \s_@@_stop
  }
\cs_new:Npn \@@_split_aux:w { }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[tested = m3prop002]
%   {\prop_remove:Nn, \prop_remove:NV, \prop_remove:cn, \prop_remove:cV}
% \begin{macro}[tested = m3prop002]
%   {\prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:cn, \prop_gremove:cV}
%   Deleting from a property starts by splitting the list.
%   If the key is present in the property list, the returned value is ignored.
%   If the key is missing, nothing happens.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_remove:Nn #1#2
  {
    \@@_split:NnTF #1 {#2}
      { \tl_set:Nn #1 { ##1 ##3 } }
      { }
  }
\cs_new_protected:Npn \prop_gremove:Nn #1#2
  {
    \@@_split:NnTF #1 {#2}
      { \tl_gset:Nn #1 { ##1 ##3 } }
      { }
  }
\cs_generate_variant:Nn \prop_remove:Nn  {     NV }
\cs_generate_variant:Nn \prop_remove:Nn  { c , cV }
\cs_generate_variant:Nn \prop_gremove:Nn {     NV }
\cs_generate_variant:Nn \prop_gremove:Nn { c , cV }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[tested = m3prop002]
%   {
%     \prop_get:NnN, \prop_get:NVN, \prop_get:NoN,
%     \prop_get:cnN, \prop_get:cVN, \prop_get:coN
%   }
%   Getting an item from a list is very easy: after splitting,
%   if the key is in the property list, just set the token list variable
%   to the return value, otherwise to \cs{q_no_value}.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_get:NnN #1#2#3
  {
    \@@_split:NnTF #1 {#2}
      { \tl_set:Nn #3 {##2} }
      { \tl_set:Nn #3 { \q_no_value } }
  }
\cs_generate_variant:Nn \prop_get:NnN {     NV , Nv , No }
\cs_generate_variant:Nn \prop_get:NnN { c , cV , cv , co }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[tested = m3prop002]
%   {\prop_pop:NnN, \prop_pop:NoN, \prop_pop:cnN, \prop_pop:coN}
% \begin{macro}[tested = m3prop002]
%   {\prop_gpop:NnN, \prop_gpop:NoN, \prop_gpop:cnN, \prop_gpop:coN}
%   Popping a value also starts by doing the split.
%   If the key is present, save the value in the token list and update the
%   property list as when deleting.
%   If the key is missing, save \cs{q_no_value} in the token list.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_pop:NnN #1#2#3
  {
    \@@_split:NnTF #1 {#2}
      {
        \tl_set:Nn #3 {##2}
        \tl_set:Nn #1 { ##1 ##3 }
      }
      { \tl_set:Nn #3 { \q_no_value } }
  }
\cs_new_protected:Npn \prop_gpop:NnN #1#2#3
  {
    \@@_split:NnTF #1 {#2}
      {
        \tl_set:Nn #3 {##2}
        \tl_gset:Nn #1 { ##1 ##3 }
      }
      { \tl_set:Nn #3 { \q_no_value } }
  }
\cs_generate_variant:Nn \prop_pop:NnN  {     No }
\cs_generate_variant:Nn \prop_pop:NnN  { c , co }
\cs_generate_variant:Nn \prop_gpop:NnN {     No }
\cs_generate_variant:Nn \prop_gpop:NnN { c , co }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\prop_item:Nn, \prop_item:cn}
% \begin{macro}[EXP]{\@@_item:nnn}
%   Getting the value corresponding to a key in a property list in an
%   expandable fashion simply uses \cs{prop_map_tokens:Nn} to go through
%   the property list.  The auxiliary \cs{@@_item:nnn} receives the
%   search string~|#1|, the key~|#2| and the value~|#3| and returns as
%   appropriate.
%    \begin{macrocode}
\cs_new:Npn \prop_item:Nn #1#2
  {
    \exp_args:NNo \prop_map_tokens:Nn #1
      { \exp_after:wN \@@_item:nnn \exp_after:wN { \tl_to_str:n {#2} } }
  }
\cs_new:Npn \@@_item:nnn #1#2#3
  {
    \str_if_eq:eeT {#1} {#2}
      { \prop_map_break:n { \exp_not:n {#3} } }
  }
\cs_generate_variant:Nn \prop_item:Nn { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\prop_count:N, \prop_count:c}
% \begin{macro}[EXP]{\@@_count:nn}
%   Counting the key--value pairs in a property list is done using the
%   same approach as for other count functions: turn each entry into a
%   \texttt{+1} then use integer evaluation to actually do the
%   mathematics.
%    \begin{macrocode}
\cs_new:Npn \prop_count:N #1
  {
    \int_eval:n
      {
        0
        \prop_map_function:NN #1 \@@_count:nn
      }
  }
\cs_new:Npn \@@_count:nn #1#2 { + 1 }
\cs_generate_variant:Nn \prop_count:N { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\prop_to_keyval:N}
% \begin{macro}[EXP]
%   {\@@_to_keyval_exp_after:wN, \@@_to_keyval:nn, \@@_to_keyval:nnw}
%   Each property name and value pair will be returned in the form
%   \verb*| |\marg{name}\verb*|= |\marg{value}. As one of the main use cases for
%   this macro is to pass the \meta{property list} on to a key--value parser, we
%   have to make sure that the behaviour is as good as possible. Using a space
%   before the opening brace we get the correct brace stripping behaviour for
%   most of the key--value parsers available in \LaTeX.
%   Iterate over the
%   \meta{property list} and remove the leading comma afterwards. Only the value
%   has to be protected in \cs{__kernel_exp_not:w} as the property name is
%   always a string. After the loop the leading comma is removed by
%   \cs{use_none:n} and afterwards \cs{__kernel_exp_not:w} eventually finds the
%   opening brace of its argument.
%    \begin{macrocode}
\cs_new:Npn \prop_to_keyval:N #1
  {
    \__kernel_exp_not:w
      \prop_if_empty:NTF #1
        { {} }
        {
          \exp_after:wN \exp_after:wN \exp_after:wN
          {
            \tex_expanded:D
              {
                \__kernel_exp_not:w { \use_none:n }
                \prop_map_function:NN #1 \@@_to_keyval:nn
              }
          }
        }
  }
\cs_new:Npn \@@_to_keyval:nn #1#2
  { , ~ {#1} =~ { \__kernel_exp_not:w {#2} } }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[TF, tested = m3prop004]
%   {\prop_pop:NnN, \prop_pop:cnN, \prop_gpop:NnN, \prop_gpop:cnN}
%   Popping an item from a property list, keeping track of whether
%   the key was present or not, is implemented as a conditional.
%   If the key was missing, neither the property list, nor the token
%   list are altered. Otherwise, \cs{prg_return_true:} is used after
%   the assignments.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \prop_pop:NnN #1#2#3 { T , F , TF }
  {
    \@@_split:NnTF #1 {#2}
      {
        \tl_set:Nn #3 {##2}
        \tl_set:Nn #1 { ##1 ##3 }
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\prg_new_protected_conditional:Npnn \prop_gpop:NnN #1#2#3 { T , F , TF }
  {
    \@@_split:NnTF #1 {#2}
      {
        \tl_set:Nn #3 {##2}
        \tl_gset:Nn #1 { ##1 ##3 }
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \prop_pop:NnN { c } { T , F , TF }
\prg_generate_conditional_variant:Nnn \prop_gpop:NnN { c } { T , F , TF }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[tested = m3prop002]
%   {
%     \prop_put:Nnn, \prop_put:NnV, \prop_put:Nno, \prop_put:Nne, \prop_put:Nnx,
%     \prop_put:NVn, \prop_put:NVV, \prop_put:NVx, \prop_put:Nvx,
%     \prop_put:Non, \prop_put:Noo, \prop_put:Nxx,
%     \prop_put:cnn, \prop_put:cnV, \prop_put:cno, \prop_put:cne, \prop_put:cnx,
%     \prop_put:cVn, \prop_put:cVV, \prop_put:cVx, \prop_put:cvx,
%     \prop_put:con, \prop_put:coo, \prop_put:cxx
%   }
% \begin{macro}[tested = m3prop002]
%   {
%     \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nno, \prop_gput:Nne, \prop_gput:Nnx,
%     \prop_gput:NVn, \prop_gput:NVV, \prop_hput:NVx, \prop_hput:Nvx,
%     \prop_gput:Non, \prop_gput:Noo, \prop_gput:Nxx,
%     \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cno, \prop_gput:cne, \prop_gput:cnx,
%     \prop_gput:cVn, \prop_gput:cVV, \prop_gput:cVx, \prop_gput:cvx,
%     \prop_gput:con, \prop_gput:coo, \prop_gput:cxx
%   }
% \begin{macro}{\@@_put:NNnn}
%   Since the branches of \cs{@@_split:NnTF} are used as the replacement
%   text of an internal macro, and since the \meta{key} and new
%   \meta{value} may contain arbitrary tokens, it is not safe to include
%   them in the argument of \cs{@@_split:NnTF}.  We thus start by
%   storing in \cs{l_@@_internal_tl} tokens which (after
%   \texttt{x}-expansion) encode the key--value pair.  This variable can
%   safely be used in \cs{@@_split:NnTF}.  If the \meta{key} was absent,
%   append the new key--value to the list.
%   Otherwise concatenate the extracts |##1|
%   and |##3| with the new key--value pair \cs{l_@@_internal_tl}.  The
%   updated entry is placed at the same spot as the original \meta{key}
%   in the property list, preserving the order of entries.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_put:Nnn  { \@@_put:NNnn \__kernel_tl_set:Nx }
\cs_new_protected:Npn \prop_gput:Nnn { \@@_put:NNnn \__kernel_tl_gset:Nx }
\cs_new_protected:Npn \@@_put:NNnn #1#2#3#4
  {
    \tl_set:Nn \l_@@_internal_tl
      {
        \exp_not:N \@@_pair:wn \tl_to_str:n {#3}
        \s_@@ { \exp_not:n {#4} }
      }
    \@@_split:NnTF #2 {#3}
      { #1 #2 { \exp_not:n {##1} \l_@@_internal_tl \exp_not:n {##3} } }
      { #1 #2 { \exp_not:o {#2} \l_@@_internal_tl } }
  }
\cs_generate_variant:Nn \prop_put:Nnn
  {     NnV , Nno , Nne , Nnx , NV , NVV , NVx , Nvx , No , Noo , Nxx }
\cs_generate_variant:Nn \prop_put:Nnn
  { c , cnV , cno , cne , cnx , cV , cVV , cVx , cvx , co , coo , cxx }
\cs_generate_variant:Nn \prop_gput:Nnn
  {     NnV , Nno , Nne , Nnx , NV , NVV , NVx , Nvx , No , Noo , Nxx }
\cs_generate_variant:Nn \prop_gput:Nnn
  { c , cnV , cno , cne , cnx , cV , cVV , cVx , cvx , co , coo , cxx }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[tested = m3prop002]
%   {\prop_put_if_new:Nnn, \prop_put_if_new:cnn}
% \begin{macro}[tested = m3prop002]
%   {\prop_gput_if_new:Nnn, \prop_gput_if_new:cnn}
% \begin{macro}{\@@_put_if_new:NNnn}
%   Adding conditionally also splits. If the key is already present,
%   the three brace groups given by \cs{@@_split:NnTF} are removed.
%   If the key is new, then the value is added, being careful to
%   convert the key to a string using \cs{tl_to_str:n}.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_put_if_new:Nnn
  { \@@_put_if_new:NNnn \__kernel_tl_set:Nx }
\cs_new_protected:Npn \prop_gput_if_new:Nnn
  { \@@_put_if_new:NNnn \__kernel_tl_gset:Nx }
\cs_new_protected:Npn \@@_put_if_new:NNnn #1#2#3#4
  {
    \tl_set:Nn \l_@@_internal_tl
      {
        \exp_not:N \@@_pair:wn \tl_to_str:n {#3}
        \s_@@ \exp_not:n { {#4} }
      }
    \@@_split:NnTF #2 {#3}
      { }
      { #1 #2 { \exp_not:o {#2} \l_@@_internal_tl } }
  }
\cs_generate_variant:Nn \prop_put_if_new:Nnn  { c }
\cs_generate_variant:Nn \prop_gput_if_new:Nnn { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Property list conditionals}
%
% \begin{macro}[pTF, tested = m3prop004]{\prop_if_exist:N, \prop_if_exist:c}
%   Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
%    \begin{macrocode}
\prg_new_eq_conditional:NNn \prop_if_exist:N \cs_if_exist:N
  { TF , T , F , p }
\prg_new_eq_conditional:NNn \prop_if_exist:c \cs_if_exist:c
  { TF , T , F , p }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF, tested = m3prop003]{\prop_if_empty:N, \prop_if_empty:c}
%   Same test as for token lists.
%    \begin{macrocode}
\prg_new_conditional:Npnn \prop_if_empty:N #1 { p , T , F , TF }
  {
    \tl_if_eq:NNTF #1 \c_empty_prop
      \prg_return_true: \prg_return_false:
  }
\prg_generate_conditional_variant:Nnn \prop_if_empty:N
  { c } { p , T , F , TF }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF, tested = m3prop003]
%   {
%     \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:No,
%     \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:co
%   }
% \begin{macro}[EXP]{\@@_if_in:nnn}
%   Testing expandably if a key is in a property list
%   requires to go through the key--value pairs one by one.
%   This is rather slow, and a faster test would be
%   \begin{verbatim}
%     \prg_new_protected_conditional:Npnn \prop_if_in:Nn #1 #2
%       {
%         \@@_split:NnTF #1 {#2}
%           { \prg_return_true: }
%           { \prg_return_false: }
%       }
%   \end{verbatim}
%   but \cs{@@_split:NnTF} is non-expandable.
%   Instead, we use \cs{prop_map_tokens:Nn} to compare the search key to
%   each key in turn using \cs{str_if_eq:ee}, which is expandable.
%    \begin{macrocode}
\prg_new_conditional:Npnn \prop_if_in:Nn #1#2 { p , T , F , TF }
  {
    \exp_args:NNo \prop_map_tokens:Nn #1
      { \exp_after:wN \@@_if_in:nnn \exp_after:wN { \tl_to_str:n {#2} } }
    \prg_return_false:
  }
\cs_new:Npn \@@_if_in:nnn #1#2#3
  {
    \str_if_eq:eeT {#1} {#2}
      { \prop_map_break:n { \use_i:nn \prg_return_true: } }
  }
\prg_generate_conditional_variant:Nnn \prop_if_in:Nn
  { NV , No , c , cV , co } { p , T , F , TF }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Recovering values from property lists with branching}
%
% \begin{macro}[TF, tested = m3prop004]
%   {
%     \prop_get:NnN, \prop_get:NVN, \prop_get:NoN,
%     \prop_get:cnN, \prop_get:cVN, \prop_get:coN
%   }
%   Getting the value corresponding to a key, keeping track of whether
%   the key was present or not, is implemented as a conditional (with
%   side effects). If the key was absent, the token list is not altered.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \prop_get:NnN #1#2#3 { T , F , TF }
  {
    \@@_split:NnTF #1 {#2}
      {
        \tl_set:Nn #3 {##2}
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \prop_get:NnN
  { NV , Nv , No , c , cV , cv , co } { T , F , TF }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Mapping over property lists}
%
% \begin{macro}[tested = m3prop003]
%   {
%     \prop_map_function:NN, \prop_map_function:Nc,
%     \prop_map_function:cN, \prop_map_function:cc
%   }
% \begin{macro}{\@@_map_function:Nw}
%   The even-numbered arguments of \cs{@@_map_function:Nw} are keys,
%   hence have string catcodes, except at the end where they are
%   \cs{fi:} \cs{prop_map_break:}.  The \cs{fi:} ends the \cs{if_false:}
%   |#|\meta{even} \cs{fi:} construction and we jump out of the loop.
%   No need for any quark test.
%    \begin{macrocode}
\cs_new:Npn \prop_map_function:NN #1#2
  {
    \exp_after:wN \use_i_ii:nnn
    \exp_after:wN \@@_map_function:Nw
    \exp_after:wN #2
    #1
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \prg_break_point:Nn \prop_map_break: { }
  }
\cs_new:Npn \@@_map_function:Nw #1
    \@@_pair:wn #2 \s_@@ #3
    \@@_pair:wn #4 \s_@@ #5
    \@@_pair:wn #6 \s_@@ #7
    \@@_pair:wn #8 \s_@@ #9
  {
    \if_false: #2 \fi: #1 {#2} {#3}
    \if_false: #4 \fi: #1 {#4} {#5}
    \if_false: #6 \fi: #1 {#6} {#7}
    \if_false: #8 \fi: #1 {#8} {#9}
    \@@_map_function:Nw #1
  }
\cs_generate_variant:Nn \prop_map_function:NN { Nc , c , cc }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[tested = m3prop003]{\prop_map_inline:Nn, \prop_map_inline:cn}
%   Mapping in line requires a nesting level counter.  Store the current
%   definition of \cs{@@_pair:wn}, and define it anew.  At the end of
%   the loop, revert to the earlier definition.  Note that besides pairs
%   of the form \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{value}, there
%   are a leading and a trailing tokens, but both are equal to
%   \cs{scan_stop:}, hence have no effect in such inline mapping.
%   Such \cs{scan_stop:} could have affected ligatures if they appeared
%   during the mapping.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_map_inline:Nn #1#2
  {
    \cs_gset_eq:cN
      { @@_map_ \int_use:N \g__kernel_prg_map_int :wn } \@@_pair:wn
    \int_gincr:N \g__kernel_prg_map_int
    \cs_gset_protected:Npn \@@_pair:wn ##1 \s_@@ ##2 {#2}
    #1
    \prg_break_point:Nn \prop_map_break:
      {
        \int_gdecr:N \g__kernel_prg_map_int
        \cs_gset_eq:Nc \@@_pair:wn
          { @@_map_ \int_use:N \g__kernel_prg_map_int :wn }
      }
  }
\cs_generate_variant:Nn \prop_map_inline:Nn { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[rEXP]{\prop_map_tokens:Nn, \prop_map_tokens:cn}
% \begin{macro}{\@@_map_tokens:nw}
%   The mapping is very similar to \cs{prop_map_function:NN}.  The
%   \cs{use_i:nn} removes the leading \cs{s_@@}.  The odd construction
%   |\use:n {#1}| allows |#1| to contain any token without interfering
%   with \cs{prop_map_break:}.  The loop stops when the \meta{key}
%   between \cs{@@_pair:wn} and \cs{s_@@} is \cs{fi:}
%   \cs{prop_map_break:} instead of being a string.
%    \begin{macrocode}
\cs_new:Npn \prop_map_tokens:Nn #1#2
  {
    \exp_last_unbraced:Nno
      \use_i:nn { \@@_map_tokens:nw {#2} } #1
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
    \prg_break_point:Nn \prop_map_break: { }
  }
\cs_new:Npn \@@_map_tokens:nw #1
    \@@_pair:wn #2 \s_@@ #3
    \@@_pair:wn #4 \s_@@ #5
    \@@_pair:wn #6 \s_@@ #7
    \@@_pair:wn #8 \s_@@ #9
  {
    \if_false: #2 \fi: \use:n {#1} {#2} {#3}
    \if_false: #4 \fi: \use:n {#1} {#4} {#5}
    \if_false: #6 \fi: \use:n {#1} {#6} {#7}
    \if_false: #8 \fi: \use:n {#1} {#8} {#9}
    \@@_map_tokens:nw {#1}
  }
\cs_generate_variant:Nn \prop_map_tokens:Nn { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
% \begin{macro}[tested = m3prop003]{\prop_map_break:}
% \begin{macro}[tested = m3prop003]{\prop_map_break:n}
%   The break statements are based on the general \cs{prg_map_break:Nn}.
%    \begin{macrocode}
\cs_new:Npn \prop_map_break:
  { \prg_map_break:Nn \prop_map_break: { } }
\cs_new:Npn \prop_map_break:n
  { \prg_map_break:Nn \prop_map_break: }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Viewing property lists}
%
% \begin{macro}[tested = m3show001]
%   {\prop_show:N, \prop_show:c, \prop_log:N, \prop_log:c}
% \begin{macro}{\@@_show:NN}
% \begin{macro}[rEXP]{\@@_show_validate:w}
%   Apply the general \cs{__kernel_chk_tl_type:NnnT}.
%   Contrarily to sequences and comma lists,
%   we use \cs{msg_show_item:nn} to format both the key and the value
%   for each pair.
%    \begin{macrocode}
\cs_new_protected:Npn \prop_show:N { \@@_show:NN \msg_show:nnxxxx }
\cs_generate_variant:Nn \prop_show:N { c }
\cs_new_protected:Npn \prop_log:N { \@@_show:NN \msg_log:nnxxxx }
\cs_generate_variant:Nn \prop_log:N { c }
\cs_new_protected:Npn \@@_show:NN #1#2
  {
    \__kernel_chk_tl_type:NnnT #2 { prop }
      {
        \s_@@
        \exp_after:wN \use_i:nn \exp_after:wN \@@_show_validate:w #2
        \@@_pair:wn \q_recursion_tail \s_@@ { } \q_recursion_stop
      }
      {
        #1 { prop } { show }
          { \token_to_str:N #2 }
          { \prop_map_function:NN #2 \msg_show_item:nn }
          { } { }
      }
  }
\cs_new:Npn \@@_show_validate:w #1 \@@_pair:wn #2 \s_@@ #3
  {
    \quark_if_recursion_tail_stop:n {#2}
    \exp_not:N \@@_pair:wn \tl_to_str:n {#2} \s_@@ \exp_not:n { {#3} }
    \@@_show_validate:w
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
