From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/l3kernel/l3prop.dtx | 1262 ++++++++++++++++++++++++++++++ 1 file changed, 1262 insertions(+) create mode 100644 macros/latex/contrib/l3kernel/l3prop.dtx (limited to 'macros/latex/contrib/l3kernel/l3prop.dtx') diff --git a/macros/latex/contrib/l3kernel/l3prop.dtx b/macros/latex/contrib/l3kernel/l3prop.dtx new file mode 100644 index 0000000000..d79073065d --- /dev/null +++ b/macros/latex/contrib/l3kernel/l3prop.dtx @@ -0,0 +1,1262 @@ +% \iffalse meta-comment +% +%% File: l3prop.dtx +% +% Copyright (C) 1990-2019 The LaTeX3 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 \pkg{l3prop} package\\ Property lists^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2019-08-25} +% +% \maketitle +% +% \begin{documentation} +% +% \LaTeX3 implements a \enquote{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}. 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{keys} 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} +% +% \section{Adding entries to property lists} +% +% \begin{function}[updated = 2012-07-09] +% { +% \prop_put:Nnn, \prop_put:NnV, \prop_put:Nno, \prop_put:Nnx, +% \prop_put:NVn, \prop_put:NVV, \prop_put:Non, \prop_put:Noo, +% \prop_put:cnn, \prop_put:cnV, \prop_put:cno, \prop_put:cnx, +% \prop_put:cVn, \prop_put:cVV, \prop_put:con, \prop_put:coo, +% \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nno, \prop_gput:Nnx, +% \prop_gput:NVn, \prop_gput:NVV, \prop_gput:Non, \prop_gput:Noo, +% \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cno, \prop_gput:cnx, +% \prop_gput:cVn, \prop_gput:cVV, \prop_gput:con, \prop_gput:coo +% } +% \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}. 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. If the \meta{key} is already present +% in the \meta{property list}, the existing entry is overwritten +% by the new \meta{value}. +% \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. If the \meta{key} is not present in the +% \meta{property list} then a new entry is added. 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} +% +% \section{Recovering values from property lists} +% +% \begin{function}[updated = 2011-08-28] +% { +% \prop_get:NnN, \prop_get:NVN, \prop_get:NoN, +% \prop_get:cnN, \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 +% 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} +% +% \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: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:NoN, +% \prop_get:cnN, \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 to 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. +% \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}[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 { } } +% { +% % 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 = 2015-08-01]{\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 = 2015-08-01]{\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} +%<*initex|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 + { \__kernel_msg_expandable_error:nn { kernel } { misused-prop } } +% \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{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_set_from_keyval:Nn} and others. +% \begin{macrocode} +\prop_new:N \l_@@_internal_prop +% \end{macrocode} +% \end{variable} +% +% \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} +% { +% \@@_from_keyval:n, +% \@@_from_keyval_loop:w, +% \@@_from_keyval_split:Nw, +% \@@_from_keyval_key:n, +% \@@_from_keyval_key:w, +% \@@_from_keyval_value:n, +% \@@_from_keyval_value:w +% } +% 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{q_nil}, the subsequent part of the item, and the trailing |=|'s +% and \cs{q_stop}. After finding the \meta{key} just store it after +% \cs{q_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#2 + { + \prop_clear:N \l_@@_internal_prop + \@@_from_keyval:n {#2} + \prop_set_eq:NN #1 \l_@@_internal_prop + \prop_clear:N \l_@@_internal_prop + } +\cs_generate_variant:Nn \prop_set_from_keyval:Nn { c } +\cs_new_protected:Npn \prop_gset_from_keyval:Nn #1#2 + { + \prop_clear:N \l_@@_internal_prop + \@@_from_keyval:n {#2} + \prop_gset_eq:NN #1 \l_@@_internal_prop + \prop_clear:N \l_@@_internal_prop + } +\cs_generate_variant:Nn \prop_gset_from_keyval:Nn { c } +\cs_new_protected:Npn \prop_const_from_keyval:Nn #1#2 + { + \prop_clear:N \l_@@_internal_prop + \@@_from_keyval:n {#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 \@@_from_keyval:n #1 + { + \@@_from_keyval_loop:w \prg_do_nothing: #1 , + \q_recursion_tail , \q_recursion_stop + } +\cs_new_protected:Npn \@@_from_keyval_loop:w #1 , + { + \quark_if_recursion_tail_stop:o {#1} + \@@_from_keyval_split:Nw \@@_from_keyval_key:n + #1 = = \q_stop {#1} + \@@_from_keyval_loop:w \prg_do_nothing: + } +\cs_new_protected:Npn \@@_from_keyval_split:Nw #1#2 = + { \tl_trim_spaces_apply:oN {#2} #1 } +\cs_new_protected:Npn \@@_from_keyval_key:n #1 + { \@@_from_keyval_key:w #1 \q_nil } +\cs_new_protected:Npn \@@_from_keyval_key:w #1 \q_nil #2 \q_stop + { + \@@_from_keyval_split:Nw \@@_from_keyval_value:n + \prg_do_nothing: #2 \q_stop {#1} + } +\cs_new_protected:Npn \@@_from_keyval_value:n #1 + { \@@_from_keyval_value:w #1 \q_nil } +\cs_new_protected:Npn \@@_from_keyval_value:w #1 \q_nil #2 \q_stop #3#4 + { + \tl_if_empty:nF { #3 #1 #2 } + { + \str_if_eq:nnTF {#2} { = } + { \prop_put:Nnn \l_@@_internal_prop {#3} {#1} } + { + \__kernel_msg_error:nnx { kernel } { prop-keyval } + { \exp_not:o {#4} } + } + } + } +% \end{macrocode} +% \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{q_mark} |#4| |#5| \cs{q_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 \q_mark ##4 ##5 \q_stop + { ##4 {#3} {#4} } + \exp_after:wN \@@_split_aux:w #1 \q_mark \use_i:nn + \@@_pair:wn #2 \s_@@ { } \q_mark \use_ii:nn \q_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 , No } +\cs_generate_variant:Nn \prop_get:NnN { c , 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_Nn:nwwn} +% Getting the value corresponding to a key in a property list in an +% expandable fashion is similar to mapping some tokens. Go through +% the property list one \meta{key}--\meta{value} pair at a time: the +% arguments of \cs{@@_item_Nn:nwn} are the \meta{key} we are looking +% for, a \meta{key} of the property list, and its associated value. +% The \meta{keys} are compared (as strings). If they match, the +% \meta{value} is returned, within \cs{exp_not:n}. The loop +% terminates even if the \meta{key} is missing, and yields an empty +% value, because we have appended the appropriate +% \meta{key}--\meta{empty value} pair to the property list. +% \begin{macrocode} +\cs_new:Npn \prop_item:Nn #1#2 + { + \exp_last_unbraced:Noo \@@_item_Nn:nwwn { \tl_to_str:n {#2} } #1 + \@@_pair:wn \tl_to_str:n {#2} \s_@@ { } + \prg_break_point: + } +\cs_new:Npn \@@_item_Nn:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4 + { + \str_if_eq:eeTF {#1} {#3} + { \prg_break:n { \exp_not:n {#4} } } + { \@@_item_Nn:nwwn {#1} } + } +\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}[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:Nnx, +% \prop_put:NVn, \prop_put:NVV, \prop_put:Non, \prop_put:Noo, +% \prop_put:cnn, \prop_put:cnV, \prop_put:cno, \prop_put:cnx, +% \prop_put:cVn, \prop_put:cVV, \prop_put:con, \prop_put:coo +% } +% \begin{macro}[tested = m3prop002] +% { +% \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nno, \prop_gput:Nnx, +% \prop_gput:NVn, \prop_gput:NVV, \prop_gput:Non, \prop_gput:Noo, +% \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cno, \prop_gput:cnx, +% \prop_gput:cVn, \prop_gput:cVV, \prop_gput:con, \prop_gput:coo +% } +% \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 \tl_set:Nx } +\cs_new_protected:Npn \prop_gput:Nnn { \@@_put:NNnn \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 , Nnx , NV , NVV , No , Noo } +\cs_generate_variant:Nn \prop_put:Nnn + { c , cnV , cno , cnx , cV , cVV , co , coo } +\cs_generate_variant:Nn \prop_gput:Nnn + { NnV , Nno , Nnx , NV , NVV , No , Noo } +\cs_generate_variant:Nn \prop_gput:Nnn + { c , cnV , cno , cnx , cV , cVV , co , coo } +% \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 \tl_set:Nx } +\cs_new_protected:Npn \prop_gput_if_new:Nnn + { \@@_put_if_new:NNnn \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:nwwn, \@@_if_in:N} +% 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, the key is compared to each key in turn using +% \cs{str_if_eq:ee}, which is expandable. To terminate the mapping, +% we append to the property list the key that is searched for. This +% second \cs{tl_to_str:n} is not expanded at the start, but only when +% included in the \cs{str_if_eq:ee}. It cannot make the breaking +% mechanism choke, because the arbitrary token list material is +% enclosed in braces. The second argument of \cs{@@_if_in:nwwn} is +% most often empty. When the \meta{key} is found in the list, +% \cs{@@_if_in:N} receives \cs{@@_pair:wn}, and if it is +% found as the extra item, the function receives +% \cs{q_recursion_tail}, easily recognizable. +% +% Here, \cs{prop_map_function:NN} is not sufficient for the mapping, +% since it can only map a single token, and cannot carry the key that +% is searched for. +% \begin{macrocode} +\prg_new_conditional:Npnn \prop_if_in:Nn #1#2 { p , T , F , TF } + { + \exp_last_unbraced:Noo \@@_if_in:nwwn { \tl_to_str:n {#2} } #1 + \@@_pair:wn \tl_to_str:n {#2} \s_@@ { } + \q_recursion_tail + \prg_break_point: + } +\cs_new:Npn \@@_if_in:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4 + { + \str_if_eq:eeTF {#1} {#3} + { \@@_if_in:N } + { \@@_if_in:nwwn {#1} } + } +\cs_new:Npn \@@_if_in:N #1 + { + \if_meaning:w \q_recursion_tail #1 + \prg_return_false: + \else: + \prg_return_true: + \fi: + \prg_break: + } +\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 , No , c , cV , co } { T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \subsection{Mapping to 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:Nwwn} +% The argument delimited by \cs{@@_pair:wn} is empty except at the end +% of the loop where it is \cs{prg_break:}. 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:Nwwn + \exp_after:wN #2 + #1 + \prg_break: \@@_pair:wn \s_@@ { } \prg_break_point: + \prg_break_point:Nn \prop_map_break: { } + } +\cs_new:Npn \@@_map_function:Nwwn #1#2 \@@_pair:wn #3 \s_@@ #4 + { + #2 + #1 {#3} {#4} + \@@_map_function:Nwwn #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}[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} +% Apply the general \cs{__kernel_chk_defined:NT} and +% \cs{msg_show:nnnnnn}. 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_defined:NT #2 + { + #1 { LaTeX/kernel } { show-prop } + { \token_to_str:N #2 } + { \prop_map_function:NN #2 \msg_show_item:nn } + { } { } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex -- cgit v1.2.3