% \iffalse meta-comment
%
%% File: l3keys.dtx Copyright (C) 2006-2012 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 4256 2012-11-02 08:47:41Z joseph $
  {L3 Experimental key-value interfaces}
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \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:
%</package>
%    \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
  {
    \str_if_eq_x:nnTF
      { \exp_not:N \q_nil } { \exp_not:o { \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 .<property> \\
    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
%</deprecated>
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% 
% Internal material for removal by 2012-12-31.
% 
%    \begin{macrocode}
\cs_new_eq:NN \c_keys_code_root_tl \c__keys_code_root_tl
%    \end{macrocode}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
%\end{implementation}
%
%\PrintIndex
