summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3keys.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3keys.dtx729
1 files changed, 699 insertions, 30 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx b/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
index df988bea954..0325817636e 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
@@ -35,7 +35,7 @@
%
%<*driver|package>
\RequirePackage{l3names}
-\GetIdInfo$Id: l3keys.dtx 2568 2011-08-14 05:03:32Z bruno $
+\GetIdInfo$Id: l3keys.dtx 2677 2011-08-28 06:54:22Z joseph $
{L3 Experimental key-value interfaces}
%</driver|package>
%<*driver>
@@ -189,15 +189,55 @@
% does not exist, it will be created at the point that the key is
% set up. The \meta{boolean} will be assigned globally.
% \end{function}
+%
+% \begin{function}{.bool_set_inverse:N}
+% \begin{syntax}
+% \meta{key} .bool_set_inverse:N = \meta{boolean}
+% \end{syntax}
+% Defines \meta{key} to set \meta{boolean} to the logical
+% inverse of \meta{value} (which must be either \texttt{true} or
+% \texttt{false}).
+% If the \meta{boolean} does not exist, it will be created at the point
+% that the key is set up. The \meta{boolean} will be assigned locally.
+%
+% \textbf{This property is experimental.}
+% \end{function}
+%
+% \begin{function}{.bool_gset_inverse:N}
+% \begin{syntax}
+% \meta{key} .bool_gset_inverse:N = \meta{boolean}
+% \end{syntax}
+% Defines \meta{key} to set \meta{boolean} to the logical
+% inverse of \meta{value} (which must be either \texttt{true} or
+% \texttt{false}).
+% If the \meta{boolean} does not exist, it will be created at the point
+% that the key is set up. The \meta{boolean} will be assigned globally.
+%
+% \textbf{This property is experimental.}
+% \end{function}
%
% \begin{function}{.choice:}
% \begin{syntax}
% \meta{key} .choice:
% \end{syntax}
-% Sets \meta{key} to act as a multiple choice key. Each valid choice
+% Sets \meta{key} to act as a choice key. Each valid choice
% for \meta{key} must then be created, as discussed in
% section~\ref{sec:choice}.
% \end{function}
+%
+% \begin{function}{.choices:nn}
+% \begin{syntax}
+% \meta{key} .choices:nn \meta{choices} \meta{code}
+% \end{syntax}
+% Sets \meta{key} to act as a choice key, and defines a series \meta{choices}
+% which are implemented using the \meta{code}. Inside \meta{code},
+% \cs{l_keys_choice_tl} will be the name of the choice made, and
+% \cs{l_keys_choice_int} will be the position of the choice in the list
+% of \meta{choices} (indexed from $0$).
+% Choices are discussed in detail in section~\ref{sec:choice}.
+%
+% \textbf{This property is experimental.}
+% \end{function}
%
% \begin{function}{.choice_code:n, .choice_code:x}
% \begin{syntax}
@@ -325,6 +365,32 @@
% is used, then the value will be passed through to the subsidiary
% \meta{keys} for processing (as |#1|).
% \end{function}
+%
+% \begin{function}{.multichoice:}
+% \begin{syntax}
+% \meta{key} .multichoice:
+% \end{syntax}
+% Sets \meta{key} to act as a multiple choice key. Each valid choice
+% for \meta{key} must then be created, as discussed in
+% section~\ref{sec:choice}.
+%
+% \textbf{This property is experimental.}
+% \end{function}
+%
+% \begin{function}{.multichoice:nn}
+% \begin{syntax}
+% \meta{key} .multichoice:nn \meta{choices} \meta{code}
+% \end{syntax}
+% Sets \meta{key} to act as a multiple choice key, and defines a series
+% \meta{choices}
+% which are implemented using the \meta{code}. Inside \meta{code},
+% \cs{l_keys_choice_tl} will be the name of the choice made, and
+% \cs{l_keys_choice_int} will be the position of the choice in the list
+% of \meta{choices} (indexed from $0$).
+% Choices are discussed in detail in section~\ref{sec:choice}.
+%
+% \textbf{This property is experimental.}
+% \end{function}
%
% \begin{function}{.skip_set:N, .skip_set:c}
% \begin{syntax}
@@ -427,10 +493,16 @@
% As will be illustrated in the next section, this subdivision is
% particularly relevant to making multiple choices.
%
-% \section{Multiple choice keys}
+% \section{Choice and multiple choice keys}
% \label{sec:choice}
%
-% Multiple choices are created by setting the \texttt{.choice:}
+% The \pkg{l3keys} system supports two types of choice key, in which a series
+% of pre-defined input values are linked to varying implementations. Choice
+% keys are usually created so that the various values are mutually-exclusive:
+% only one can apply at any one time. \enquote{Multiple} choice keys are also
+% supported: these allow a selection of values to be chosen at the same time.
+%
+% Mutually-exclusive choices are created by setting the \texttt{.choice:}
% property:
% \begin{verbatim}
% \keys_define:nn { module }
@@ -460,13 +532,34 @@
% }
% \end{verbatim}
% Following common computing practice, \cs{l_keys_choice_int} is
-% indexed from $0$ (as an offset), so that the value of
+% indexed from $0$ (as an offset), so that the value of
% \cs{l_keys_choice_int} for the first choice in a list will be
% zero.
+%
+% The same approach is also implemented by the \emph{experimental}
+% property \texttt{.choices:nn}. This combines the functionality of
+% \texttt{.choice_code:n} and \texttt{.generate_choices:n} into one
+% property:
+% \begin{verbatim}
+% \keys_define:nn { module }
+% {
+% key .choices:nn =
+% { choice-a, choice-b, choice-c }
+% {
+% You~gave~choice~'\int_use:N \l_keys_choice_tl',~
+% which~is~in~position~
+% \int_use:N \l_keys_choice_int \c_space_tl
+% in~the~list.
+% }
+% }
+% \end{verbatim}
+% Note that the \texttt{.choices:nn} property should \emph{not} be mixed with
+% use of \texttt{.generate_choices:n}.
%
% \begin{variable}{\l_keys_choice_int, \l_keys_choice_tl}
% Inside the code block for a choice generated using
-% \texttt{.generate_choice:}, the variables \cs{l_keys_choice_tl} and
+% \texttt{.generate_choice:} or \texttt{.choices:nn},
+% the variables \cs{l_keys_choice_tl} and
% \cs{l_keys_choice_int} are available to indicate the name of the
% current choice, and its position in the comma list. The position
% is indexed from $0$.
@@ -479,7 +572,7 @@
% \begin{verbatim}
% \keys_define:nn { module }
% {
-% key .choice:n,
+% key .choice:,
% key / choice-a .code:n = code-a,
% key / choice-b .code:n = code-b,
% key / choice-c .code:n = code-c,
@@ -491,6 +584,48 @@
% These variables do not have defined behaviour when used outside of
% code created using \texttt{.generate_choices:n}
% (\emph{i.e.}~anything might happen).
+%
+% Multiple choices are created in a very similar manner to mutually-exclusive
+% choices, using the properties \texttt{.multichoice:} and
+% \texttt{.multichoices:nn}. As with mutually exclusive choices, multiple
+% choices are define as sub-keys. Thus both
+% \begin{verbatim}
+% \keys_define:nn { module }
+% {
+% key .multichoices:nn =
+% { choice-a, choice-b, choice-c }
+% {
+% You~gave~choice~'\int_use:N \l_keys_choice_tl',~
+% which~is~in~position~
+% \int_use:N \l_keys_choice_int \c_space_tl
+% in~the~list.
+% }
+% }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \keys_define:nn { module }
+% {
+% key .multichoice:,
+% key / choice-a .code:n = code-a,
+% key / choice-b .code:n = code-b,
+% key / choice-c .code:n = code-c,
+% }
+%\end{verbatim}
+% are valid. The \texttt{.multichoices:nn} property causes
+% \cs{l_keys_choice_tl} and \cs{l_keys_choice_int} to be set in exactly
+% the same way as described for \texttt{.choices:nn}.
+%
+% When multiple choice keys are set, the value is treated as a comma-separated
+% list:
+% \begin{verbatim}
+% \keys_set:nn { module }
+% {
+% key = { a , b , c } % 'key' defined as a multiple choice
+% }
+%\end{verbatim}
+% Each choice will be applied in turn, with the usual handling of unknown
+% values.
%
% \section{Setting keys}
%
@@ -501,9 +636,7 @@
% Parses the \meta{keyval list}, and sets those keys which are defined
% for \meta{module}. The behaviour on finding an unknown key can be set
% by defining a special \texttt{unknown} key: this will be illustrated
-% later. In contrast to \cs{keys_define:nn}, this function does check
-% category codes and ignore spaces, and is therefore suitable for user
-% input.
+% later.
% \end{function}
%
% If a key is not known, \cs{keys_set:nn} will look for a special
@@ -520,8 +653,7 @@
% \begin{variable}{\l_keys_key_tl}
% When processing an unknown key, the name of the key is available
% as \cs{l_keys_key_tl}. Note that this will have been processed
-% using \cs{tl_to_str:n}. The value passed to the key (if any) is
-% available as the macro parameter |#1|.
+% using \cs{tl_to_str:n}.
% \end{variable}
%
% \begin{variable}{\l_keys_path_tl}
@@ -529,22 +661,55 @@
% as \cs{l_keys_path_tl}. Note that this will have been processed
% using \cs{tl_to_str:n}.
% \end{variable}
+%
+% \begin{variable}{\l_keys_value_tl}
+% When processing an unknown key, the value of the key is available as
+% \cs{l_keys_value_tl}. Note that this will be empty if no value was given
+% for the key.
+% \end{variable}
+%
+% \section{Setting known keys only}
+%
+% The functionality described in this section is experimental and may be
+% altered or removed, depending on feedback.
+%
+% \begin{function}
+% {
+% \keys_set_known:nnN, \keys_set_known:nVN,
+% \keys_set_known:nvN, \keys_set_known:noN
+% }
+% \begin{syntax}
+% \cs{keys_set_known:nn} \Arg{module} \Arg{keyval list} \meta{clist}
+% \end{syntax}
+% Parses the \meta{keyval list}, and sets those keys which are defined
+% for \meta{module}. Any keys which are unknown are not processed further
+% by the parser.
+% The key--value pairs for each \emph{unknown} key name will be
+% stored in the \meta{clist}.
+% \end{function}
%
% \section{Utility functions for keys}
%
% \begin{function}[EXP,pTF]{\keys_if_exist:nn}
% \begin{syntax}
-% \cs{keys_if_exist:nn} \meta{module} \meta{key}
-% \cs{keys_if_exist:nn} \meta{module} \meta{key}
+% \cs{keys_if_exist_p:nn} \meta{module} \meta{key}
+% \cs{keys_if_exist:nnTF} \meta{module} \meta{key}
% ~~\Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the \meta{key} exists for \meta{module}, \emph{i.e.}~if any code
% has been defined for \meta{key}.
-% The branching versions then leave either \meta{true code} or
-% \meta{false code} in the input stream, as appropriate to the truth
-% of the test and the variant of the function chosen. The logical
-% truth of the test is left in the input stream by the predicate
-% version.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\keys_if_choice_exist:nn}
+% \begin{syntax}
+% \cs{keys_if_exist_p:nnn} \meta{module} \meta{key} \meta{choice}
+% \cs{keys_if_exist:nnnTF} \meta{module} \meta{key} \meta{choice}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{choice} is defined for the \meta{key} within the
+% \meta{module},, \emph{i.e.}~if any code has been defined for
+% \meta{key}/\meta{choice}. The test is \texttt{false} if the \meta{key}
+% itself is not defined.
% \end{function}
%
% \begin{function}{\keys_show:nn}
@@ -555,6 +720,78 @@
% \meta{key} for a \meta{module}.
% \end{function}
%
+% \section{Low-level interface for parsing key--val lists}
+%
+% To re-cap from earlier, a key--value list is input of the form
+% \begin{verbatim}
+% KeyOne = ValueOne ,
+% KeyTwo = ValueTwo ,
+% KeyThree
+% \end{verbatim}
+% where each key--value pair is separated by a comma from the rest of
+% the list, and each key--value pair does not necessarily contain an
+% equals sign or a value! Processing this type of input correctly
+% requires a number of careful steps, to correctly account for
+% braces, spaces and the category codes of separators.
+%
+% While the functions described earlier are used as a high-level interface
+% for processing such input, in especial circumstances you may wish to use
+% a lower-level approach.
+% The low-level parsing system converts a \meta{key--value list}
+% into \meta{keys} and associated \meta{values}. After the parsing phase
+% is completed, the resulting keys and values (or keys alone) are
+% available for further processing. This processing is not carried out by the
+% low-level parser itself, and so the parser requires the names of
+% two functions along with the key--value list. One function is
+% needed to process key--value pairs (\emph{i.e}~two arguments),
+% and a second function if required for keys given without arguments
+% (\emph{i.e.}~a single argument).
+%
+% The parser does not double |#| tokens or expand any input. The tokens
+% |=| and |,| are corrected so that the parser does not \enquote{miss}
+% any due to category code changes. Spaces are removed from the ends
+% of the keys and values. Values which are given in braces will have
+% exactly one set removed, thus
+% \begin{verbatim}
+% key = {value here},
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% key = value here,
+% \end{verbatim}
+% are treated identically.
+%
+% \begin{function}{\keyval_parse:NNn}
+% \begin{syntax}
+% \cs{keyval_parse:NNn} \meta{function1} \meta{function2}
+% ~~\Arg{key--value list}
+% \end{syntax}
+% Parses the \meta{key--value list} into a series of \meta{keys} and
+% associated \meta{values}, or keys alone (if no \meta{value} was
+% given). \meta{function1} should take one argument, while
+% \meta{function2} should absorb two arguments. After
+% \cs{keyval_parse:NNn} has parsed the \meta{key--value list},
+% \meta{function1} will be used to process keys given with no value
+% and \meta{function2} will be used to process keys given with a
+% value. The order of the \meta{keys} in the \meta{key--value list}
+% will be preserved. Thus
+% \begin{verbatim}
+% \keyval_parse:NNn \function:n \function:nn
+% { key1 = value1 , key2 = value2, key3 = , key4 }
+% \end{verbatim}
+% will be converted into an input stream
+% \begin{verbatim}
+% \function:nn { key1 } { value1 }
+% \function:nn { key2 } { value2 }
+% \function:nn { key3 } { }
+% \function:n { key4 }
+% \end{verbatim}
+% Note that there is a difference between an empty value (an equals
+% sign followed by nothing) and a missing value (no equals sign at
+% all).
+% \end{function}
+%
+%
% \end{documentation}
%
% \begin{implementation}
@@ -573,6 +810,239 @@
%</package>
% \end{macrocode}
%
+% \subsection{Low-level interface}
+%
+% For historical reasons this code uses the `keyval' module prefix.
+%
+% \begin{variable}{\g_keyval_level_int}
+% For nesting purposes an integer is needed for the current level.
+% \begin{macrocode}
+\int_new:N \g_keyval_level_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keyval_key_tl, \l_keyval_value_tl}
+% The current key name and value.
+% \begin{macrocode}
+\tl_new:N \l_keyval_key_tl
+\tl_new:N \l_keyval_value_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keyval_sanitise_tl}
+% \begin{variable}{\l_keyval_parse_tl}
+% Token list variables for dealing with awkward category codes in the
+% input.
+% \begin{macrocode}
+\tl_new:N \l_keyval_sanitise_tl
+\tl_new:N \l_keyval_parse_tl
+% \end{macrocode}
+%\end{variable}
+%\end{variable}
+%
+% \begin{macro}{\keyval_parse:n}
+% The parsing function first deals with the category codes for
+% |=| and |,|, so that there are no odd events. The input is then
+% handed off to the element by element system.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:n { `\= }
+ \char_set_catcode_active:n { `\, }
+ \char_set_lccode:nn { `\8 } { `\= }
+ \char_set_lccode:nn { `\9 } { `\, }
+\tl_to_lowercase:n
+ {
+ \group_end:
+ \cs_new_protected:Npn \keyval_parse:n #1
+ {
+ \group_begin:
+ \tl_clear:N \l_keyval_sanitise_tl
+ \tl_set:Nn \l_keyval_sanitise_tl {#1}
+ \tl_replace_all:Nnn \l_keyval_sanitise_tl { = } { 8 }
+ \tl_replace_all:Nnn \l_keyval_sanitise_tl { , } { 9 }
+ \tl_clear:N \l_keyval_parse_tl
+ \exp_after:wN \keyval_parse_elt:w \exp_after:wN
+ \q_no_value \l_keyval_sanitise_tl 9 \q_nil 9
+ \exp_after:wN \group_end:
+ \l_keyval_parse_tl
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\keyval_parse_elt:w}
+% Each item to be parsed will have \cs{q_no_value} added to the front.
+% Hence the blank test here can always be used to find a totally
+% empty argument. If this is the case, the system loops round. If there
+% is something to parse, there is a check for the \cs{q_nil} marker
+% and if not a hand-off.
+% \begin{macrocode}
+\cs_new_protected:Npn \keyval_parse_elt:w #1 ,
+ {
+ \tl_if_blank:oTF { \use_none:n #1 }
+ { \keyval_parse_elt:w \q_no_value }
+ {
+ \quark_if_nil:oF { \use_ii:nn #1 }
+ {
+ \keyval_split_key_value:w #1 = = \q_stop
+ \keyval_parse_elt:w \q_no_value
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\keyval_split_key_value:w}
+% \begin{macro}[aux]{\keyval_split_key_value_aux:wTF}
+% The key and value are handled separately. First the key is grabbed and
+% saved as \cs{l_keyval_key_tl}. Then a check is need to see if there is
+% a value at all: if not then the key name is simply added to the output.
+% If there is a value then there is a check to ensure that there was
+% only one |=| in the input (remembering some extra ones are around at
+% the moment to prevent errors). All being well, there is an
+% hand-off to find the value: the \cs{q_nil} is there to prevent loss
+% of braces.
+% \begin{macrocode}
+\cs_new_protected:Npn \keyval_split_key_value:w #1 = #2 \q_stop
+ {
+ \keyval_split_key:w #1 \q_stop
+ \str_if_eq:nnTF {#2} { = }
+ {
+ \tl_put_right:Nx \l_keyval_parse_tl
+ {
+ \exp_not:c
+ { keyval_key_no_value_elt_ \int_use:N \g_keyval_level_int :n }
+ { \exp_not:o \l_keyval_key_tl }
+ }
+ }
+ {
+ \keyval_split_key_value_aux:wTF #2 \q_no_value \q_stop
+ { \keyval_split_value:w \q_nil #2 }
+ { \msg_kernel_error:nn { keyval } { misplaced-equals-sign } }
+ }
+ }
+\cs_new:Npn \keyval_split_key_value_aux:wTF #1 = #2#3 \q_stop
+ { \tl_if_head_eq_meaning:nNTF {#3} \q_no_value }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\keyval_split_key:w}
+% \begin{macro}{\keyval_remove_spaces:w}
+% \begin{macro}[aux]{\keyval_split_key_aux:w}
+% \begin{macro}[aux]{\keyval_remove_spaces_aux:w}
+% The aim here is to remove spaces and also exactly one set of braces.
+% The spaces are trimmed off from each end using a \enquote{funny}
+% |Q|, which will never turn up in normal use. The idea is that
+% the \texttt{f}-type expansion will stop if it finds an unexpandable
+% token or a space, and will gobble the space. To avoid expanding
+% anything else, the \cs{exp_not:N} works by ensuring that the first
+% non-space token in the setting will stop the \texttt{f}-type
+% expansion. The \cs{use_none:n} is needed to remove the leading
+% quark, while the second setting of \cs{l_keyval_key_tl}
+% removes exactly one set of braces.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_math_toggle:n { `\Q }
+ \cs_new_protected:Npn \keyval_split_key:w #1 \q_stop
+ {
+ \exp_args:NNf \tl_set:Nn \l_keyval_key_tl
+ {
+ \exp_after:wN \keyval_remove_spaces:w \exp_after:wN
+ \exp_not:N \use_none:n #1 Q ~ Q
+ }
+ \tl_set:Nx \l_keyval_key_tl
+ { \exp_after:wN \keyval_split_key_aux:w \l_keyval_key_tl \q_stop }
+ }
+ \cs_gset:Npn \keyval_split_key_aux:w #1 \q_stop { \exp_not:n {#1} }
+ \cs_gset:Npn \keyval_remove_spaces:w #1 ~ Q
+ { \keyval_remove_spaces_aux:w #1 Q }
+ \cs_gset:Npn \keyval_remove_spaces_aux:w #1 Q #2 {#1}
+\group_end:
+% \end{macrocode}
+% Fixme: use \cs{tl_trim_spaces} instead of the aux function above?
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\keyval_split_value:w}
+% Here the value has to be separated from the equals signs and the
+% leading \cs{q_nil} added in to keep the brace levels. Fist the
+% processing function can be added to the output list. If there is no
+% value, setting \cs{l_keyval_value_tl} with three groups removed will
+% leave nothing at all, and so an empty group can be added to the
+% parsed list. On the other hand, if the value is entirely contained
+% within a set of braces then \cs{l_keyval_value_tl} will contain
+% \cs{q_nil} only. In that case, strip off the leading quark using
+% \cs{use_ii:nnn}, which also deals with any spaces.
+% \begin{macrocode}
+\cs_new_protected:Npn \keyval_split_value:w #1 = =
+ {
+ \tl_put_right:Nx \l_keyval_parse_tl
+ {
+ \exp_not:c
+ { keyval_key_value_elt_ \int_use:N \g_keyval_level_int :nn }
+ { \exp_not:o \l_keyval_key_tl }
+ }
+ \tl_set:Nx \l_keyval_value_tl
+ { \exp_not:o { \use_none:nnn #1 \q_nil \q_nil } }
+ \tl_if_empty:NTF \l_keyval_value_tl
+ { \tl_put_right:Nn \l_keyval_parse_tl { { } } }
+ {
+ \quark_if_nil:NTF \l_keyval_value_tl
+ {
+ \tl_put_right:Nx \l_keyval_parse_tl
+ { { \exp_not:o { \use_ii:nnn #1 \q_nil } } }
+ }
+ { \keyval_split_value_aux:w #1 \q_stop }
+ }
+ }
+% \end{macrocode}
+% A similar idea to the key code: remove the spaces from each end and
+% deal with one set of braces.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_math_toggle:n { `\Q }
+ \cs_new_protected:Npn \keyval_split_value_aux:w \q_nil #1 \q_stop
+ {
+ \exp_args:NNf \tl_set:Nn \l_keyval_value_tl
+ { \keyval_remove_spaces:w \exp_not:N #1 Q ~ Q }
+ \tl_put_right:Nx \l_keyval_parse_tl
+ { { \exp_not:o \l_keyval_value_tl } }
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\keyval_parse:NNn}
+% The outer parsing routine just sets up the processing functions and
+% hands off.
+% \begin{macrocode}
+\cs_new_protected:Npn \keyval_parse:NNn #1#2#3
+ {
+ \int_gincr:N \g_keyval_level_int
+ \cs_gset_eq:cN
+ { keyval_key_no_value_elt_ \int_use:N \g_keyval_level_int :n } #1
+ \cs_gset_eq:cN
+ { keyval_key_value_elt_ \int_use:N \g_keyval_level_int :nn } #2
+ \keyval_parse:n {#3}
+ \int_gdecr:N \g_keyval_level_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% One message for the low level parsing system.
+% \begin{macrocode}
+\msg_kernel_new:nnnn { keyval } { misplaced-equals-sign }
+ { Misplaced~equals~sign~in~key-value~input~\msg_line_number: }
+ {
+ LaTeX~is~attempting~to~parse~some~key-value~input~but~found~
+ two~equals~signs~not~separated~by~a~comma.
+ }
+% \end{macrocode}
+%
+%
% \subsection{Constants and variables}
%
% \begin{variable}{\c_keys_code_root_tl, \c_keys_vars_root_tl}
@@ -644,6 +1114,13 @@
\tl_new:N \l_keys_property_tl
% \end{macrocode}
% \end{variable}
+%
+% \begin{variable}{\l_keys_unknown_clist}
+% Used when setting only known keys to store those left over.
+% \begin{macrocode}
+\tl_new:N \l_keys_unknown_clist
+% \end{macrocode}
+% \end{variable}
%
% \begin{variable}{\l_keys_value_tl}
% The value given for a key: may be empty if no value was given.
@@ -785,6 +1262,27 @@
}
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[int]{\keys_bool_set_inverse:NN}
+% Inverse boolean setting is much the same.
+% \begin{macrocode}
+\cs_new_nopar:Npn \keys_bool_set_inverse:NN #1#2
+ {
+ \cs_if_exist:NF #1 { \bool_new:N #1 }
+ \keys_choice_make:
+ \keys_cmd_set:nx { \l_keys_path_tl / true }
+ { \exp_not:c { bool_ #2 set_false:N } \exp_not:N #1 }
+ \keys_cmd_set:nx { \l_keys_path_tl / false }
+ { \exp_not:c { bool_ #2 set_true:N } \exp_not:N #1 }
+ \keys_cmd_set:nn { \l_keys_path_tl / unknown }
+ {
+ \msg_kernel_error:nnx { keys } { boolean-values-only }
+ { \l_keys_key_tl }
+ }
+ \keys_default_set:n { true }
+ }
+% \end{macrocode}
+% \end{macro}
%
% \begin{macro}[int]{\keys_choice_make:}
% To make a choice from a key, two steps: set the code, and set the
@@ -802,6 +1300,29 @@
}
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[int]{\keys_choices_make:nn}
+% Auto-generating choices means setting up the root key as a choice, then
+% defining each choice in turn.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_choices_make:nn #1#2
+ {
+ \keys_choice_make:
+ \int_zero:N \l_keys_choice_int
+ \clist_map_inline:nn {#1}
+ {
+ \keys_cmd_set:nx { \l_keys_path_tl / ##1 }
+ {
+ \tl_set:Nn \exp_not:N \l_keys_choice_tl {##1}
+ \int_set:Nn \exp_not:N \l_keys_choice_int
+ { \int_use:N \l_keys_choice_int }
+ \exp_not:n {#2}
+ }
+ \int_incr:N \l_keys_choice_int
+ }
+ }
+% \end{macrocode}
+% \end{macro}
%
% \begin{macro}[int]{\keys_choices_generate:n}
% \begin{macro}[aux]{\keys_choices_generate_aux:n}
@@ -826,8 +1347,8 @@
{
\keys_cmd_set:nx { \l_keys_path_tl / #1 }
{
- \exp_not:n { \tl_set:Nn \l_keys_choice_tl } {#1}
- \exp_not:n { \int_set:Nn \l_keys_choice_int }
+ \tl_set:Nn \exp_not:N \l_keys_choice_tl {#1}
+ \int_set:Nn \exp_not:N \l_keys_choice_int
{ \int_use:N \l_keys_choice_int }
\exp_not:v
{ \c_keys_vars_root_tl \l_keys_path_tl .choice~code }
@@ -895,7 +1416,7 @@
\cs_new_protected_nopar:Npn \keys_meta_make:n #1
{
\exp_args:NNo \keys_cmd_set:nn \l_keys_path_tl
- { \exp_after:wN \keys_set:nn \exp_after:wN { \l_keys_module_tl } {#1} }
+ { \exp_after:wN \keys_set:nn \exp_after:wN { \l_keys_module_tl } {#1} }
}
\cs_new_protected_nopar:Npn \keys_meta_make:x #1
{
@@ -904,6 +1425,47 @@
}
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[int]{\keys_multichoice_find:n}
+% \begin{macro}[int]{\keys_multichoice_make:}
+% \begin{macro}[int]{\keys_multichoices_make:nn}
+% Choices where several values can be selected are very similar to normal
+% exclusive choices. There is just a slight change in implementation to
+% map across a comma-separated list. This then requires that the appropriate
+% set up takes place elsewhere.
+% \begin{macrocode}
+\cs_new_nopar:Npn \keys_multichoice_find:n #1
+ { \clist_map_function:nN {#1} \keys_choice_find:n }
+\cs_new_protected_nopar:Npn \keys_multichoice_make:
+ {
+ \keys_cmd_set:nn { \l_keys_path_tl }
+ { \keys_multichoice_find:n {##1} }
+ \keys_cmd_set:nn { \l_keys_path_tl / unknown }
+ {
+ \msg_kernel_error:nnxx { keys } { choice-unknown }
+ { \l_keys_path_tl } {##1}
+ }
+ }
+\cs_new_protected:Npn \keys_multichoices_make:nn #1#2
+ {
+ \keys_multichoice_make:
+ \int_zero:N \l_keys_choice_int
+ \clist_map_inline:nn {#1}
+ {
+ \keys_cmd_set:nx { \l_keys_path_tl / ##1 }
+ {
+ \tl_set:Nn \exp_not:N \l_keys_choice_tl {##1}
+ \int_set:Nn \exp_not:N \l_keys_choice_int
+ { \int_use:N \l_keys_choice_int }
+ \exp_not:n {#2}
+ }
+ \int_incr:N \l_keys_choice_int
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[int]{\keys_value_requirement:n}
% Values can be required or forbidden by having the appropriate marker
@@ -955,15 +1517,37 @@
% \end{macrocode}
% \end{macro}
% \end{macro}
+%
+% \begin{macro}{.bool_set_inverse:N}
+% \begin{macro}{.bool_gset_inverse:N}
+% One function for this.
+% \begin{macrocode}
+\cs_new_protected_nopar:cpn { \c_keys_props_root_tl .bool_set_inverse:N } #1
+ { \keys_bool_set_inverse:NN #1 { } }
+\cs_new_protected_nopar:cpn { \c_keys_props_root_tl .bool_gset_inverse:N } #1
+ { \keys_bool_set_inverse:NN #1 g }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}{.choice:}
% Making a choice is handled internally, as it is also needed by
-% \texttt{.generate_choices:n}.
+% \texttt{.generate_choices:n}.
% \begin{macrocode}
\cs_new_protected_nopar:cpn { \c_keys_props_root_tl .choice: }
{ \keys_choice_make: }
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}{.choices:nn}
+% For auto-generation of a series of mutually-exclusive choices.
+% Here, |#1| will consist of two separate
+% arguments, hence the slightly odd-looking implementation.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_keys_props_root_tl .choices:nn } #1
+ { \keys_choices_make:nn #1 }
+% \end{macrocode}
+% \end{macro}
%
% \begin{macro}{.code:n, .code:x}
% Creating code is simply a case of passing through to the underlying
@@ -1062,6 +1646,19 @@
{ \keys_meta_make:x {#1} }
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}{.multichoice:}
+% \begin{macro}{.multichoices:nn}
+% The same idea as \texttt{.choice:} and \texttt{.choices:nn}, but
+% where more than one choice is allowed.
+% \begin{macrocode}
+\cs_new_protected_nopar:cpn { \c_keys_props_root_tl .multichoice: }
+ { \keys_multichoice_make: }
+\cs_new_protected:cpn { \c_keys_props_root_tl .multichoices:nn } #1
+ { \keys_multichoices_make:nn #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}{.skip_set:N, .skip_set:c}
% \begin{macro}{.skip_gset:N, .skip_gset:c}
@@ -1139,6 +1736,31 @@
% \end{macrocode}
% \end{macro}
% \end{macro}
+%
+% \begin{macro}
+% {
+% \keys_set_known:nnN, \keys_set_known:nVN,
+% \keys_set_known:nvN, \keys_set_known:noN
+% }
+% \begin{macro}[aux]{\keys_set_known_aux:nnnN, \keys_set_known_aux:onnN}
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_set_known:nnN
+ { \keys_set_known_aux:onnN { \l_keys_module_tl } }
+\cs_new_protected:Npn \keys_set_known_aux:nnnN #1#2#3#4
+ {
+ \tl_set:Nn \l_keys_module_tl {#2}
+ \clist_clear:N \l_keys_unknown_clist
+ \cs_set_eq:NN \keys_execute_unknown: \keys_execute_unknown_alt:
+ \keyval_parse:NNn \keys_set_elt:n \keys_set_elt:nn {#3}
+ \cs_set_eq:NN \keys_execute_unknown: \keys_execute_unknown_std:
+ \tl_set:Nn \l_keys_module_tl {#1}
+ \clist_set_eq:NN #4 \l_keys_unknown_clist
+ }
+\cs_generate_variant:Nn \keys_set_known:nnN { nV , nv , no }
+\cs_generate_variant:Nn \keys_set_known_aux:nnnN { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[int]{\keys_set_elt:n, \keys_set_elt:nn}
% \begin{macro}[aux]{\keys_set_elt_aux:nn}
@@ -1199,8 +1821,11 @@
{
\quark_if_no_value:cF { \c_keys_vars_root_tl \l_keys_path_tl .default }
{
- \tl_set_eq:Nc \l_keys_value_tl
- { \c_keys_vars_root_tl \l_keys_path_tl .default }
+ \cs_if_exist:cT { \c_keys_vars_root_tl \l_keys_path_tl .default }
+ {
+ \tl_set_eq:Nc \l_keys_value_tl
+ { \c_keys_vars_root_tl \l_keys_path_tl .default }
+ }
}
}
}
@@ -1222,7 +1847,12 @@
% \end{macro}
%
% \begin{macro}[int]{\keys_execute:}
-% \begin{macro}[aux]{\keys_execute_unknown:}
+% \begin{macro}[aux]
+% {
+% \keys_execute_unknown:,
+% \keys_execute_unknown_std:,
+% \keys_execute_unknown_alt:
+% }
% \begin{macro}[aux]{\keys_execute:nn}
% Actually executing a key is done in two parts. First, look for the
% key itself, then look for the \texttt{unknown} key with the same
@@ -1238,6 +1868,16 @@
{ \l_keys_path_tl } { \l_keys_module_tl }
}
}
+\cs_new_eq:NN \keys_execute_unknown_std: \keys_execute_unknown:
+\cs_new_nopar:Npn \keys_execute_unknown_alt:
+ {
+ \clist_put_right:Nx \l_keys_unknown_clist
+ {
+ \exp_not:o \l_keys_key_tl
+ \bool_if:NF \l_keys_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
\cs_new_nopar:Npn \keys_execute:nn #1#2
{
\cs_if_exist:cTF { \c_keys_code_root_tl #1 }
@@ -1252,7 +1892,7 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\keys_choice_find:n}
+% \begin{macro}[int]{\keys_choice_find:n}
% Executing a choice has two parts. First, try the choice given, then
% if that fails call the unknown key. That will exist, as it is created
% when a choice is first made. So there is no need for any escape code.
@@ -1267,14 +1907,26 @@
%
% \subsection{Utilities}
%
-% \begin{macro}[pTF]{\keys_if_exist:nn}
+% \begin{macro}[EXP,pTF]{\keys_if_exist:nn}
% A utility for others to see if a key exists.
% \begin{macrocode}
\prg_new_conditional:Npnn \keys_if_exist:nn #1#2 { p , T , F , TF }
{
\cs_if_exist:cTF { \c_keys_code_root_tl #1 / #2 }
- { \prg_return_true:}
- { \prg_return_false:}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\keys_if_choice_exist:nnn}
+% Just an alternative view on \cs{keys_if_exist:nn(TF)}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \keys_if_choice_exist:nnn #1#2#3 { p , T , F , TF }
+ {
+ \cs_if_exist:cTF { \c_keys_code_root_tl #1 / #2 / #3 }
+ { \prg_return_true: }
+ { \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
@@ -1356,6 +2008,23 @@
}
% \end{macrocode}
%
+% \subsection{Deprecated functions}
+%
+% Deprecated on 2011-05-27, for removal by 2011-08-31.
+%
+% \begin{macro}{\KV_process_space_removal_sanitize:NNn}
+% \begin{macro}{\KV_process_space_removal_no_sanitize:NNn}
+% \begin{macro}{\KV_process_no_space_removal_no_sanitize:NNn}
+% There is just one function for this now.
+% \begin{macrocode}
+\cs_new_eq:NN \KV_process_space_removal_sanitize:NNn \keyval_parse:NNn
+\cs_new_eq:NN \KV_process_space_removal_no_sanitize:NNn \keyval_parse:NNn
+\cs_new_eq:NN \KV_process_no_space_removal_no_sanitize:NNn \keyval_parse:NNn
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \begin{macrocode}
%</initex|package>
% \end{macrocode}