%\iffalse % !TEX program = pdflatex %% File: keys3.dtx Copyright (C) 2008-2009 Joseph Wright %% %% 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 %% %% ----------------------------------------------------------------------- %<*driver|package> \RequirePackage{l3names} % %\fi \GetIdInfo$Id: keys3.dtx 75 2009-03-12 22:10:52Z josephwright $ {Key management for LaTeX3} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass[full]{l3doc} \begin{document} \DocInput{\filename.\filenameext} \end{document} % % \fi % % \title{The \textsf{keys3} package^^A % \thanks{This file has version number \fileversion, last % revised \filedate.}\\ % Key management for \LaTeX3} % \author{Joseph Wright^^A % \thanks{E-mail: joseph.wright@morningstar2.co.uk}} % \date{\filedate} % \maketitle % %\section{Key management} % % The key--value method is a popular system for creating large numbers % of settings for controlling macro or package behaviour. For the % user, the system normally results in input of the form %\begin{verbatim} % \PackageControlMacro{ % key = value, % key two = value two % } %\end{verbatim} % or %\begin{verbatim} % \PackageMacro[ % key = value, % key two = value two % ]{argument}. %\end{verbatim} % For the programmer, the original \textsf{keyval} package gives only % the most basic interface for this work. All key macros have to be % created one at a time, and as a result the \textsf{kvoptions} and % \textsf{xkeyval} packages have been written to extend the ease of % creating keys. However, the underlying model is rather inflexible. % % As an alternative, \textsf{pgfkeys} from the \textsf{pgf}/Ti\emph{k}z % bundle uses a ``file-like'' model for keys. In this model, each key % has one or more functions attached to define its action. In % \textsf{pgfkeys} terminology, these are key \emph{handlers}. Keys can % be created and used using a unified interface: %\begin{verbatim} % \pgfkeys{ % /path/key/.handler = code, % /path/key = value % } %\end{verbatim} % % The \textsf{keys3} package is aimed at creating a high-level % programmers interface for key--value controls in \LaTeX3. Key % creation and control follows the \textsf{pgfkeys} closely, although % changes have been made to adhere to the new coding ideas of \LaTeX3, % and new functions have been added. The \textsf{keys3} package is % \emph{not} a simple translation of \textsf{pgfkeys} to the new syntax. % In package internals have been written from the ground up, to better % enforce variable typing and to act in a know manner under failing % circumstances. % % In the \textsf{keys3} model, each key belongs to a module, % which may have one or more subdivisions. Each key then has % one or more properties which define how the key acts. The idea % of a single function for all key control is retained: %\begin{verbatim} % \keys_manage:n{ % /module/key/.property = code, % /module/sub/key/.property = more code, % /module/key = value % } %\end{verbatim} % The combination of all \m{module} parts up to the \m{key} is referred % to as the \emph{path} of the key. % %\subsection{Creating, retrieving and setting keys} % % The main interface for key management is the the \cs{keys_manage:n} % macro. This can be used to create, retrieve and set keys, and is % therefore the preferred interface for the package. % % \begin{function}{ % \keys_manage:n| % \keys_manage_quick:n| % \keys_manage_internal:n| % } % \begin{syntax} % "\keys_manage:n" % \end{syntax} % The main key management function, which parses over the and processes each key. The standard version removes leading % and trailing spaces and checks catcodes for ungrouped ``|,|'' and % ``|=|''. The |_quick| version omits these tests for speed, and is % therefore more suited to creating keys inside code blocks. The % \texttt{internal} variant is used when recycling keys inside % \textsf{keys3}. % \end{function} % % \begin{function}{ % \keys_manage:nn| % \keys_manage_quick:nn| % } % \begin{syntax} % "\keys_manage:nn" % \end{syntax} % Versions of the above which include a as an argument. These % are faster versions of the |:n| macros when setting lots of keys % separately and with known paths. % \end{function} % % Setting up and altering keys is carried out using one or more % properties (in \textsf{pgfkeys}, these are called handlers). In all % cases, \m{key} may be a full-qualified key with a path, or a partial % key to which the default path will be added. Notice that when a % single argument is required, the braces around the argument may be % omitted without any error arising. % %\subsubsection{Storing values} % % A common use of key--value input is to store the values given in a % variable for later use. \textsf{keys3} provides pre-defined % properties for storing in int, skip, tlp and toks variables. The % method is similar in all cases: %\begin{verbatim} % \keys_manage:n{ % /module/key~one/.int_set:N = \l_mod_data_int, % /module/key~two/.skip_set:N = \l_mod_data_skip, % /module/key~three/.tlp_set:N = \l_mod_data_tlp, % /module/key~four/.toks_set:N = \l_mod_data_toks % } %\end{verbatim} % Global variants for all of these are. % % % When using keys to store a value, it is often convenient to give only % the unique part of the variable name. This is particularly true when % creating a large number of related storage areas. To achieve this, % \textsf{keys3} requires that the module prefix to be used is defined % first. The special key \texttt{/keys/current_module:n} can be set with % the name of the current module. %\begin{verbatim} % \keys_manage:n{ % /keys/current_module:n = module, % /module/key/.tlp_set:n = my_data, % } %\end{verbatim} % This will use a variable called \cs{l_module_my_data_tlp} to store the % input. Thus the preceding code achieves the same effect as %\begin{verbatim} % \keys_manage:n{ % /module/key/.tlp_set:N = \l_module_my_data_tlp % } %\end{verbatim} % The same method applies to int, skip and toks variables. The % appropriate prefix (\cs{l_} or \cs{g_}) and suffix (|_int|, |_skip|, % |_tlp| or |_toks|) is always added. % %\subsubsection{Multiple choice keys} % % Multiple choice keys are created in \textsf{keys3} using the % \texttt{.expects_choice:} property. Each choice is then a sub-key of % the choice key. %\begin{verbatim} % \keys_manage:n{ % /module/key/.expects_choice:, % /module/key/choice~a/.code:n = Some code, % /module/key/choice~b/.code:n = Some other code, % } %\end{verbatim} % In this way, choices which execute arbitrary code can be created. % Notice that the \m{code} should \emph{not} include a parameter (|#1|). % % Often it is desirable to create a family of similar choices, which % only require either the text of the choice, or the position of the % choice in a list, to be used. To create this type of simple choice, % \textsf{keys3} provides the \texttt{.create_choices:nn} property. This % applies the same code to a list of choice text. Inside the code, % the name of the choice given is available as % \cs{l_keys_current_choice_tlp}. The position of the choice in the % lists is also available, as \cs{l_keys_current_choice_int}. %\begin{verbatim} % \keys_manage:n { % /module/key/.create_choices:nn = {choice~a, choice~b, choice~c} { % You~gave~choice~``\l_keys_current_choice_tlp'',~which~is~in~ % position~\l_keys_current_choice_int~in~the~list. % } % } %\end{verbatim} % %\subsection{Properties} % % \begin{function}{ % .bool_set:N| % .bool_gset:N| % } % \begin{syntax} % /.bool_set:N = % \end{syntax} % Defines to set to (which must be either % \texttt{true} or \texttt{false}). % \end{function} % % \begin{function}{ % .bool_set_inverse:n| % .bool_gset_inverse:n| % } % \begin{syntax} % /.bool_set_inverse:n = % \end{syntax} % Defines to set switch with unique to (which % must be either \texttt{true} or \texttt{false}), with reversed logic. % The switch name will be constructed using the current (if % any), prefixed by |\l_| and ending with |_int|. % \end{function} % % \begin{function}{ % .bool_set_inverse:N| % .bool_gset_inverse:N| % } % \begin{syntax} % /.bool_set_inverse:N = % \end{syntax} % Defines to set to (which must be either % \texttt{true} or \texttt{false}), with reversed logic. % \end{function} % % \begin{function}{ % .bool_set:n| % .bool_gset:n| % } % \begin{syntax} % /.bool_set:n = % \end{syntax} % Defines to set switch with unique to (which % must be either \texttt{true} or \texttt{false}). The switch % name will be constructed using the current (if any), % prefixed by |\l_| and ending with |_int|. % \end{function} % % \begin{function}{.cd:} % \begin{syntax} % /.cd: % \end{syntax} % Changes path to that given by . % \end{function} % % \begin{function}{ % .code:n| % .code:x % } % \begin{syntax} % /.code:n = % \end{syntax} % Stores the for execution when is called. The can % include one parameter (|#1|). % \end{function} % % \begin{function}{ % .code:Nn| % .code:Nx % } % \begin{syntax} % /.code:Nn = % \end{syntax} % Stores the for execution when is called. The can % include parameters, which can be in the range 0--9. % \end{function} % % \begin{function}{ % .create_choices:nn| % .create_choices:nx| % } % \begin{syntax} % /.create_choices:nn = % \end{syntax} % Creates a sub-key of for each in the comma-separated % . Each will have associated with it. The % current choice text is available as \cs{l_keys_current_choice_tlp}, % and its position in the as \cs{l_keys_current_choice_int}. % \end{function} % % \begin{function}{.default:n} % \begin{syntax} % /.default:n = % \end{syntax} % Creates a default value for , which is used if no value is % given. The is stored as a tlp, and so must be compatible % with this variable type. % \end{function} % % \begin{function}{.expects_choice:} % \begin{syntax} % /.expects_choice: % \end{syntax} % Indicates that the given for should be a sub-key of % . In this way, accepts one of a limited range of choices. % \end{function} % % \begin{function}{ % .int_set:N| % .int_gset:N| % } % \begin{syntax} % /.int_set:N = % \end{syntax} % Defines to store in the named. % \end{function} % % \begin{function}{ % .int_set:n| % .int_gset:n| % } % \begin{syntax} % /.int_set:n = % \end{syntax} % Defines to store in a int with unique name . The % int name will be constructed using the current (if any), % prefixed by |\l_| and ending with |_int|. % \end{function} % % \begin{function}{.retry:n} % \begin{syntax} % /.retry:n = % \end{syntax} % Executes if it exists and if the previous \texttt{.try:n} % failed. The is passed to , if successful. % \end{function} % % \begin{function}{ % .show_code:| % .show_key:| % } % \begin{syntax} % /.show_code: % /.show_key: % \end{syntax} % Shows the function for the current key or the code associated with % the current key. % \end{function} % % \begin{function}{ % .skip_set:N| % .skip_gset:N| % } % \begin{syntax} % /.skip_set:N = % \end{syntax} % Defines to store in the named. % \end{function} % % \begin{function}{ % .skip_set:n| % .skip_gset:n| % } % \begin{syntax} % /.skip_set:n = % \end{syntax} % Defines to store in a skip with unique name . The % skip name will be constructed using the current (if any), % prefixed by |\l_| and ending with |_skip|. % \end{function} % % \begin{function}{ % .tlp_set:N| % .tlp_set_x:N| % .tlp_gset:N| % .tlp_gset_x:N| % } % \begin{syntax} % /.tlp_set:N = % \end{syntax} % Defines to store in the named. The \texttt{x} % versions use \cs{tlp_(g)set:Nx} for this process, the standard % version \cs{tlp_(g)set:Nn}. % \end{function} % % \begin{function}{ % .tlp_set:n| % .tlp_set_x:n| % .tlp_gset:n| % .tlp_gset_x:n| % } % \begin{syntax} % /.tlp_set:n = % \end{syntax} % Defines to store in a tlp with unique name . The % tlp name will be constructed using the current (if any), % prefixed by |\l_| and ending with |_tlp|. % \end{function} % % \begin{function}{ % .toks_set:N| % .toks_gset:N| % } % \begin{syntax} % /.toks_set:N = % \end{syntax} % Defines to store in the named. % \end{function} % % \begin{function}{ % .toks_set:n| % .toks_gset:n| % } % \begin{syntax} % /.toks_set:n = % \end{syntax} % Defines to store in a toks with unique name . The % rest of the toks name will be constructed using the current % (if any), prefixes by |\l_| and ending with |_toks|. % \end{function} % % \begin{function}{.try:n} % \begin{syntax} % /.try:n = % \end{syntax} % Executes if defined, and does nothing otherwise. The is % passed to , if successful. % \end{function} % % \begin{function}{ % .use_keys:n| % .use_keys:x| % } % \begin{syntax} % /.use_keys:n = % \end{syntax} % Calling applies the as a block. Thus one % can make many related settings. As usual, |#1| is available in the % as will take one argument. % \end{function} % % \begin{function}{ % .use_keys:Nn| % .use_keys:Nx| % } % \begin{syntax} % /.use_keys:Nn = % \end{syntax} % Calling applies the as a block. Thus one % can make many related settings. The will take of % arguments, which can be used inside the . % \end{function} % % \begin{function}{ % .value_forbidden:| % .value_required:| % } % \begin{syntax} % /.value_forbidden: % \end{syntax} % Flags for forbidding and requiring a for . % \end{function} % %\subsection{Variables and constants} % % \begin{variable}{ % \c_keys_0_empty_tlp| % \c_keys_1_empty_tlp| % \c_keys_2_empty_tlp| % \c_keys_3_empty_tlp| % \c_keys_4_empty_tlp| % \c_keys_5_empty_tlp| % \c_keys_6_empty_tlp| % \c_keys_7_empty_tlp| % \c_keys_8_empty_tlp| % \c_keys_9_empty_tlp| % } % A set of tlps containing empty groups. % \end{variable} % % \begin{variable}{\c_keys_cs_prefix_tlp} % The prefix added to the fully-qualified key when saving them. % \end{variable} % % \begin{variable}{ % \c_keys_errors_path_tlp| % \c_keys_properties_path_tlp| % \c_keys_utilities_path_tlp| % } % Paths for properties used by \textsf{l3keys} itself. % \end{variable} % % \begin{variable}{\c_keys_root_tlp} % The root path for keys. % \end{variable} % % \begin{variable}{\l_keys_choice_code_tlp} % The code to execute for each multiple choice when created \emph{en % masse}. % \end{variable} % % \begin{variable}{ % \l_keys_current_choice_tlp| % \l_keys_current_choice_int| % } % Information on multiple choices. % \end{variable} % % \begin{variable}{ % \l_keys_current_key_full_tlp| % \l_keys_current_key_name_tlp| % } % The current key name is stored both with and without a path. % \end{variable} % % \begin{variable}{\l_keys_current_module_tlp} % Current module name used when creating csnames. % \end{variable} % % \begin{variable}{ % \l_keys_current_path_tlp| % \l_keys_default_path_tlp| % \l_keys_choice_path_tlp| % } % Various key paths need to be stored. % \end{variable} % % \begin{variable}{\l_keys_current_value_toks} % The value given for the current key, stored as a token register. % \end{variable} % % \begin{variable}{\l_keys_no_value_bool} % A marker for ``no value'' as key input. % \end{variable} % % \begin{variable}{\l_keys_success_bool} % A marker used when trying keys without raising errors. % \end{variable} % % \begin{variable}{\l_keys_tmpa_tlp} % A scratch variable. % \end{variable} % % \begin{variable}{ % \l_keys_err_unknown_key_tlp| % \l_keys_err_value_ignored_tlp| % \l_keys_err_value_required_tlp| % \l_keys_err_def_x_args_tlp| % \l_keys_err_boolean_expected_tlp| % \l_keys_err_not_boolean_tlp| % \l_keys_err_unknown_choice_tlp| % } % Identification tlps for error messages. % \end{variable} % %\subsection{Internal functions} % % Notice that everything should be done using the keys system. Only % \cs{keys_manage:n} and so on are intended for external use. In all % cases, \m{key} is a fully-qualified key name. Functions created will % be prefixed with \cs{c_keys_cs_prefix_tlp}. % % \begin{function}{\keys_bool_new:n} % \begin{syntax} % "\keys_bool_new:n" % \end{syntax} % Creates a switch , which will be set to \texttt{true}. % \end{function} % % \begin{function}{ % \keys_bool_set:nN| % \keys_bool_set:nnn| % } % \begin{syntax} % "\keys_bool_set:nN" % "\keys_bool_set:nnn" % \end{syntax} % Uses (either \texttt{set} or \texttt{gset}) to use % in setting . If a is given, it is used to % construct a csname for the switch including the current . % The should be |l_| or |g_|. % \end{function} % % \begin{function}{ % \keys_bool_set_inverse:n| % \keys_bool_set_inverse:nN| % \keys_bool_set_inverse:nnn| % } % \begin{syntax} % "\keys_bool_set:n" % "\keys_bool_set:nN" % "\keys_bool_set:nnn" % \end{syntax} % Uses (either \texttt{set} or \texttt{gset}) to use % in setting . If a is given, it is used to % construct a csname for the switch including the current . % The should be |l_| or |g_|. The logic of the setting is % reversed compared to the \cs{keys_bool_set:} functions. % \end{function} % % \begin{function}{\keys_clear_properties:n} % \begin{syntax} % "\keys_clear_properties:n" % \end{syntax} % Clears the internal properties of . % \end{function} % % \begin{function}{\keys_choice_create:n} % \begin{syntax} % "\keys_choice_create:n" % \end{syntax} % Creates as a sub-key of . % \end{function} % % \begin{function}{\keys_choices_create:Nnn} % \begin{syntax} % "\keys_choices_create:Nnn" % \end{syntax} % Takes a comma separated and makes a as a sub-key of % the current for each. The will execute , which % is processed according to (either \texttt{n} or % \texttt{x}). The has access to \cs{l_keys_current_choice_tlp} % and \cs{l_keys_current_choice_int}, which indicate the choice given. % \end{function} % % \begin{function}{\keys_default_add:} % \begin{syntax} % "\keys_default_add:" % \end{syntax} % If no value was given for the current key, and a default value is % available, copies the default into \cs{l_keys_current_value_toks}. % \end{function} % % \begin{function}{\keys_err_new:nNnnn} % \begin{syntax} % "\keys_err_new:nNnnn" % \end{syntax} % Creates error with and taking arguments, with % description followed by , and recovery . % \end{function} % % \begin{function}{ % \keys_err_use:nw| % \keys_err_use:n| % \keys_err_use:nn| % } % \begin{syntax} % "\keys_err_use:n" % "\keys_err_use:nn" % \end{syntax} % Issues error , with or without . The \texttt{w} % variant may take one or two arguments. % \end{function} % % \begin{function}{ % \keys_find_code_full:| % \keys_find_code_name: % } % \begin{syntax} % "\keys_find_code_full:" % "\keys_find_code_name:" % \end{syntax} % Find code to execute for the current fully qualified key % (\texttt{full}) or key name only (\texttt{name}). % \end{function} % % \begin{function}{\keys_if_cmd_really_exist:n / (TF)} % \begin{syntax} % "\keys_if_cmd_really_exist:nTF" % ~~~~ % \end{syntax} % Checks for the existence of a |._cmd:w| function for . % \end{function} % % \begin{function}{\keys_if_really_exist:n / (TF)} % \begin{syntax} % "\keys_if_really_exist:nTF" % ~~~~ % \end{syntax} % Checks for the existence of a . % \end{function} % % \begin{function}{\keys_if_value:n / (TF)} % \begin{syntax} % "\keys_if_value:nTF" % \end{syntax} % Checks for the switch of a (typically % \texttt{required} or \texttt{forbidden}). % \end{function} % % \begin{function}{ % \keys_no_value_elt:n| % \keys_value_elt:nn % } % \begin{syntax} % "\keys_no_value_elt:n" % "\keys_value_elt:nn" % \end{syntax} % Functions used by \textsf{l3keyval} for each of the being processed. % \end{function} % % \begin{function}{\keys_parse:n} % \begin{syntax} % "\keys_parse:n" % \end{syntax} % Functions which actually parses . % \end{function} % % \begin{function}{\keys_parse_list:n} % \begin{syntax} % "\keys_parse_list:n" % \end{syntax} % Set up macro for \cs{keys_parse:n}. % \end{function} % % \begin{function}{ % \keys_path_add:N| % \keys_path_add:w % } % \begin{syntax} % "\keys_path_add:N" % "\keys_path_add:w" "\q_stop" % \end{syntax} % Adds a full path to key name stored in or given as . % \end{function} % % \begin{function}{\keys_process_elt:nn} % \begin{syntax} % "\keys_process_elt:nn" % \end{syntax} % Lead-off processor for converting into a fully-qualified % and checking validity of . % \end{function} % % \begin{function}{ % \keys_separate_path:| % \keys_separate_path:w % } % \begin{syntax} % "\keys_separate_path:" % "\keys_separate_path:w" / / "\q_stop" % \end{syntax} % Separates key into and . % \end{function} % % \begin{function}{ % \keys_set:NN| % \keys_set:Nnn| % } % \begin{syntax} % "\keys_set:NN" % "\keys_set:Nnn" % \end{syntax} % Uses to store in . If a is % given, it is used to construct a csname for the variable including % the current . The should be |l_| or |g_|. % \end{function} % % \begin{function}{\keys_set_eq:nn} % \begin{syntax} % "\keys_set_eq:nn" % \end{syntax} % Sets equal to . % \end{function} % % \begin{function}{ % \keys_set_cmd:nn| % \keys_set_cmd:nx| % \keys_set_cmd:nNn| % \keys_set_cmd:nNx| % } % \begin{syntax} % "\keys_set_cmd:nn" % "\keys_set_cmd:nNn" % \end{syntax} % Creates a |._cmd:w| function for , with definition . The % \texttt{N} variant can include parameters. % \end{function} % % \begin{function}{ % \keys_store:nn| % \keys_store:nx % } % \begin{syntax} % "\keys_store:nn" % \end{syntax} % Stores in function. % \end{function} % % \begin{function}{\keys_toks_set:Nn} % \begin{syntax} % "\keys_toks_set:Nn" % \end{syntax} % Sets equal to the content of % \end{function} % % \begin{function}{\keys_try:} % \begin{syntax} % "\keys_try:" % \end{syntax} % Attempt to execute a key, with no error if the key is unknown. % \end{function} % % \begin{function}{\keys_undefine:n} % \begin{syntax} % "\keys_undefine:n" % \end{syntax} % Delete definition of . % \end{function} % % \begin{function}{\keys_use:n} % \begin{syntax} % "\keys_use:n" % \end{syntax} % Use definition of . % \end{function} % % \begin{function}{ % \keys_use_cmd:n| % \keys_use_cmd:nn % } % \begin{syntax} % "\keys_use_cmd:n" % "\keys_use_cmd:nn" % \end{syntax} % Uses the |._cmd:w| function for , passing if needed. % \end{function} % %\subsubsection{Internal properties} % % The internal key properties should not be accessed directly. % % \begin{function}{._cmd:w} % \begin{syntax} % \end{syntax} % The function which is executed for a key with \texttt{.code}. % \end{function} % % \begin{function}{._default_tlp} % \begin{syntax} % \end{syntax} % Holds the default value for a key. % \end{function} % % \begin{function}{._forbidden_bool} % \begin{syntax} % \end{syntax} % Indicates that a value cannot be given for . % \end{function} % % \begin{function}{._num_args_tlp} % \begin{syntax} % \end{syntax} % For functions defined with \texttt{.code:Nn} and \texttt{.code:Nx}, % contains the number of arguments the associated |._cmd:w| function % takes. % \end{function} % % \begin{function}{._required_bool} % \begin{syntax} % \end{syntax} % Indicates that must have a value provided. % \end{function} % %\subsection{Implementation} % % The usual preliminaries. The key--value parsing itself is handled by % \textsf{l3keyval}, which does the very low-level stuff so there is no % need to worry here. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \RequirePackage{l3keyval,l3messages,l3clist,l3skip} % \end{macrocode} % %\subsubsection{Variables and contrasts} % %\begin{macro}{\c_keys_0_empty_tlp} %\begin{macro}{\c_keys_1_empty_tlp} %\begin{macro}{\c_keys_2_empty_tlp} %\begin{macro}{\c_keys_3_empty_tlp} %\begin{macro}{\c_keys_4_empty_tlp} %\begin{macro}{\c_keys_5_empty_tlp} %\begin{macro}{\c_keys_6_empty_tlp} %\begin{macro}{\c_keys_7_empty_tlp} %\begin{macro}{\c_keys_8_empty_tlp} %\begin{macro}{\c_keys_9_empty_tlp} % A set of empty arguments. % \begin{macrocode} \tlp_new:cn { c_keys_0_empty_tlp } {} \tlp_new:cn { c_keys_1_empty_tlp } { {} } \tlp_new:cn { c_keys_2_empty_tlp } { {} {} } \tlp_new:cn { c_keys_3_empty_tlp } { {} {} {} } \tlp_new:cn { c_keys_4_empty_tlp } { {} {} {} {} } \tlp_new:cn { c_keys_5_empty_tlp } { {} {} {} {} {} } \tlp_new:cn { c_keys_6_empty_tlp } { {} {} {} {} {} {} } \tlp_new:cn { c_keys_7_empty_tlp } { {} {} {} {} {} {} {} } \tlp_new:cn { c_keys_8_empty_tlp } { {} {} {} {} {} {} {} {} } \tlp_new:cn { c_keys_9_empty_tlp } { {} {} {} {} {} {} {} {} {} } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\c_keys_cs_prefix_tlp} % First, the small number of constants needed are created. A prefix is % used to keep all of the actual key macros in one place. % \begin{macrocode} \tlp_new:Nn \c_keys_cs_prefix_tlp { keys-root } % \end{macrocode} %\end{macro} % %\begin{macro}{\c_keys_errors_path_tlp} %\begin{macro}{\c_keys_properties_path_tlp} %\begin{macro}{\c_keys_utilities_path_tlp} % The locations of all of the keys used by \textsf{keys3} itself. % \begin{macrocode} \tlp_new:Nn \c_keys_errors_path_tlp { /keys/errors } \tlp_new:Nn \c_keys_properties_path_tlp { /keys/properties } \tlp_new:Nn \c_keys_utilities_path_tlp { /keys } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\c_keys_root_tlp} % The key root should have a clear name; like all of the key macros, % this does not include the prefix. % \begin{macrocode} \tlp_new:Nn \c_keys_root_tlp { / } % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_choice_code_tlp} % When making choices, the code for each key has to be stored. % \begin{macrocode} \tlp_new:N \l_keys_choice_code_tlp % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_current_choice_tlp} %\begin{macro}{\l_keys_current_choice_int} % Multiple choices need some storage. % \begin{macrocode} \tlp_new:N \l_keys_current_choice_tlp \int_new:N \l_keys_current_choice_int % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\l_keys_current_key_full_tlp} %\begin{macro}{\l_keys_current_key_name_tlp} % The current key name and the fully-qualified key are stored. % \begin{macrocode} \tlp_new:N \l_keys_current_key_full_tlp \tlp_new:N \l_keys_current_default_tlp % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\l_keys_current_module_tlp} % The module name of the current module is stored here. % \begin{macrocode} \tlp_new:N \l_keys_current_module_tlp % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_current_path_tlp} %\begin{macro}{\l_keys_default_path_tlp} %\begin{macro}{\l_keys_choice_path_tlp} % The current and default paths can be stored as tlps. The default path % is then initialised as the key root. % \begin{macrocode} \tlp_new:N \l_keys_current_path_tlp \tlp_new:N \l_keys_default_path_tlp \tlp_set_eq:NN \l_keys_default_path_tlp \c_keys_root_tlp \tlp_new:N \l_keys_choice_path_tlp % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\l_keys_current_value_toks} % The current value is stored in a token register. % \begin{macrocode} \toks_new:N \l_keys_current_value_toks % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_no_value_bool} % To indicate that no value has been given. % \begin{macrocode} \bool_new:N \l_keys_no_value_bool % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_success_bool} % A switch for trying keys. % \begin{macrocode} \bool_new:N \l_keys_success_bool % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_tmpa_tlp} % A scratch area. % \begin{macrocode} \tlp_new:N \l_keys_tmpa_tlp % \end{macrocode} %\end{macro} % %\subsubsection{Functions} % %\begin{macro}{\keys_manage:n} %\begin{macro}{\keys_manage_quick:n} %\begin{macro}{\keys_manage_internal:n} %\begin{macro}[aux]{\keys_manage_aux:nn} %\begin{macro}[aux]{\keys_manage_aux:Vn} % The main key management macros both call the auxiliary function after % setting up the parser. The expansion trick means a literal path is % sent to the later function, and so the default path can be redefined. % \begin{macrocode} \cs_new:NNn \keys_manage:n 1 { \cs_set_eq:NN \keys_parse:n \KV_parse_space_removal_sanitize:n \tlp_clear:N \l_keys_current_module_tlp \keys_manage_internal:n {#1} } \cs_new:NNn \keys_manage_quick:n 1 { \let:NN \keys_parse:n \KV_parse_no_space_removal_no_sanitize:n \tlp_clear:N \l_keys_current_module_tlp \keys_manage_internal:n {#1} } \cs_new:NNn \keys_manage_internal:n 1 { \keys_manage_aux:Vn \l_keys_default_path_tlp {#1} } \cs_new:NNn \keys_manage_aux:nn 2 { \tlp_set_eq:NN \l_keys_default_path_tlp \c_keys_root_tlp \keys_parse_list:n {#2} \tlp_set:Nn \l_keys_default_path_tlp {#1} } \exp_def_form:nnn { keys_manage_aux } { nn } { Vn } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\begin{macro}{\keys_manage:nn} %\begin{macro}{\keys_manage_quick:nn} %\begin{macro}[aux]{\keys_manage_aux:nnn} %\begin{macro}[aux]{\keys_manage_aux:Vnn} % This version uses the same tricks but includes the path as a second % argument. When setting lots of keys separately, this is a little % faster than the key-based method. % \begin{macrocode} \cs_new:NNn \keys_manage:nn 2 { \let:NN \keys_parse:n \KV_parse_space_removal_sanitize:n \keys_manage_aux:Vnn \l_keys_default_path_tlp {#1} {#2} } \cs_new:NNn \keys_manage_quick:nn 2 { \let:NN \keys_parse:n \KV_parse_space_no_removal_no_sanitize:n \keys_manage_aux:Vnn \l_keys_default_path_tlp {#1} {#2} } \cs_new:NNn \keys_manage_aux:nnn 3 { \tlp_set:Nn \l_keys_default_path_tlp {#2} \tlp_clear:N \l_keys_current_module_tlp \keys_parse_list:n {#3} \tlp_set:Nn \l_keys_default_path_tlp {#1} } \cs_new:NNn \keys_manage_aux:Vnn 3 { \exp_args:NV \keys_manage_aux:nnn #1 {#2} {#3} } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\subsubsection{Internal functions} % %\begin{macro}{\keys_bool_new:n} % To create a new switch, which will be true (as the existence of the % switch is the flag here). % \begin{macrocode} \cs_new:NNn \keys_bool_new:n 1 { \bool_new:c { \c_keys_cs_prefix_tlp #1 } \bool_set_true:c { \c_keys_cs_prefix_tlp #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_bool_set:nN} %\begin{macro}{\keys_bool_set:nnn} % Boolean keys are created by using the fact that only \texttt{true} and % \texttt{false} give the right result when looking for a setting % function. A default is also set, so that the key name alone can be % given. % \begin{macrocode} \cs_new:NNn \keys_bool_set:nN 2 { \keys_parse_list:n { \c_keys_properties_path_tlp /.code:n = { \cs_if_really_exist:cTF { bool_ #1 _ ##1 :N } { \use:c { bool_ #1 _ ##1 :N } #2 }{ \keys_err_use:nn { boolean_expected } {##1} } }, \l_keys_current_path_tlp /.default:n = true } } \cs_new:NNn \keys_bool_set:nnn 3 { \keys_parse_list:n { \c_keys_properties_path_tlp /.code:x = { \exp_not:N \cs_if_really_exist:cTF { bool_ #1 _ ##1 :c } { \exp_not:N \use:c { bool_ #1 _ ##1 :c } { #2 \l_keys_current_module_tlp #3 _bool } }{ \exp_not:N \keys_err_use:nn { boolean_expected } {##1} } }, \l_keys_current_path_tlp /.default:n = true } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\keys_bool_set_inverse:n} %\begin{macro}{\keys_bool_set_inverse:nN} %\begin{macro}{\keys_bool_set_inverse:nnn} % To set keys with reversed logic, the basics can be done in the same % way as for the standard switches. The only thing needed is a % reversal of \texttt{true}/\texttt{false}. % \begin{macrocode} \cs_new:NNn \keys_bool_set_inverse:n 1 { \tlist_if_eq:nnTF {#1} { true } { false } { true } } \cs_new:NNn \keys_bool_set_inverse:nN 2 { \keys_parse_list:n { \c_keys_properties_path_tlp /.code:n = { \cs_if_really_exist:cTF { bool_ #1 _ ##1 :N } { \use:c { bool_ #1 _ \keys_bool_set_inverse:n {##1} :N } #2 }{ \keys_err_use:nn { boolean_expected } {##1} } }, \l_keys_current_path_tlp /.default:n = true } } \cs_new:NNn \keys_bool_set_inverse:nnn 3 { \keys_parse_list:n { \c_keys_properties_path_tlp /.code:x = { \exp_not:N \cs_if_really_exist:cTF { bool_ #1 _ ##1 :c } { \exp_not:N \use:c { bool_ #1 _ \exp_not:N \keys_bool_set_inverse:n {##1} :c } { #2 \l_keys_current_module_tlp #3 _bool } }{ \exp_not:N \keys_err_use:nn { boolean_expected } {##1} } }, \l_keys_current_path_tlp /.default:n = true } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\keys_choices_create:Nnn} %\begin{macro}[aux]{\keys_choices_create_aux:n} %\begin{macro}[aux]{\keys_choices_create_aux:x} %\begin{macro}{\keys_choice_create:n} % When making multiple choices, the code for each choice is the same. % Only the path and counter need to be altered. % \begin{macrocode} \cs_new:NNn \keys_choices_create:Nnn 3 { \tlp_set_eq:NN \l_keys_choice_path_tlp \l_keys_current_path_tlp \int_zero:N \l_keys_current_choice_int \use:c { keys_choices_create_aux: #1 } {#3} \clist_map_function:nN {#2} \keys_choice_create:n \keys_manage_internal:n { \l_keys_choice_path_tlp /.expects_choice: } } \cs_new:NNn \keys_choices_create_aux:n 1 { \tlp_set:Nn \l_keys_choice_code_tlp { \exp_not:n {#1} } } \cs_new:NNn \keys_choices_create_aux:x 1 { \tlp_set:Nn \l_keys_choice_code_tlp {#1} } \cs_new:NNn \keys_choice_create:n 1 { \int_incr:N \l_keys_current_choice_int \keys_parse_list:n { \l_keys_choice_path_tlp / #1 /.code:x = { \exp_not:n { \int_set:Nn \l_keys_current_choice_int } { \int_use:N \l_keys_current_choice_int } \exp_not:n { \tlp_set:Nn \l_keys_current_choice_tlp } {#1} \l_keys_choice_code_tlp } } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\keys_clear_properties:n} % To avoid problem on redefinition, all properties are removed. % \begin{macrocode} \cs_new:NNn \keys_clear_properties:n 1 { \keys_undefine:n { #1/._boolean } \keys_undefine:n { #1/._cmd:w } \keys_undefine:n { #1/._default_tlp } \keys_undefine:n { #1/._forbidden_bool } \keys_undefine:n { #1/._num_args_tlp } \keys_undefine:n { #1/._required_bool } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_default_add:} % Copies the default value to the current one if needed. % \begin{macrocode} \cs_new:NNn \keys_default_add: 0 { \bool_if:NT \l_keys_no_value_bool { \keys_if_really_exist:nT { \l_keys_current_key_full_tlp /._default_tlp } { \keys_toks_set:Nn \l_keys_current_value_toks { \l_keys_current_key_full_tlp /._default_tlp } \bool_set_false:N \l_keys_no_value_bool } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_find_code_full:} %\begin{macro}{\keys_find_code_name:} % Two functions to find something to process the key value given. First, % a search is made for either a command property for the key, or a % function for the key itself. If that fails, the generic handlers are % used after separating out the key name and key path. % \begin{macrocode} \cs_new:NNn \keys_find_code_full: 0 { \keys_if_cmd_really_exist:nTF { \l_keys_current_key_full_tlp } { \keys_use_cmd:n { \l_keys_current_key_full_tlp } }{ \keys_if_really_exist:nTF { \l_keys_current_key_full_tlp } { \bool_if:NTF \l_keys_no_value_bool { \keys_use:n { \l_keys_current_key_full_tlp } }{ \keys_store:nx { \l_keys_current_key_full_tlp } { \toks_use:N \l_keys_current_value_toks } } }{ \keys_find_code_name: } } } \cs_new:NNn \keys_find_code_name: 0 { \keys_separate_path: \keys_if_cmd_really_exist:nTF { \c_keys_properties_path_tlp / \l_keys_current_key_name_tlp } { \keys_use_cmd:n {\c_keys_properties_path_tlp / \l_keys_current_key_name_tlp} }{ \keys_if_cmd_really_exist:nTF { \l_keys_current_path_tlp / unknown } { \keys_use_cmd:n { \l_keys_current_path_tlp / unknown } }{ \keys_err_use:n { unknown_key } } } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}[TF]{\keys_if_cmd_really_exist:n} % A dedicated check for the |._cmd:w| property key. % \begin{macrocode} \def_long_test_function_new:npn { keys_if_cmd_really_exist:n } #1 { \if_cs_exist:w \c_keys_cs_prefix_tlp #1 /._cmd:w \cs_end: } % \end{macrocode} %\end{macro} % %\begin{macro}[TF]{\keys_if_really_exist:n} % Check if a key exists without adding to the hash table. % \begin{macrocode} \def_long_test_function_new:npn { keys_if_really_exist:n } #1 { \if_cs_exist:w \c_keys_cs_prefix_tlp #1 \cs_end: } % \end{macrocode} %\end{macro} % %\begin{macro}[TF]{\keys_if_value:n} % Used for required and forbidden values. % \begin{macrocode} \def_long_test_function_new:npn { keys_if_value:n } #1 { \if_cs_exist:w \c_keys_cs_prefix_tlp \l_keys_current_key_full_tlp /._ #1 _bool \cs_end: } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_no_value_elt:n} %\begin{macro}{\keys_value_elt:nn} % The two functions passed to \textsf{l3keyval} to actually act on each % key found. % \begin{macrocode} \cs_new:NNn \keys_no_value_elt:n 1 { \bool_set_true:N \l_keys_no_value_bool \keys_process_elt:nn {#1} { } } \cs_new:NNn \keys_value_elt:nn 2 { \bool_set_false:N \l_keys_no_value_bool \keys_process_elt:nn {#1} {#2} } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\keys_parse:n} % The macro used to actually process the key--value input is taken from % \textsf{l3keyval}. There are two possible options, and so at this % stage the macro is simply reserved. % \begin{macrocode} \cs_new:NNn \keys_parse:n 1 {\ERROR} % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_parse_list:n} % All of the management macros call this common parser. First, the key % processing macros are defined, then the appropriate parser is called. % \begin{macrocode} \cs_new:NNn \keys_parse_list:n 1 { \let:NN \KV_key_value_elt:nn \keys_value_elt:nn \let:NN \KV_key_no_value_elt:n \keys_no_value_elt:n \keys_parse:n {#1} } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_path_add:N} %\begin{macro}{\keys_path_add:w} %\begin{macro}[aux]{\keys_path_add_aux:w} % The code to check for a path looks for a |/| at the start of the key. % \begin{macrocode} \cs_new:NNn \keys_path_add:N 1 { \exp_after:NN \keys_path_add:w #1 \q_stop } \cs_new:Npn \keys_path_add:w { \exp_after:NN \peek_meaning:NTF \c_keys_root_tlp { \use_none_delimit_by_q_stop:w }{ \keys_path_add_aux:w } } \cs_new:Npn \keys_path_add_aux:w #1 \q_stop { \tlp_set:Nx \l_keys_current_key_full_tlp {\l_keys_default_path_tlp #1} } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\keys_process_elt:nn} %\begin{macro}[aux]{\keys_process_elt_aux:} % The key processor starts by storing the given key name and value, and % adding a path to the former if necessary. There is then potentially a % need to fill in a default value before checking for required or % forbidden values. % \begin{macrocode} \cs_new:NNn \keys_process_elt:nn 2 { \tlp_set:Nx \l_keys_current_key_full_tlp {#1} \toks_set:Nn \l_keys_current_value_toks {#2} \keys_path_add:N \l_keys_current_key_full_tlp \keys_default_add: \keys_if_value:nTF { required } { \bool_if:NTF \l_keys_no_value_bool { \keys_err_use:n { value_required } }{ \keys_process_elt_aux: } }{ \keys_process_elt_aux: } } \cs_new:NNn \keys_process_elt_aux: 0 { \keys_if_value:nTF { forbidden } { \bool_if:NTF \l_keys_no_value_bool { \keys_find_code_full: }{ \keys_err_use:nn { value_forbidden } { \toks_use:N \l_keys_current_value_toks } } }{ \keys_find_code_full: } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\keys_separate_path:} %\begin{macro}{\keys_separate_path:w} % A simple piece of recursion to find the key name and path. % \begin{macrocode} \cs_new:NNn \keys_separate_path: 0 { \tlp_clear:N \l_keys_current_path_tlp \exp_after:NN \keys_separate_path:w \l_keys_current_key_full_tlp / \q_stop } \cs_new:Npn \keys_separate_path:w / #1 / #2 \q_stop { \tlist_if_empty:nTF {#2} { \tlp_set:Nn \l_keys_current_key_name_tlp {#1} }{ \tlp_put_right:Nn \l_keys_current_path_tlp { / #1 } \keys_separate_path:w /#2 \q_stop } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\keys_set:NN} %\begin{macro}{\keys_set:Nnn} % Storage of data in outside of keys. % \begin{macrocode} \cs_new:NNn \keys_set:NN 2 { \keys_parse_list:n { \c_keys_properties_path_tlp /.code:n = { #1 #2 {##1} } } } \cs_new:NNn \keys_set:Nnn 3 { \keys_parse_list:n { \c_keys_properties_path_tlp /.code:x = { \exp_not:N #1 { #2 \l_keys_current_module_tlp #3 } {##1} } } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\keys_set_eq:nn} % To alias one key to another. % \begin{macrocode} \cs_new:NNn \keys_set_eq:nn 2 { \cs_set_eq:cc { \c_keys_cs_prefix_tlp #1 } { \c_keys_cs_prefix_tlp #2 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_set_cmd:nn} %\begin{macro}{\keys_set_cmd:nx} %\begin{macro}{\keys_set_cmd:nNn} %\begin{macro}{\keys_set_cmd:nNx} %\begin{macro}{._cmd:w} %\begin{macro}{._num_args_tlp} % Creation of the key |._cmd:w| macros happens here. For the % multiple-argument versions, the number of arguments is stored for % use later on. % \begin{macrocode} \cs_new:NNn \keys_set_cmd:nn 2 { \keys_clear_properties:n {#1} \cs_set:cNn { \c_keys_cs_prefix_tlp #1 /._cmd:w } 1 {#2} } \cs_new:NNn \keys_set_cmd:nx 2 { \keys_clear_properties:n {#1} \cs_set:cNx { \c_keys_cs_prefix_tlp #1 /._cmd:w } 1 {#2} } \cs_new:NNn \keys_set_cmd:nNn 3 { \keys_clear_properties:n {#1} \cs_set:cNn { \c_keys_cs_prefix_tlp #1 /._cmd:w } #2 {#3} \keys_store:nn { #1 /._num_args_tlp} {#2} } \cs_new:NNn \keys_set_cmd:nNx 3 { \keys_clear_properties:n {#1} \cs_set:cNx { \c_keys_cs_prefix_tlp #1 /._cmd:w } #2 {#3} \keys_store:nn { #1 /._num_args_tlp} {#2} } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\keys_store:nn} %\begin{macro}{\keys_store:nx} % Direct storage of data in keys. % \begin{macrocode} \cs_new:NNn \keys_store:nn 2 { \tlp_set:cn { \c_keys_cs_prefix_tlp #1 } {#2} } \cs_new:NNn \keys_store:nx 2 { \tlp_set:cx { \c_keys_cs_prefix_tlp #1 } {#2} } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\keys_toks_set:Nn} % Sets a toks to the content of a key. % \begin{macrocode} \cs_new:NNn \keys_toks_set:Nn 2 { \exp_args:NNv \toks_set:Nn #1 { \c_keys_cs_prefix_tlp #2 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_try:} % A function to look for the |._cmd:w| property of a key, and executes % it if found. % \begin{macrocode} \cs_new:NNn \keys_try: 0 { \bool_set_false:N \l_keys_success_bool \keys_if_cmd_really_exist:nT { \l_keys_current_path_tlp } { \bool_set_true:N \l_keys_success_bool \bool_if:NT \l_keys_no_value_bool { \keys_if_really_exist:nT { \l_keys_current_path_tlp /._default_tlp } { \keys_toks_set:Nn \l_keys_current_value_toks { \l_keys_current_path_tlp /._default_tlp } } } \let:NN \l_keys_current_key_full_tlp \l_keys_current_path_tlp \keys_use_cmd:n { \l_keys_current_path_tlp } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_undefine:n} % To remove a key. % \begin{macrocode} \cs_new:NNn \keys_undefine:n 1 { \cs_set_eq:cN { \c_keys_cs_prefix_tlp #1 } \c_undefined } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_use:n} % Use whatever is stored in a key. % \begin{macrocode} \cs_new:NNn \keys_use:n 1 { \use:c { \c_keys_cs_prefix_tlp #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\keys_use_cmd:n} %\begin{macro}{\keys_use_cmd:nn} %\begin{macro}[aux]{\keys_use_cmd_aux:nn} %\begin{macro}[aux]{\keys_use_cmd_aux:w} % Some care is needed when using command keys. For commands with % multiple arguments a check is made in case none were given, and if % so a series of empty values is given instead. % \begin{macrocode} \cs_new:NNn \keys_use_cmd:n 1 { \exp_args:Nno \keys_use_cmd:nn {#1} { \toks_use:N \l_keys_current_value_toks } } \cs_new:NNn \keys_use_cmd:nn 2 { \keys_if_really_exist:nTF { #1 /._num_args_tlp } { \keys_use_cmd_aux:nn {#1} {#2} }{ \keys_use:n {#1/._cmd:w} {#2} } } \cs_new:NNn \keys_use_cmd_aux:nn 2 { \tlist_if_empty:nTF {#2} { \cs_set:NNn \keys_use_cmd_aux:w 0 { \keys_use:n { #1 /._cmd:w } } \exp_after:NN \exp_after:NN \exp_after:NN \keys_use_cmd_aux:w \cs:w c_keys_ \keys_use:n { #1 /._num_args_tlp } _empty_tlp \cs_end: }{ \keys_use:n { #1 /._cmd:w } #2 } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\subsubsection{Error handling code} % % The \LaTeX3 approach is to have named errors called separately and % defined separately. To make life a little easier here, some custom % functions are used to keep repetition down. % %\begin{macro}{\keys_err_new:nNnnn} % To create new error messages, a utility function is created. % \begin{macrocode} \cs_new:NNn \keys_err_new:nNnnn 1 { \tlp_new:cn { l_keys_err_#1_tlp } {#1} \exp_args:NNc \err_interrupt_new:NNNnnn \c_keys_err_tlp { l_keys_err_#1_tlp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\l_keys_err_unknown_key_tlp} %\begin{macro}{\l_keys_err_value_ignored_tlp} %\begin{macro}{\l_keys_err_value_required_tlp} %\begin{macro}{\l_keys_err_def_x_args_tlp} %\begin{macro}{\l_keys_err_boolean_expected_tlp} %\begin{macro}{\l_keys_err_not_boolean_tlp} %\begin{macro}{\l_keys_err_unknown_choice_tlp} % Text for package error messages is stored in \texttt{keys3.err}. The % error names do not need \cs{keys} at the start as they have to be in % the file. % \begin{macrocode} \err_file_new:Nn \c_keys_err_tlp { keys3.err } \keys_err_new:nNnnn { unknown_key } 1 { The~key~`#1'~is~unknown~and~is~being~ignored } { \err_help_return_or_X: } { } \keys_err_new:nNnnn { value_required } 1 { The~key~`#1'~requires~a~value~and~is~being~ignored } { \err_help_return_or_X: } { } \keys_err_new:nNnnn { value_forbidden } 2 { The~key~`#1'~cannot~taken~a~value:~the~given~input~\iow_newline: ~\text_put_sp:~\text_put_sp:`#2'~is~being~ignored } { \err_help_return_or_X: } { } \keys_err_new:nNnnn { boolean_expected } 2 { Key~`#1'~takes~the~Boolean~values~`true'~and~\iow_newline:~ \text_put_sp:~\text_put_sp:`false'~only.~The~given~value~`#2'~is~ being~ignored } { \err_help_return_or_X: } { } \keys_err_new:nNnnn { not_boolean } 1 { Key~`#1'~is~not~a~Boolean~key:~you~cannot~create~a~complement } { \err_help_return_or_X: } { } \keys_err_new:nNnnn { unknown_choice } 2 { Choice~`#2'~unknown~for~key~`#1':~\iow_newline:~\text_put_sp:~ \text_put_sp:~the~key~is~being~ignored } { \err_help_return_or_X: } { } \err_file_close:N \c_keys_err_tlp % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\begin{macro}{\keys_err_use:nw} %\begin{macro}{\keys_err_use:n} %\begin{macro}{\keys_err_use:nn} % Utilities for using errors: the first function is \texttt{nw} as it % may need one or two arguments in addition to the \texttt{n}. % \begin{macrocode} \cs_new:NNn \keys_err_use:nw 1 { \exp_args:NNc \err_interrupt:NNw \c_keys_err_tlp { l_keys_err_#1_tlp } } \cs_new:NNn \keys_err_use:n 1 { \keys_err_use:nw {#1} \l_keys_current_key_full_tlp } \cs_new:NNn \keys_err_use:nn 2 { \keys_err_use:nw {#1} \l_keys_current_key_full_tlp {#2} } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} % % %\subsubsection{Property definitions} % %\begin{macro}{.code:n} %\begin{macro}{.code:Nn} % The \texttt{.code:n} and \texttt{.code:Nn} properties have to be % defined directly. % \begin{macrocode} \keys_set_cmd:nn { \c_keys_properties_path_tlp /.code:n } { \keys_set_cmd:nn { \l_keys_current_path_tlp } {#1} } \keys_set_cmd:nNn { \c_keys_properties_path_tlp /.code:Nn } 2 { \keys_set_cmd:nNn { \l_keys_current_path_tlp } #1 {#2} } % \end{macrocode} %\end{macro} %\end{macro} % % The remaining definitions can all be carried out using the package % itself. As category codes and spaces are not an issue here, the % |_quick| version of \cs{keys_manage} is used. % % First, an error is created for unknown keys: this is done early to % catch any internal errors. % \begin{macrocode} \keys_manage_quick:n { \c_keys_errors_path_tlp /unknown/.code:n = { \keys_err_use:n { unknown_key } } } % \end{macrocode} % %\begin{macro}{.code:x} %\begin{macro}{.code:Nx} % Fully-expanded versions of the basic \texttt{.code} properties. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.code:x/.code:n = { \keys_set_cmd:nx { \l_keys_current_path_tlp } {#1} }, \c_keys_properties_path_tlp /.code:Nx/.code:Nn = 2 { \keys_set_cmd:nNx { \l_keys_current_path_tlp } {#1} {#2} } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{.cd:} % The change-directory property simply alters the value of the default % path. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.cd:/.code:n = { \tlp_set:Nx \l_keys_default_path_tlp { \l_keys_current_path_tlp / } } } % \end{macrocode} %\end{macro} % %\begin{macro}{.value_forbidden:} %\begin{macro}{._required_bool} %\begin{macro}{.value_required:} %\begin{macro}{._forbidden_bool} % Values are required or forbidden by creating the appropriate flags. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.value_required:/.code:n = { \keys_bool_new:n { \l_keys_current_path_tlp /._required_bool } \keys_undefine:n { \l_keys_current_path_tlp /._forbidden_bool } }, \c_keys_properties_path_tlp /.value_forbidden:/.code:n = { \keys_bool_new:n { \l_keys_current_path_tlp /._forbidden_bool } \keys_undefine:n { \l_keys_current_path_tlp /._required_bool } } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{.default:n} %\begin{macro}{._default_tlp} % The default value for a key is stored in the |._default_tlp| % private property. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.default:n/.code:n = { \keys_store:nn { \l_keys_current_path_tlp /._default_tlp } {#1} \keys_undefine:n { \l_keys_current_path_tlp /._required_bool } } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{.tlp_set:N} %\begin{macro}{.tlp_set_x:N} %\begin{macro}{.tlp_gset:N} %\begin{macro}{.tlp_gset_x:N} % Storage of the value in a tlp, as given or expanded, local or global. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.tlp_set:N/.code:n = { \keys_set:NN \tlp_set:Nn #1 }, \c_keys_properties_path_tlp /.tlp_set_x:N/.code:n = { \keys_set:NN \tlp_set:Nx #1 }, \c_keys_properties_path_tlp /.tlp_gset:N/.code:n = { \keys_set:NN \tlp_gset:Nn #1 }, \c_keys_properties_path_tlp /.tlp_gset_x:N/.code:n = { \keys_set:NN \tlp_gset:Nx #1 } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{.int_set:N} %\begin{macro}{.int_gset:N} %\begin{macro}{.skip_set:N} %\begin{macro}{.skip_gset:N} %\begin{macro}{.toks_set:N} %\begin{macro}{.toks_gset:N} % For int, skip and toks storage, no expansion to worry about. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.int_set:N/.code:n = { \keys_set:NN \int_set:Nn #1 }, \c_keys_properties_path_tlp /.int_gset:N/.code:n = { \keys_set:NN \int_gset:Nn #1 }, \c_keys_properties_path_tlp /.skip_set:N/.code:n = { \keys_set:NN \skip_set:Nn #1 }, \c_keys_properties_path_tlp /.skip_gset:N/.code:n = { \keys_set:NN \skip_gset:Nn #1 }, \c_keys_properties_path_tlp /.toks_set:N/.code:n = { \keys_set:NN \toks_set:Nn #1 }, \c_keys_properties_path_tlp /.toks_gset:N/.code:n = { \keys_set:NN \toks_gset:Nn #1 } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % % The prefix for the current module is needed when storing material % by csname. % \begin{macrocode} \keys_manage_quick:n { \c_keys_utilities_path_tlp /current_module:n/.code:n = { \tlist_if_empty:nTF {#1} { \tlp_clear:N \l_keys_current_module_tlp }{ \tlp_set:Nn \l_keys_current_module_tlp { #1 _ } } } } % \end{macrocode} % %\begin{macro}{.tlp_set:n} %\begin{macro}{.tlp_set_x:n} %\begin{macro}{.tlp_gset:n} %\begin{macro}{.tlp_gset_x:n} %\begin{macro}{.int_set:n} %\begin{macro}{.int_gset:n} %\begin{macro}{.skip_set:n} %\begin{macro}{.skip_gset:n} %\begin{macro}{.toks_set:n} %\begin{macro}{.toks_gset:n} % With the module available, storage properties that only need the % unique part of the variable name are created. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.tlp_set:n/.code:n = { \keys_set:Nnn \tlp_set:cn { l_ } { #1 _tlp } }, \c_keys_properties_path_tlp /.tlp_set_x:n/.code:n = { \keys_set:Nnn \tlp_set:cx { l_ } { #1 _tlp } }, \c_keys_properties_path_tlp /.tlp_gset:n/.code:n = { \keys_set:Nnn \tlp_gset:cn { g_ } { #1 _tlp } }, \c_keys_properties_path_tlp /.tlp_gset_x:n/.code:n = { \keys_set:Nnn \tlp_gset:cx { g_ } { #1 _tlp } }, \c_keys_properties_path_tlp /.int_set:n/.code:n = { \keys_set:Nnn \int_set:cn { l_ } { #1 _int } }, \c_keys_properties_path_tlp /.int_gset:n/.code:n = { \keys_set:Nnn \int_gset:cn { g_ } { #1 _int } }, \c_keys_properties_path_tlp /.skip_set:n/.code:n = { \keys_set:Nnn \skip_set:cn { l_ } { #1 _skip } }, \c_keys_properties_path_tlp /.skip_gset:n/.code:n = { \keys_set:Nnn \skip_gset:cn { g_ } { #1 _skip } }, \c_keys_properties_path_tlp /.toks_set:n/.code:n = { \keys_set:Nnn \toks_set:cn { l_ } { #1 _toks } }, \c_keys_properties_path_tlp /.toks_gset:n/.code:n = { \keys_set:Nnn \toks_gset:cn { g_ } { #1 _toks } }, } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{.bool_set:N} %\begin{macro}{.bool_gset:N} %\begin{macro}{.bool_set:n} %\begin{macro}{.bool_gset:n} % The properties for switches look similar, although internally things % are rather different. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.bool_set:N/.code:n = { \keys_bool_set:nN { set } #1 }, \c_keys_properties_path_tlp /.bool_gset:N/.code:n = { \keys_bool_set:nN { gset } #1 }, \c_keys_properties_path_tlp /.bool_set:n/.code:n = { \keys_bool_set:nnn { set } { l_ } { #1 } }, \c_keys_properties_path_tlp /.bool_gset:n/.code:n = { \keys_bool_set:nnn { gset } { g_ } { #1 } }, } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{.bool_set_inverse:N} %\begin{macro}{.bool_gset_inverse:N} %\begin{macro}{.bool_set_inverse:n} %\begin{macro}{.bool_gset_inverse:n} % The inverse versions of the switches are handled in the same way % here. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.bool_set_inverse:N/.code:n = { \keys_bool_set_inverse:nN { set } #1 }, \c_keys_properties_path_tlp /.bool_gset_inverse:N/.code:n = { \keys_bool_set_inverse:nN { gset } #1 }, \c_keys_properties_path_tlp /.bool_set_inverse:n/.code:n = { \keys_bool_set_inverse:nnn { set } { l_ } { #1 } }, \c_keys_properties_path_tlp /.bool_gset_inverse:n/.code:n = { \keys_bool_set_inverse:nnn { gset } { g_ } { #1 } }, } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{.expects_choice:} % Multiple choice keys are created by searching sub-keys. So the % code to make a key into a multiple choice is quite simple. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.expects_choice:/.code:n = { \keys_manage_internal:n { \l_keys_current_path_tlp /.cd:, .code:n = { \tlp_set:Nn \l_keys_current_choice_tlp {##1} \int_zero:N \l_keys_current_choice_int \exp_args:No \keys_parse_list:n { \l_keys_current_key_full_tlp / ##1 } }, unknown/.code:n = { \keys_err_use:nn { unknown_choice } { \l_keys_current_key_name_tlp } } } }, } % \end{macrocode} %\end{macro} %\begin{macro}{.create_choices:nn} %\begin{macro}{.create_choices:nx} % Creating choices as a block. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.create_choices:nn/.code:Nn = 2 { \keys_choices_create:Nnn n {#1} {#2} }, \c_keys_properties_path_tlp /.create_choices:nx/.code:Nn = 2 { \keys_choices_create:Nnn x {#1} {#2} }, } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{.use_keys:n} %\begin{macro}{.use_keys:x} %\begin{macro}{.use_keys:Nn} %\begin{macro}{.use_keys:Nx} % Keys calling other keys is actually quite easy. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.use_keys:n/.code:n = { \keys_manage_internal:n { \l_keys_current_path_tlp /.code:n = { \keys_parse_list:n {#1} } } }, \c_keys_properties_path_tlp /.use_keys:x/.code:n = { \keys_manage_internal:n { \l_keys_current_path_tlp /.code:x = { \exp_not:N \keys_parse_list:n {#1} } } }, \c_keys_properties_path_tlp /.use_keys:Nn/.code:Nn = 2 { \keys_manage_internal:n { \l_keys_current_path_tlp /.code:Nn = #1 { \keys_parse_list:n {#2} } } }, \c_keys_properties_path_tlp /.use_keys:Nx/.code:Nn = 2 { \keys_manage_internal:n { \l_keys_current_path_tlp /.code:Nx = #1 { \exp_not:N \keys_parse_list:n {#2} } } } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{.try:n} %\begin{macro}{.retry:n} % For attempting to set keys without assuming they exist. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.try:n/.code:n = { \keys_try: }, \c_keys_properties_path_tlp /.retry:n/.code:n = { \bool_if:NF \l_keys_success_bool { \keys_try: } } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{.show_code:} %\begin{macro}{.show_key:} % Finally, two keys for debugging problems. % \begin{macrocode} \keys_manage_quick:n { \c_keys_properties_path_tlp /.show_code:/.code:n = { \cs_show:c { \c_keys_cs_prefix_tlp \l_keys_current_path_tlp /._cmd:w } }, \c_keys_properties_path_tlp /.show_key:/.code:n = { \cs_show:c { \c_keys_cs_prefix_tlp \l_keys_current_path_tlp } } } % % \end{macrocode} %\end{macro} %\end{macro} % %\Finale