diff options
author | Karl Berry <karl@freefriends.org> | 2011-08-29 00:08:58 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2011-08-29 00:08:58 +0000 |
commit | 62ea3c38421c330e364e1b53c366715efe3c7554 (patch) | |
tree | f323abbf0bd2346b481fc35187c6ec2d50ce2d1a /Master/texmf-dist/source/latex/l3kernel/l3keys.dtx | |
parent | 0a90bbc7d05e8e767bb8992e0ed6180206ae2bc7 (diff) |
l3kernel 2678 (28aug11)
git-svn-id: svn://tug.org/texlive/trunk@23738 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3keys.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3keys.dtx | 729 |
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} |