% \iffalse meta-comment % %% File: l3keys.dtx Copyright (C) 2006-2013 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 %% %% http://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 released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX3 Project. %% %% ----------------------------------------------------------------------- % %<*driver|package> \RequirePackage{l3bootstrap} \GetIdInfo$Id: l3keys.dtx 4461 2013-02-24 18:55:17Z joseph $ {L3 Experimental key-value interfaces} % %<*driver> \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3keys} package\\ Key--value interfaces^^A % \thanks{This file describes v\ExplFileVersion, % last revised \ExplFileDate.}^^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 \ExplFileDate} % % \maketitle % % \begin{documentation} % % The key--value method is a popular system for creating large numbers % of settings for controlling function or package behaviour. For the % user, the system normally results in input of the form % \begin{verbatim} % \PackageControlMacro{ % key-one = value one, % key-two = value two % } % \end{verbatim} % or % \begin{verbatim} % \PackageMacro[ % key-one = value one, % key-two = value two % ]{argument}. % \end{verbatim} % % The high level functions here are intended as a method to create % key--value controls. Keys are themselves created using a key--value % interface, minimising the number of functions and arguments % required. Each key is created by setting one or more \emph{properties} % of the key: % \begin{verbatim} % \keys_define:nn { module } % { % key-one .code:n = code including parameter #1, % key-two .tl_set:N = \l_module_store_tl % } % \end{verbatim} % These values can then be set as with other key--value approaches: % \begin{verbatim} % \keys_set:nn { module } % { % key-one = value one, % key-two = value two % } % \end{verbatim} % % At a document level, \cs{keys_set:nn} will be used within a % document function, for example % \begin{verbatim} % \DeclareDocumentCommand \SomePackageSetup { m } % { \keys_set:nn { module } { #1 } } % \DeclareDocumentCommand \SomePackageMacro { o m } % { % \group_begin: % \keys_set:nn { module } { #1 } % % Main code for \SomePackageMacro % \group_end: % } % \end{verbatim} % % Key names may contain any tokens, as they are handled internally % using \cs{tl_to_str:n}. As will be discussed in % section~\ref{sec:subdivision}, it is suggested that the character % |/| is reserved for sub-division of keys into logical % groups. Functions and variables are \emph{not} expanded when creating % key names, and so % \begin{verbatim} % \tl_set:Nn \l_module_tmp_tl { key } % \keys_define:nn { module } % { % \l_module_tmp_tl .code:n = code % } % \end{verbatim} % will create a key called \cs{l_module_tmp_tl}, and not one called % \texttt{key}. % % \section{Creating keys} % % \begin{function}{\keys_define:nn} % \begin{syntax} % \cs{keys_define:nn} \Arg{module} \Arg{keyval list} % \end{syntax} % Parses the \meta{keyval list} and defines the keys listed there for % \meta{module}. The \meta{module} name should be a text value, but % there are no restrictions on the nature of the text. In practice the % \meta{module} should be chosen to be unique to the module in question % (unless deliberately adding keys to an existing module). % % The \meta{keyval list} should consist of one or more key names along % with an associated key \emph{property}. The properties of a key % determine how it acts. The individual properties are described % in the following text; a typical use of \cs{keys_define:nn} might % read % \begin{verbatim} % \keys_define:nn { mymodule } % { % keyname .code:n = Some~code~using~#1, % keyname .value_required: % } % \end{verbatim} % where the properties of the key begin from the |.| after the key % name. % \end{function} % % The various properties available take either no arguments at % all, or require exactly one argument. This is indicated in the % name of the property using an argument specification. In the following % discussion, each property is illustrated attached to an % arbitrary \meta{key}, which when used may be supplied with a % \meta{value}. All key \emph{definitions} are local. % % \begin{function}{.bool_set:N, .bool_gset:N} % \begin{syntax} % \meta{key} .bool_set:N = \meta{boolean} % \end{syntax} % Defines \meta{key} to set \meta{boolean} to \meta{value} (which % must be either \texttt{true} or \texttt{false}). If the variable % does not exist, it will be created at the point that the key is % set up. % \end{function} % % \begin{function}[added = 2011-08-28] % {.bool_set_inverse:N, .bool_gset_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. % \end{function} % % \begin{function}{.choice:} % \begin{syntax} % \meta{key} .choice: % \end{syntax} % 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}[added = 2011-08-21]{.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~$1$). % Choices are discussed in detail in section~\ref{sec:choice}. % \end{function} % % \begin{function}{.choice_code:n, .choice_code:x} % \begin{syntax} % \meta{key} .choice_code:n = \meta{code} % \end{syntax} % Stores \meta{code} for use when \texttt{.generate_choices:n} creates % one or more choice sub-keys of the current key. Inside \meta{code}, % \cs{l_keys_choice_tl} will expand to the name of the choice made, and % \cs{l_keys_choice_int} will be the position of the choice in the list % given to \texttt{.generate_choices:n}. Choices are discussed in % detail in section~\ref{sec:choice}. % \end{function} % % \begin{function}[added=2011/09/11] % {.clist_set:N, .clist_set:c, .clist_gset:N, .clist_gset:c} % \begin{syntax} % \meta{key} .clist_set:N = \meta{comma list variable} % \end{syntax} % Defines \meta{key} to set \meta{comma list variable} to \meta{value}. % Spaces around commas and empty items will be stripped. % If the variable does not exist, it % will be created at the point that the key is set up. % \end{function} % % \begin{function}{.code:n, .code:x} % \begin{syntax} % \meta{key} .code:n = \meta{code} % \end{syntax} % Stores the \meta{code} for execution when \meta{key} is used. The % The \meta{code} can include one parameter ("#1"), which will be the % \meta{value} given for the \meta{key}. The \texttt{x}-type variant % will expand \meta{code} at the point where the \meta{key} is % created. % \end{function} % % \begin{function}{.default:n, .default:V} % \begin{syntax} % \meta{key} .default:n = \meta{default} % \end{syntax} % Creates a \meta{default} value for \meta{key}, which is used if no % value is given. This will be used if only the key name is given, % but not if a blank \meta{value} is given: % \begin{verbatim} % \keys_define:nn { module } % { % key .code:n = Hello~#1, % key .default:n = World % } % \keys_set:nn { module } % { % key = Fred, % Prints 'Hello Fred' % key, % Prints 'Hello World' % key = , % Prints 'Hello ' % } % \end{verbatim} % \end{function} % % \begin{function}{.dim_set:N, .dim_set:c, .dim_gset:N, .dim_gset:c} % \begin{syntax} % \meta{key} .dim_set:N = \meta{dimension} % \end{syntax} % Defines \meta{key} to set \meta{dimension} to \meta{value} (which % must a dimension expression). If the variable does not exist, it % will be created at the point that the key is set up. % \end{function} % % \begin{function}{.fp_set:N, .fp_set:c, .fp_gset:N, .fp_gset:c} % \begin{syntax} % \meta{key} .fp_set:N = \meta{floating point} % \end{syntax} % Defines \meta{key} to set \meta{floating point} to \meta{value} % (which must a floating point number). If the variable does not exist, % it will be created at the point that the key is set up. % \end{function} % % \begin{function}{.generate_choices:n} % \begin{syntax} % \meta{key} .generate_choices:n = \Arg{list} % \end{syntax} % This property will mark \meta{key} as a multiple choice key, % and will use the \meta{list} to define the choices. The \meta{list} % should consist of a comma-separated list of choice names. Each % choice will be set up to execute \meta{code} as set using % \texttt{.choice_code:n} (or \texttt{.choice_code:x}). Choices are % discussed in detail in section~\ref{sec:choice}. % \end{function} % % \begin{function}[added = 2012-06-02]{.initial:n, .initial:V} % \begin{syntax} % \meta{key} .initial:n = \meta{value} % \end{syntax} % Initialises the \meta{key} with the \meta{value}, equivalent to % \begin{quote} % \cs{keys_set:nn} \marg{module} \{ \meta{key} = \meta{value} \} % \end{quote} % \end{function} % % \begin{function}{.int_set:N, .int_set:c, .int_gset:N, .int_gset:c} % \begin{syntax} % \meta{key} .int_set:N = \meta{integer} % \end{syntax} % Defines \meta{key} to set \meta{integer} to \meta{value} (which % must be an integer expression). If the variable does not exist, it % will be created at the point that the key is set up. % \end{function} % % \begin{function}{.meta:n, .meta:x} % \begin{syntax} % \meta{key} .meta:n = \Arg{keyval list} % \end{syntax} % Makes \meta{key} a meta-key, which will set \meta{keyval list} in % one go. If \meta{key} is given with a value at the time the key % is used, then the value will be passed through to the subsidiary % \meta{keys} for processing (as |#1|). % \end{function} % % \begin{function}[added = 2011-08-21]{.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}[added = 2011-08-21]{.multichoices:nn} % \begin{syntax} % \meta{key} .multichoices: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~$1$). % 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, .skip_gset:N, .skip_gset:c} % \begin{syntax} % \meta{key} .skip_set:N = \meta{skip} % \end{syntax} % Defines \meta{key} to set \meta{skip} to \meta{value} (which % must be a skip expression). If the variable does not exist, it % will be created at the point that the key is set up. % \end{function} % % \begin{function}{.tl_set:N, .tl_set:c, .tl_gset:N, .tl_gset:c} % \begin{syntax} % \meta{key} .tl_set:N = \meta{token list variable} % \end{syntax} % Defines \meta{key} to set \meta{token list variable} to \meta{value}. % If the variable does not exist, it will be created at the point that % the key is set up. % \end{function} % % \begin{function}{.tl_set_x:N, .tl_set_x:c, .tl_gset_x:N, .tl_gset_x:c} % \begin{syntax} % \meta{key} .tl_set_x:N = \meta{token list variable} % \end{syntax} % Defines \meta{key} to set \meta{token list variable} to \meta{value}, % which will be subjected to an \texttt{x}-type expansion % (\emph{i.e.}~using \cs{tl_set:Nx}). If the variable does not exist, % it will be created at the point that the key is set up. % \end{function} % % \begin{function}{.value_forbidden:} % \begin{syntax} % \meta{key} .value_forbidden: % \end{syntax} % Specifies that \meta{key} cannot receive a \meta{value} when used. % If a \meta{value} is given then an error will be issued. % \end{function} % % \begin{function}{.value_required:} % \begin{syntax} % \meta{key} .value_required: % \end{syntax} % Specifies that \meta{key} must receive a \meta{value} when used. % If a \meta{value} is not given then an error will be issued. % \end{function} % % \section{Sub-dividing keys} % \label{sec:subdivision} % % When creating large numbers of keys, it may be desirable to divide % them into several sub-groups for a given module. This can be achieved % either by adding a sub-division to the module name: % \begin{verbatim} % \keys_define:nn { module / subgroup } % { key .code:n = code } % \end{verbatim} % or to the key name: % \begin{verbatim} % \keys_define:nn { module } % { subgroup / key .code:n = code } % \end{verbatim} % As illustrated, the best choice of token for sub-dividing keys in % this way is |/|. This is because of the method that is % used to represent keys internally. Both of the above code fragments % set the same key, which has full name \texttt{module/subgroup/key}. % % As will be illustrated in the next section, this subdivision is % particularly relevant to making multiple choices. % % \section{Choice and multiple choice keys} % \label{sec: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 } % { key .choice: } % \end{verbatim} % For keys which are set up as choices, the valid choices are generated % by creating sub-keys of the choice key. This can be carried out in % two ways. % % In many cases, choices execute similar code which is dependant only % on the name of the choice or the position of the choice in the % list of choices. Here, the keys can share the same code, and can % be rapidly created using the \texttt{.choice_code:n} and % \texttt{.generate_choices:n} properties: % \begin{verbatim} % \keys_define:nn { module } % { % key .choice_code:n = % { % 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. % }, % key .generate_choices:n = % { choice-a, choice-b, choice-c } % } % \end{verbatim} % Following common computing practice, \cs{l_keys_choice_int} is % indexed from~$1$. % % 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:} 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$. % \end{variable} % % On the other hand, it is sometimes useful to create choices which % use entirely different code from one another. This can be achieved % by setting the \texttt{.choice:} property of a key, then manually % defining sub-keys. % \begin{verbatim} % \keys_define:nn { module } % { % key .choice:, % key / choice-a .code:n = code-a, % key / choice-b .code:n = code-b, % key / choice-c .code:n = code-c, % } %\end{verbatim} % % It is possible to mix the two methods, but manually-created choices % should \emph{not} use \cs{l_keys_choice_tl} or \cs{l_keys_choice_int}. % 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} % % \begin{function} % {\keys_set:nn, \keys_set:nV, \keys_set:nv, \keys_set:no} % \begin{syntax} % \cs{keys_set:nn} \Arg{module} \Arg{keyval list} % \end{syntax} % 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. % \end{function} % % If a key is not known, \cs{keys_set:nn} will look for a special % \texttt{unknown} key for the same module. This mechanism can be % used to create new keys from user input. % \begin{verbatim} % \keys_define:nn { module } % { % unknown .code:n = % You~tried~to~set~key~'\l_keys_key_tl'~to~'#1'. % } %\end{verbatim} % % \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}. % \end{variable} % % \begin{variable}{\l_keys_path_tl} % When processing an unknown key, the path of the key used is available % 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} % % \begin{function}[added = 2011-08-23] % { % \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_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}. % \end{function} % % \begin{function}[added = 2011-08-21,EXP,pTF]{\keys_if_choice_exist:nnn} % \begin{syntax} % \cs{keys_if_choice_exist_p:nnn} \meta{module} \meta{key} \meta{choice} \\ % \cs{keys_if_choice_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} % \begin{syntax} % \cs{keys_show:nn} \Arg{module} \Arg{key} % \end{syntax} % Shows the function which is used to actually implement a % \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}[updated = 2011-09-08]{\keyval_parse:NNn} % \begin{syntax} % \cs{keyval_parse:NNn} \meta{function_1} \meta{function_2} \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{function_1} should take one argument, while % \meta{function_2} should absorb two arguments. After % \cs{keyval_parse:NNn} has parsed the \meta{key--value list}, % \meta{function_1} will be used to process keys given with no value % and \meta{function_2} 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). Spaces are trimmed from the ends of the \meta{key} and \meta{value}, % and any \emph{outer} set of braces are removed from the \meta{value} as % part of the processing. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3keys} Implementation} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<*package> \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} \__expl_package_check: % % \end{macrocode} % % \subsection{Low-level interface} % % \begin{macrocode} %<@@=keyval> % \end{macrocode} % % For historical reasons this code uses the `keyval' module prefix. % % \begin{variable}{\g_@@_level_int} % For nesting purposes an integer is needed for the current level. % \begin{macrocode} \int_new:N \g_@@_level_int % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_key_tl, \l_@@_value_tl} % The current key name and value. % \begin{macrocode} \tl_new:N \l_@@_key_tl \tl_new:N \l_@@_value_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_sanitise_tl} % \begin{variable}{\l_@@_parse_tl} % Token list variables for dealing with awkward category codes in the % input. % \begin{macrocode} \tl_new:N \l_@@_sanitise_tl \tl_new:N \l_@@_parse_tl % \end{macrocode} %\end{variable} %\end{variable} % % \begin{macro}{\@@_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 \@@_parse:n #1 { \group_begin: \tl_clear:N \l_@@_sanitise_tl \tl_set:Nn \l_@@_sanitise_tl {#1} \tl_replace_all:Nnn \l_@@_sanitise_tl { = } { 8 } \tl_replace_all:Nnn \l_@@_sanitise_tl { , } { 9 } \tl_clear:N \l_@@_parse_tl \exp_after:wN \@@_parse_elt:w \exp_after:wN \q_nil \l_@@_sanitise_tl 9 \q_recursion_tail 9 \q_recursion_stop \exp_after:wN \group_end: \l_@@_parse_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_elt:w} % Each item to be parsed will have \cs{q_nil} 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 end of the input before % handing off. % \begin{macrocode} \cs_new_protected:Npn \@@_parse_elt:w #1 , { \tl_if_blank:oTF { \use_none:n #1 } { \@@_parse_elt:w \q_nil } { \quark_if_recursion_tail_stop:o { \use_ii:nn #1 } \@@_split_key_value:w #1 = = \q_stop \@@_parse_elt:w \q_nil } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_split_key_value:w} % \begin{macro}[aux]{\@@_split_key_value:wTF} % The key and value are handled separately. First the key is grabbed and % saved as \cs{l_@@_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 \@@_split_key_value:w #1 = #2 \q_stop { \@@_split_key:n {#1} \str_if_eq:nnTF {#2} { = } { \tl_put_right:Nx \l_@@_parse_tl { \exp_not:c { @@_key_no_value_elt_ \int_use:N \g_@@_level_int :n } { \exp_not:o \l_@@_key_tl } } } { \@@_split_key_value:wTF #2 \q_no_value \q_stop { \@@_split_value:w \q_nil #2 } { \__msg_kernel_error:nn { kernel } { misplaced-equals-sign } } } } \cs_new:Npn \@@_split_key_value:wTF #1 = #2#3 \q_stop { \tl_if_head_eq_meaning:nNTF {#3} \q_no_value } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_split_key:n} % \begin{macro}[aux]{\@@_split_key:w} % There are two possible cases here. The first case is that |#1| is % surrounded by braces, in which case the |\use_none:nnn #1 \q_nil \q_nil| % will yield \cs{q_nil}. There, we can remove the leading \cs{q_nil}, the % braces and any spaces around the outside with \cs{use_ii:nnn}. On the % other hand, if there are no braces then the second branch removes the % leading \cs{q_nil} and any surrounding spaces. (This code does not have % to cover the case with no key, as that's already taken out above.) % \begin{macrocode} \cs_new_protected:Npn \@@_split_key:n #1 { \quark_if_nil:oTF { \use_none:nnn #1 \q_nil \q_nil } { \tl_set:Nx \l_@@_key_tl { \exp_not:o { \use_ii:nnn #1 \q_nil } } } { \@@_split_key:w #1 \q_stop } } \cs_new_protected:Npn \@@_split_key:w \q_nil #1 \q_stop { \tl_set:Nx \l_@@_key_tl { \tl_trim_spaces:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_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_@@_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_@@_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 \@@_split_value:w #1 = = { \tl_put_right:Nx \l_@@_parse_tl { \exp_not:c { @@_key_value_elt_ \int_use:N \g_@@_level_int :nn } { \exp_not:o \l_@@_key_tl } } \tl_set:Nx \l_@@_value_tl { \exp_not:o { \use_none:nnn #1 \q_nil \q_nil } } \tl_if_empty:NTF \l_@@_value_tl { \tl_put_right:Nn \l_@@_parse_tl { { } } } { \quark_if_nil:NTF \l_@@_value_tl { \tl_put_right:Nx \l_@@_parse_tl { { \exp_not:o { \use_ii:nnn #1 \q_nil } } } } { \@@_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} \cs_new_protected:Npn \@@_split_value_aux:w \q_nil #1 \q_stop { \tl_set:Nx \l_@@_value_tl { \tl_trim_spaces:n {#1} } \tl_put_right:Nx \l_@@_parse_tl { { \exp_not:o \l_@@_value_tl } } } % \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_@@_level_int \cs_gset_eq:cN { @@_key_no_value_elt_ \int_use:N \g_@@_level_int :n } #1 \cs_gset_eq:cN { @@_key_value_elt_ \int_use:N \g_@@_level_int :nn } #2 \@@_parse:n {#3} \int_gdecr:N \g_@@_level_int } % \end{macrocode} % \end{macro} % % One message for the low level parsing system. % \begin{macrocode} \__msg_kernel_new:nnnn { kernel } { 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{macrocode} %<@@=keys> % \end{macrocode} % % \begin{variable}{\c_@@_code_root_tl, \c_@@_vars_root_tl} % The prefixes for the code and variables of the keys themselves. % \begin{macrocode} \tl_const:Nn \c_@@_code_root_tl { key~code~>~ } \tl_const:Nn \c_@@_vars_root_tl { key~var~>~ } % \end{macrocode} % \end{variable} % % \begin{variable}{\c_@@_props_root_tl} % The prefix for storing properties. % \begin{macrocode} \tl_const:Nn \c_@@_props_root_tl { key~prop~>~ } % \end{macrocode} % \end{variable} % % \begin{variable}{\c_@@_value_forbidden_tl, \c_@@_value_required_tl} % Two marker token lists. % \begin{macrocode} \tl_const:Nn \c_@@_value_forbidden_tl { forbidden } \tl_const:Nn \c_@@_value_required_tl { required } % \end{macrocode} % \end{variable} % % \begin{variable}{\l_keys_choice_int, \l_keys_choice_tl} % Publicly accessible data on which choice is being used when several % are generated as a set. % \begin{macrocode} \int_new:N \l_keys_choice_int \tl_new:N \l_keys_choice_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_keys_key_tl} % The name of a key itself: needed when setting keys. % \begin{macrocode} \tl_new:N \l_keys_key_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_module_tl} % The module for an entire set of keys. % \begin{macrocode} \tl_new:N \l_@@_module_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_no_value_bool} % A marker is needed internally to show if only a key or a key plus a % value was seen: this is recorded here. % \begin{macrocode} \bool_new:N \l_@@_no_value_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_keys_path_tl} % The \enquote{path} of the current key is stored here: this is % available to the programmer and so is public. % \begin{macrocode} \tl_new:N \l_keys_path_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_property_tl} % The \enquote{property} begin set for a key at definition time is % stored here. % \begin{macrocode} \tl_new:N \l_@@_property_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_unknown_clist} % Used when setting only known keys to store those left over. % \begin{macrocode} \tl_new:N \l_@@_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. % \begin{macrocode} \tl_new:N \l_keys_value_tl % \end{macrocode} % \end{variable} % % \subsection{The key defining mechanism} % % \begin{macro}{\keys_define:nn} % \begin{macro}[aux]{\@@_define:nnn, \@@_define:onn} % The public function for definitions is just a wrapper for the lower % level mechanism, more or less. The outer function is designed to % keep a track of the current module, to allow safe nesting. The module is set % removing any leading |/| (which is not needed here). % \begin{macrocode} \cs_new_protected:Npn \keys_define:nn { \@@_define:onn \l_@@_module_tl } \cs_new_protected:Npn \@@_define:nnn #1#2#3 { \tl_set:Nx \l_@@_module_tl { \tl_to_str:n {#2} } \keyval_parse:NNn \@@_define_elt:n \@@_define_elt:nn {#3} \tl_set:Nn \l_@@_module_tl {#1} } \cs_generate_variant:Nn \@@_define:nnn { o } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_define_elt:n} % \begin{macro}[int]{\@@_define_elt:nn} % \begin{macro}[aux]{\@@_define_elt_aux:nn} % The outer functions here record whether a value was given and then % converge on a common internal mechanism. There is first a search for % a property in the current key name, then a check to make sure it is % known before the code hands off to the next step. % \begin{macrocode} \cs_new_protected:Npn \@@_define_elt:n #1 { \bool_set_true:N \l_@@_no_value_bool \@@_define_elt_aux:nn {#1} { } } \cs_new_protected:Npn \@@_define_elt:nn #1#2 { \bool_set_false:N \l_@@_no_value_bool \@@_define_elt_aux:nn {#1} {#2} } \cs_new_protected:Npn \@@_define_elt_aux:nn #1#2 { \@@_property_find:n {#1} \cs_if_exist:cTF { \c_@@_props_root_tl \l_@@_property_tl } { \@@_define_key:n {#2} } { \__msg_kernel_error:nnxx { kernel } { property-unknown } { \l_@@_property_tl } { \l_keys_path_tl } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_property_find:n} % \begin{macro}[aux]{\@@_property_find:w} % Searching for a property means finding the last |.| in the input, % and storing the text before and after it. Everything is turned into % strings, so there is no problem using an \texttt{x}-type expansion. % \begin{macrocode} \cs_new_protected:Npn \@@_property_find:n #1 { \tl_set:Nx \l_keys_path_tl { \l_@@_module_tl / } \tl_if_in:nnTF {#1} { . } { \@@_property_find:w #1 \q_stop } { \__msg_kernel_error:nnx { kernel } { key-no-property } {#1} } } \cs_new_protected:Npn \@@_property_find:w #1 . #2 \q_stop { \tl_set:Nx \l_keys_path_tl { \l_keys_path_tl \tl_to_str:n {#1} } \tl_if_in:nnTF {#2} { . } { \tl_set:Nx \l_keys_path_tl { \l_keys_path_tl . } \@@_property_find:w #2 \q_stop } { \tl_set:Nn \l_@@_property_tl { . #2 } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_define_key:n} % \begin{macro}[aux]{\@@_define_key:w} % Two possible cases. If there is a value for the key, then just use % the function. If not, then a check to make sure there is no need for % a value with the property. If there should be one then complain, % otherwise execute it. There is no need to check for a |:| as if it % is missing the earlier tests will have failed. % \begin{macrocode} \cs_new_protected:Npn \@@_define_key:n #1 { \bool_if:NTF \l_@@_no_value_bool { \exp_after:wN \@@_define_key:w \l_@@_property_tl \q_stop { \use:c { \c_@@_props_root_tl \l_@@_property_tl } } { \__msg_kernel_error:nnxx { kernel } { property-requires-value } { \l_@@_property_tl } { \l_keys_path_tl } } } { \use:c { \c_@@_props_root_tl \l_@@_property_tl } {#1} } } \cs_new_protected:Npn \@@_define_key:w #1 : #2 \q_stop { \tl_if_empty:nTF {#2} } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Turning properties into actions} % % \begin{macro}[int]{\@@_bool_set:NN} % Boolean keys are really just choices, but all done by hand. The % second argument here is the scope: either empty or \texttt{g} for % global. % \begin{macrocode} \cs_new:Npn \@@_bool_set:NN #1#2 { \bool_if_exist:NF #1 { \bool_new:N #1 } \@@_choice_make: \@@_cmd_set:nx { \l_keys_path_tl / true } { \exp_not:c { bool_ #2 set_true:N } \exp_not:N #1 } \@@_cmd_set:nx { \l_keys_path_tl / false } { \exp_not:c { bool_ #2 set_false:N } \exp_not:N #1 } \@@_cmd_set:nn { \l_keys_path_tl / unknown } { \__msg_kernel_error:nnx { kernel } { boolean-values-only } { \l_keys_key_tl } } \@@_default_set:n { true } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_bool_set_inverse:NN} % Inverse boolean setting is much the same. % \begin{macrocode} \cs_new:Npn \@@_bool_set_inverse:NN #1#2 { \bool_if_exist:NF #1 { \bool_new:N #1 } \@@_choice_make: \@@_cmd_set:nx { \l_keys_path_tl / true } { \exp_not:c { bool_ #2 set_false:N } \exp_not:N #1 } \@@_cmd_set:nx { \l_keys_path_tl / false } { \exp_not:c { bool_ #2 set_true:N } \exp_not:N #1 } \@@_cmd_set:nn { \l_keys_path_tl / unknown } { \__msg_kernel_error:nnx { kernel } { boolean-values-only } { \l_keys_key_tl } } \@@_default_set:n { true } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_choice_make:} % To make a choice from a key, two steps: set the code, and set the % unknown key. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_choice_make: { \@@_cmd_set:nn { \l_keys_path_tl } { \@@_choice_find:n {##1} } \@@_cmd_set:nn { \l_keys_path_tl / unknown } { \__msg_kernel_error:nnxx { kernel } { key-choice-unknown } { \l_keys_path_tl } {##1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_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 \@@_choices_make:nn #1#2 { \@@_choice_make: \int_zero:N \l_keys_choice_int \clist_map_inline:nn {#1} { \int_incr:N \l_keys_choice_int \@@_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} } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_choices_generate:n} % \begin{macro}[aux]{\@@_choices_generate_aux:n} % Creating multiple-choices means setting up the \enquote{indicator} % code, then applying whatever the user wanted. % \begin{macrocode} \cs_new_protected:Npn \@@_choices_generate:n #1 { \cs_if_exist:cTF { \c_@@_vars_root_tl \l_keys_path_tl .choice~code } { \@@_choice_make: \int_zero:N \l_keys_choice_int \clist_map_function:nN {#1} \@@_choices_generate_aux:n } { \__msg_kernel_error:nnx { kernel } { generate-choices-before-code } { \l_keys_path_tl } } } \cs_new_protected:Npn \@@_choices_generate_aux:n #1 { \int_incr:N \l_keys_choice_int \@@_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:v { \c_@@_vars_root_tl \l_keys_path_tl .choice~code } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_choice_code_store:n, \@@_choice_code_store:x} % The code for making multiple choices is stored in a token list. % \begin{macrocode} \cs_new_protected:Npn \@@_choice_code_store:n #1 { \cs_if_exist:cF { \c_@@_vars_root_tl \l_keys_path_tl .choice~code } { \tl_new:c { \c_@@_vars_root_tl \l_keys_path_tl .choice~code } } \tl_set:cn { \c_@@_vars_root_tl \l_keys_path_tl .choice~code } {#1} } \cs_generate_variant:Nn \@@_choice_code_store:n { x } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_cmd_set:nn, \@@_cmd_set:nx, \@@_cmd_set:Vo} % \begin{macro}[aux]{\@@_cmd_set:n} % Creating a new command means tidying up the properties and then making % the internal function which actually does the work. % \begin{macrocode} \cs_new_protected:Npn \@@_cmd_set:nn #1#2 { \@@_cmd_set:n {#1} \cs_set:cpn { \c_@@_code_root_tl #1 } ##1 {#2} } \cs_new_protected:Npn \@@_cmd_set:nx #1#2 { \@@_cmd_set:n {#1} \cs_set:cpx { \c_@@_code_root_tl #1 } ##1 {#2} } \cs_generate_variant:Nn \@@_cmd_set:nn { Vo } \cs_new_protected:Npn \@@_cmd_set:n #1 { \tl_clear_new:c { \c_@@_vars_root_tl #1 .default } \tl_set:cn { \c_@@_vars_root_tl #1 .default } { \q_no_value } \tl_clear_new:c { \c_@@_vars_root_tl #1 .req } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_default_set:n, \@@_default_set:V} % Setting a default value is easy. % \begin{macrocode} \cs_new_protected:Npn \@@_default_set:n #1 { \tl_set:cn { \c_@@_vars_root_tl \l_keys_path_tl .default } {#1} } \cs_generate_variant:Nn \@@_default_set:n { V } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\@@_initialise:n, \@@_initialise:V} % \begin{macro}[aux, EXP]{\@@_initialise:wn} % A set up for initialisation from which the key system requires that % the path is split up into a module and a key name. At this stage, % \cs{l_keys_path_tl} will contain \texttt{/} so a split is easy to do. % \begin{macrocode} \cs_new_protected:Npn \@@_initialise:n #1 { \use:x { \exp_after:wN \@@_initialise:wn \l_keys_path_tl \q_stop {#1} } } \cs_generate_variant:Nn \@@_initialise:n { V } \cs_new:Npn \@@_initialise:wn #1 / #2 \q_stop #3 { \keys_set:nn {#1} { #2 = \exp_not:n { {#3} } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_meta_make:n, \@@_meta_make:x} % To create a meta-key, simply set up to pass data through. % \begin{macrocode} \cs_new_protected:Npn \@@_meta_make:n #1 { \@@_cmd_set:Vo \l_keys_path_tl { \exp_after:wN \keys_set:nn \exp_after:wN { \l_@@_module_tl } {#1} } } \cs_new_protected:Npn \@@_meta_make:x #1 { \@@_cmd_set:nx { \l_keys_path_tl } { \exp_not:N \keys_set:nn { \l_@@_module_tl } {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_multichoice_find:n} % \begin{macro}[int]{\@@_multichoice_make:} % \begin{macro}[int]{\@@_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:Npn \@@_multichoice_find:n #1 { \clist_map_function:nN {#1} \@@_choice_find:n } \cs_new_protected_nopar:Npn \@@_multichoice_make: { \@@_cmd_set:nn { \l_keys_path_tl } { \@@_multichoice_find:n {##1} } \@@_cmd_set:nn { \l_keys_path_tl / unknown } { \__msg_kernel_error:nnxx { kernel } { key-choice-unknown } { \l_keys_path_tl } {##1} } } \cs_new_protected:Npn \@@_multichoices_make:nn #1#2 { \@@_multichoice_make: \int_zero:N \l_keys_choice_int \clist_map_inline:nn {#1} { \int_incr:N \l_keys_choice_int \@@_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} } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_value_requirement:n} % Values can be required or forbidden by having the appropriate marker % set. % \begin{macrocode} \cs_new_protected:Npn \@@_value_requirement:n #1 { \tl_set_eq:cc { \c_@@_vars_root_tl \l_keys_path_tl .req } { c_@@_value_ #1 _tl } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_variable_set:NnNN, \@@_variable_set:cnNN} % \begin{macro}[int]{\@@_variable_set:NnN, \@@_variable_set:cnN} % Setting a variable takes the type and scope separately so that % it is easy to make a new variable if needed. The three-argument % version is set up so that the use of |{ }| as an \texttt{N}-type % variable is only done once! % \begin{macrocode} \cs_new_protected:Npn \@@_variable_set:NnNN #1#2#3#4 { \use:c { #2_if_exist:NF } #1 { \use:c { #2 _new:N } #1 } \@@_cmd_set:nx { \l_keys_path_tl } { \exp_not:c { #2 _ #3 set:N #4 } \exp_not:N #1 {##1} } } \cs_new_protected:Npn \@@_variable_set:NnN #1#2#3 { \@@_variable_set:NnNN #1 {#2} { } #3 } \cs_generate_variant:Nn \@@_variable_set:NnNN { c } \cs_generate_variant:Nn \@@_variable_set:NnN { c } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Creating key properties} % % The key property functions are all wrappers for internal functions, % meaning that things stay readable and can also be altered later on. % % \begin{macro}{.bool_set:N} % \begin{macro}{.bool_gset:N} % One function for this. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .bool_set:N } #1 { \@@_bool_set:NN #1 { } } \cs_new_protected:cpn { \c_@@_props_root_tl .bool_gset:N } #1 { \@@_bool_set:NN #1 g } % \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:cpn { \c_@@_props_root_tl .bool_set_inverse:N } #1 { \@@_bool_set_inverse:NN #1 { } } \cs_new_protected:cpn { \c_@@_props_root_tl .bool_gset_inverse:N } #1 { \@@_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}. % \begin{macrocode} \cs_new_protected_nopar:cpn { \c_@@_props_root_tl .choice: } { \@@_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_@@_props_root_tl .choices:nn } #1 { \@@_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 % \texttt{set} function. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .code:n } #1 { \@@_cmd_set:nn { \l_keys_path_tl } {#1} } \cs_new_protected:cpn { \c_@@_props_root_tl .code:x } #1 { \@@_cmd_set:nx { \l_keys_path_tl } {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{.choice_code:n, .choice_code:x} % Storing the code for choices % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .choice_code:n } #1 { \@@_choice_code_store:n {#1} } \cs_new_protected:cpn { \c_@@_props_root_tl .choice_code:x } #1 { \@@_choice_code_store:x {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{.clist_set:N, .clist_set:c} % \begin{macro}{.clist_gset:N, .clist_gset:c} % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .clist_set:N } #1 { \@@_variable_set:NnN #1 { clist } n } \cs_new_protected:cpn { \c_@@_props_root_tl .clist_set:c } #1 { \@@_variable_set:cnN {#1} { clist } n } \cs_new_protected:cpn { \c_@@_props_root_tl .clist_gset:N } #1 { \@@_variable_set:NnNN #1 { clist } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .clist_gset:c } #1 { \@@_variable_set:cnNN {#1} { clist } g n } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{.default:n, .default:V} % Expansion is left to the internal functions. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .default:n } #1 { \@@_default_set:n {#1} } \cs_new_protected:cpn { \c_@@_props_root_tl .default:V } #1 { \@@_default_set:V #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{.dim_set:N, .dim_set:c} % \begin{macro}{.dim_gset:N, .dim_gset:c} % Setting a variable is very easy: just pass the data along. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .dim_set:N } #1 { \@@_variable_set:NnN #1 { dim } n } \cs_new_protected:cpn { \c_@@_props_root_tl .dim_set:c } #1 { \@@_variable_set:cnN {#1} { dim } n } \cs_new_protected:cpn { \c_@@_props_root_tl .dim_gset:N } #1 { \@@_variable_set:NnNN #1 { dim } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .dim_gset:c } #1 { \@@_variable_set:cnNN {#1} { dim } g n } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{.fp_set:N, .fp_set:c} % \begin{macro}{.fp_gset:N, .fp_gset:c} % Setting a variable is very easy: just pass the data along. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .fp_set:N } #1 { \@@_variable_set:NnN #1 { fp } n } \cs_new_protected:cpn { \c_@@_props_root_tl .fp_set:c } #1 { \@@_variable_set:cnN {#1} { fp } n } \cs_new_protected:cpn { \c_@@_props_root_tl .fp_gset:N } #1 { \@@_variable_set:NnNN #1 { fp } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .fp_gset:c } #1 { \@@_variable_set:cnNN {#1} { fp } g n } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{.generate_choices:n} % Making choices is easy. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .generate_choices:n } #1 { \@@_choices_generate:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{.initial:n, .initial:V} % The standard hand-off approach. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .initial:n } #1 { \@@_initialise:n {#1} } \cs_new_protected:cpn { \c_@@_props_root_tl .initial:V } #1 { \@@_initialise:V #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{.int_set:N, .int_set:c} % \begin{macro}{.int_gset:N, .int_gset:c} % Setting a variable is very easy: just pass the data along. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .int_set:N } #1 { \@@_variable_set:NnN #1 { int } n } \cs_new_protected:cpn { \c_@@_props_root_tl .int_set:c } #1 { \@@_variable_set:cnN {#1} { int } n } \cs_new_protected:cpn { \c_@@_props_root_tl .int_gset:N } #1 { \@@_variable_set:NnNN #1 { int } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .int_gset:c } #1 { \@@_variable_set:cnNN {#1} { int } g n } % \end{macrocode} % \end{macro} % \end{macro} % %\begin{macro}{.meta:n, .meta:x} % Making a meta is handled internally. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .meta:n } #1 { \@@_meta_make:n {#1} } \cs_new_protected:cpn { \c_@@_props_root_tl .meta:x } #1 { \@@_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_@@_props_root_tl .multichoice: } { \@@_multichoice_make: } \cs_new_protected:cpn { \c_@@_props_root_tl .multichoices:nn } #1 { \@@_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} % Setting a variable is very easy: just pass the data along. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .skip_set:N } #1 { \@@_variable_set:NnN #1 { skip } n } \cs_new_protected:cpn { \c_@@_props_root_tl .skip_set:c } #1 { \@@_variable_set:cnN {#1} { skip } n } \cs_new_protected:cpn { \c_@@_props_root_tl .skip_gset:N } #1 { \@@_variable_set:NnNN #1 { skip } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .skip_gset:c } #1 { \@@_variable_set:cnNN {#1} { skip } g n } % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{.tl_set:N, .tl_set:c} % \begin{macro}{.tl_gset:N, .tl_gset:c} % \begin{macro}{.tl_set_x:N, .tl_set_x:c} % \begin{macro}{.tl_gset_x:N, .tl_gset_x:c} % Setting a variable is very easy: just pass the data along. % \begin{macrocode} \cs_new_protected:cpn { \c_@@_props_root_tl .tl_set:N } #1 { \@@_variable_set:NnN #1 { tl } n } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_set:c } #1 { \@@_variable_set:cnN {#1} { tl } n } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_set_x:N } #1 { \@@_variable_set:NnN #1 { tl } x } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_set_x:c } #1 { \@@_variable_set:cnN {#1} { tl } x } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset:N } #1 { \@@_variable_set:NnNN #1 { tl } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset:c } #1 { \@@_variable_set:cnNN {#1} { tl } g n } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset_x:N } #1 { \@@_variable_set:NnNN #1 { tl } g x } \cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset_x:c } #1 { \@@_variable_set:cnNN {#1} { tl } g x } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{.value_forbidden:} % \begin{macro}{.value_required:} % These are very similar, so both call the same function. % \begin{macrocode} \cs_new_protected_nopar:cpn { \c_@@_props_root_tl .value_forbidden: } { \@@_value_requirement:n { forbidden } } \cs_new_protected_nopar:cpn { \c_@@_props_root_tl .value_required: } { \@@_value_requirement:n { required } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Setting keys} % % \begin{macro}{\keys_set:nn, \keys_set:nV, \keys_set:nv, \keys_set:no} % \begin{macro}[aux]{\@@_set:nnn, \@@_set:onn} % A simple wrapper again. % \begin{macrocode} \cs_new_protected:Npn \keys_set:nn { \@@_set:onn { \l_@@_module_tl } } \cs_new_protected:Npn \@@_set:nnn #1#2#3 { \tl_set:Nx \l_@@_module_tl { \tl_to_str:n {#2} } \keyval_parse:NNn \@@_set_elt:n \@@_set_elt:nn {#3} \tl_set:Nn \l_@@_module_tl {#1} } \cs_generate_variant:Nn \keys_set:nn { nV , nv , no } \cs_generate_variant:Nn \@@_set:nnn { o } % \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]{\@@_set_known:nnnN, \@@_set_known:onnN} % \begin{macrocode} \cs_new_protected:Npn \keys_set_known:nnN { \@@_set_known:onnN { \l_@@_module_tl } } \cs_new_protected:Npn \@@_set_known:nnnN #1#2#3#4 { \tl_set:Nx \l_@@_module_tl { \tl_to_str:n {#2} } \clist_clear:N \l_@@_unknown_clist \cs_set_eq:NN \@@_execute_unknown: \@@_execute_unknown_alt: \keyval_parse:NNn \@@_set_elt:n \@@_set_elt:nn {#3} \cs_set_eq:NN \@@_execute_unknown: \@@_execute_unknown_std: \tl_set:Nn \l_@@_module_tl {#1} \clist_set_eq:NN #4 \l_@@_unknown_clist } \cs_generate_variant:Nn \keys_set_known:nnN { nV , nv , no } \cs_generate_variant:Nn \@@_set_known:nnnN { o } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_set_elt:n, \@@_set_elt:nn} % \begin{macro}[aux]{\@@_set_elt_aux:nn} % A shared system once again. First, set the current path and add a % default if needed. There are then checks to see if the a value is % required or forbidden. If everything passes, move on to execute the % code. % \begin{macrocode} \cs_new_protected:Npn \@@_set_elt:n #1 { \bool_set_true:N \l_@@_no_value_bool \@@_set_elt_aux:nn {#1} { } } \cs_new_protected:Npn \@@_set_elt:nn #1#2 { \bool_set_false:N \l_@@_no_value_bool \@@_set_elt_aux:nn {#1} {#2} } \cs_new_protected:Npn \@@_set_elt_aux:nn #1#2 { \tl_set:Nx \l_keys_key_tl { \tl_to_str:n {#1} } \tl_set:Nx \l_keys_path_tl { \l_@@_module_tl / \l_keys_key_tl } \@@_value_or_default:n {#2} \bool_if:nTF { \@@_if_value_p:n { required } && \l_@@_no_value_bool } { \__msg_kernel_error:nnx { kernel } { value-required } { \l_keys_path_tl } } { \bool_if:nTF { \@@_if_value_p:n { forbidden } && ! \l_@@_no_value_bool } { \__msg_kernel_error:nnxx { kernel } { value-forbidden } { \l_keys_path_tl } { \l_keys_value_tl } } { \@@_execute: } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_value_or_default:n} % If a value is given, return it as |#1|, otherwise send a default if % available. % \begin{macrocode} \cs_new_protected:Npn \@@_value_or_default:n #1 { \tl_set:Nn \l_keys_value_tl {#1} \bool_if:NT \l_@@_no_value_bool { \quark_if_no_value:cF { \c_@@_vars_root_tl \l_keys_path_tl .default } { \cs_if_exist:cT { \c_@@_vars_root_tl \l_keys_path_tl .default } { \tl_set_eq:Nc \l_keys_value_tl { \c_@@_vars_root_tl \l_keys_path_tl .default } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_if_value_p:n} % To test if a value is required or forbidden. A simple check for % the existence of the appropriate marker. % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_value:n #1 { p } { \tl_if_eq:ccTF { c_@@_value_ #1 _tl } { \c_@@_vars_root_tl \l_keys_path_tl .req } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_execute:} % \begin{macro}[aux] % { % \@@_execute_unknown:, % \@@_execute_unknown_std:, % \@@_execute_unknown_alt: % } % \begin{macro}[aux]{\@@_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 % path. If both of these fail, complain. % \begin{macrocode} \cs_new_nopar:Npn \@@_execute: { \@@_execute:nn { \l_keys_path_tl } { \@@_execute_unknown: } } \cs_new_nopar:Npn \@@_execute_unknown: { \@@_execute:nn { \l_@@_module_tl / unknown } { \__msg_kernel_error:nnxx { kernel } { key-unknown } { \l_keys_path_tl } { \l_@@_module_tl } } } \cs_new_eq:NN \@@_execute_unknown_std: \@@_execute_unknown: \cs_new_nopar:Npn \@@_execute_unknown_alt: { \clist_put_right:Nx \l_@@_unknown_clist { \exp_not:o \l_keys_key_tl \bool_if:NF \l_@@_no_value_bool { = { \exp_not:o \l_keys_value_tl } } } } \cs_new:Npn \@@_execute:nn #1#2 { \cs_if_exist:cTF { \c_@@_code_root_tl #1 } { \exp_args:Nc \exp_args:No { \c_@@_code_root_tl #1 } \l_keys_value_tl } {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_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. % \begin{macrocode} \cs_new:Npn \@@_choice_find:n #1 { \@@_execute:nn { \l_keys_path_tl / \tl_to_str:n {#1} } { \@@_execute:nn { \l_keys_path_tl / unknown } { } } } % \end{macrocode} % \end{macro} % % \subsection{Utilities} % % \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_@@_code_root_tl #1 / #2 } { \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_@@_code_root_tl #1 / #2 / #3 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\keys_show:nn} % Showing a key is just a question of using the correct name. % \begin{macrocode} \cs_new:Npn \keys_show:nn #1#2 { \cs_show:c { \c_@@_code_root_tl #1 / \tl_to_str:n {#2} } } % \end{macrocode} % \end{macro} % % \subsection{Messages} % % For when there is a need to complain. % \begin{macrocode} \__msg_kernel_new:nnnn { kernel } { boolean-values-only } { Key~'#1'~accepts~boolean~values~only. } { The~key~'#1'~only~accepts~the~values~'true'~and~'false'. } \__msg_kernel_new:nnnn { kernel } { choice-unknown } { Choice~'#2'~unknown~for~key~'#1'. } { The~key~'#1'~takes~a~limited~number~of~values.\\ The~input~given,~'#2',~is~not~on~the~list~accepted. } \__msg_kernel_new:nnnn { kernel } { generate-choices-before-code } { No~code~available~to~generate~choices~for~key~'#1'. } { \c_msg_coding_error_text_tl Before~using~.generate_choices:n~the~code~should~be~defined~ with~'.choice_code:n'~or~'.choice_code:x'. } \__msg_kernel_new:nnnn { kernel } { key-no-property } { No~property~given~in~definition~of~key~'#1'. } { \c_msg_coding_error_text_tl Inside~\keys_define:nn each~key~name needs~a~property: \\ ~ ~ #1 . \\ LaTeX~did~not~find~a~'.'~to~indicate~the~start~of~a~property. } \__msg_kernel_new:nnnn { kernel } { key-unknown } { The~key~'#1'~is~unknown~and~is~being~ignored. } { The~module~'#2'~does~not~have~a~key~called~#1'.\\ Check~that~you~have~spelled~the~key~name~correctly. } \__msg_kernel_new:nnnn { kernel } { property-requires-value } { The~property~'#1'~requires~a~value. } { \c_msg_coding_error_text_tl LaTeX~was~asked~to~set~property~'#2'~for~key~'#1'.\\ No~value~was~given~for~the~property,~and~one~is~required. } \__msg_kernel_new:nnnn { kernel } { property-unknown } { The~key~property~'#1'~is~unknown. } { \c_msg_coding_error_text_tl LaTeX~has~been~asked~to~set~the~property~'#1'~for~key~'#2':~ this~property~is~not~defined. } \__msg_kernel_new:nnnn { kernel } { value-forbidden } { The~key~'#1'~does~not~taken~a~value. } { The~key~'#1'~should~be~given~without~a~value.\\ LaTeX~will~ignore~the~given~value~'#2'. } \__msg_kernel_new:nnnn { kernel } { value-required } { The~key~'#1'~requires~a~value. } { The~key~'#1'~must~have~a~value.\\ No~value~was~present:~the~key~will~be~ignored. } % \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} %<*deprecated> \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} % % Internal material for removal by 2012-12-31. % % \begin{macrocode} %<*deprecated> \cs_new_eq:NN \c_keys_code_root_tl \c__keys_code_root_tl % % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % %\end{implementation} % %\PrintIndex