% \iffalse %% File: l3keyval.dtx Copyright (C) 2006-2009 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 ``expl3 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 LaTeX Project Team. %% %% ----------------------------------------------------------------------- %<*driver|package> \RequirePackage{l3names} % %\fi \GetIdInfo$Id: l3keyval.dtx 1890 2010-04-11 03:37:15Z will $ {L3 Experimental keyval processing} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass[full]{l3doc} \begin{document} \DocInput{l3keyval.dtx} \end{document} % % \fi % % % \title{The \textsf{l3keyval} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Key-value parsing} % \author{\Team} % \date{\filedate} % \maketitle % % \begin{documentation} % % 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. % % This module provides the low-level machinery for processing arbitrary % key--value lists. The \pkg{l3keys} module provides a higher-level % interface for managing run-time settings using key--value input, % while other parts of \LaTeX3 also use key--value input based on % \pkg{l3keyval} (for example the \pkg{xtemplate} module). % %\section{Features of \pkg{l3keyval}} % % As \pkg{l3keyval} is a low-level module, its functions are % restricted to converting a \meta{keyval list} into keys and values % for further processing. Each key and value (or key alone) % has to be processed further by a function provided when % \pkg{l3keyval} is called. Typically, this will be \emph{via} % one of the \cs{KV_process\ldots} functions: %\begin{verbatim} % \KV_process_space_removal_sanitize:NNn % \my_processor_function_one:n % \my_processor_function_two:nn % { } %\end{verbatim} % The two processor functions here handle the cases where there is % only a key, and where there is both a key and value, respectively. % % \pkg{l3keyval} parses key--value lists in a manner that does not % double "#" tokens or expand any input. The module has processor % functions which will sanitize the category codes of \texttt{=} % and \texttt{,} tokens (for use in the document body) as well % as faster versions which do not do this (for use inside code % blocks). Spaces can be removed from each end of the key and % value (again for the document body), again with faster code % to be used where this is not necessary. Values which are wrapped % in braces will have exactly one set removed, meaning that %\begin{verbatim} % key = {value here}, %\end{verbatim} % and %\begin{verbatim} % key = value here, %\end{verbatim} % are treated as identical (assuming that space removal is in force). % \pkg{l3keyval} % %\section{Functions for keyval processing} % % The \pkg{l3keyval} module should be accessed \emph{via} a small % set of external functions. These correctly set up the module % internals for use by other parts of \LaTeX3. % % In all cases, two functions have to be supplied by the programmer % to apply to the items from the after \pkg{l3keyval} % has separated out the entries. The first function should take % one argument, and will receive the names of keys for which no % value was supplied. The second function should take two arguments: % a key name and the associated value. % %\begin{function}{\KV_process_space_removal_sanitize:NNn} % \begin{syntax} % "\KV_process_space_removal_sanitize:NNn" % ~~~~ \Arg{keyval list} % \end{syntax} % Parses the splitting it into keys and associated % values. Spaces are removed from the ends of both the key and % value by this function, and the category codes of non-braced % \texttt{=} and \texttt{,} tokens are normalised so that % parsing is `category code safe'. After parsing is completed, % is used to process keys without values and % deals with keys which have associated values. %\end{function} % %\begin{function}{\KV_process_space_removal_no_sanitize:NNn} % \begin{syntax} % "\KV_process_space_removal_no_sanitize:NNn" % ~~~~ \Arg{keyval list} % \end{syntax} % Parses the splitting it into keys and associated % values. Spaces are removed from the ends of both the key and % value by this function, but category codes are not normalised. % After parsing is completed, is used to process keys % without values and deals with keys which have % associated values. %\end{function} % %\begin{function}{\KV_process_no_space_removal_no_sanitize:NNn} % \begin{syntax} % "\KV_process_no_space_removal_no_sanitize:NNn" % ~~~~ \Arg{keyval list} % \end{syntax} % Parses the splitting it into keys and associated % values. Spaces are \emph{not} removed from the ends of % the key and value, and category codes are \emph{not} normalised. % After parsing is completed, is used to process keys % without values and deals with keys which have % associated values. %\end{function} % % \begin{variable}{\l_KV_remove_one_level_of_braces_bool} % This boolean controls whether or not one level of braces is % stripped from the key and value. The default value for this % boolean is \texttt{true} so that exactly one level of braces is % stripped. For certain applications it is desirable to keep the % braces in which case the programmer just has to set the boolean % false temporarily. Only applicable when spaces are being % removed. % \end{variable} % %\section{Internal functions} % % The remaining functions provided by \pkg{l3keyval} do not % have any protection for nesting of one call to the module % inside another. They should therefore not be called directly by % other modules. % % \begin{function}{\KV_parse_no_space_removal_no_sanitize:n} % \begin{syntax} % "\KV_parse_no_space_removal_no_sanitize:n" \Arg{keyval list} % \end{syntax} % Parses the keys and values, passing the results to % \cs{KV_key_no_value_elt:n} and \cs{KV_key_value_elt:nn} as % appropriate. Spaces are not removed in the parsing process and % the category codes of \texttt{=} and \texttt{,} are not % normalised. % \end{function} % % \begin{function}{\KV_parse_space_removal_no_sanitize:n} % \begin{syntax} % "\KV_parse_space_removal_no_sanitize:n" \Arg{keyval list} % \end{syntax} % Parses the keys and values, passing the results to % \cs{KV_key_no_value_elt:n} and \cs{KV_key_value_elt:nn} as % appropriate. Spaces are removed in the parsing process from the % ends of the key and value, but the category codes of \texttt{=} % and \texttt{,} are not normalised. % \end{function} % % \begin{function}{\KV_parse_space_removal_sanitize:n} % \begin{syntax} % "\KV_parse_space_removal_sanitize:n" \Arg{keyval list} % \end{syntax} % Parses the keys and values, passing the results to % \cs{KV_key_no_value_elt:n} and \cs{KV_key_value_elt:nn} as % appropriate. Spaces are removed in the parsing process from the % ends of the key and value and the category codes of \texttt{=} % and \texttt{,} are normalised at the outer level % (\emph{i.e}.~only unbraced tokens are affected). % \end{function} % % \begin{function}{ % \KV_key_no_value_elt:n | % \KV_key_value_elt:nn % } % \begin{syntax} % "\KV_key_no_value_elt:n" \Arg{key} \\ % "\KV_key_value_elt:n" \Arg{key} \Arg{value} % \end{syntax} % Used by \cs{KV_parse\ldots} functions to further process % keys with no values and keys with values, respectively. The % standard definitions are error functions: the programmer should % provide appropriate definitions for both at point of use. % \end{function} % %\section{Variables and constants} % %\begin{variable}{\c_KV_single_equal_sign_tl} % Constant token list to make finding \texttt{=} faster. %\end{variable} % %\begin{variable}{ % \l_KV_tmpa_tl | % \l_KV_tmpb_tl %} % Scratch token lists. %\end{variable} % %\begin{variable}{ % \l_KV_parse_tl | % \l_KV_currkey_tl | % \l_KV_currval_tl %} % Token list variables for various parts of the parsed input. %\end{variable} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3keyval} implementation} % % \begin{function}{ \KV_sanitize_outerlevel_active_equals:N | % \KV_sanitize_outerlevel_active_commas:N } % \begin{syntax} % "\KV_sanitize_outerlevel_active_equals:N" % \end{syntax} % Replaces catcode other "=" and "," within a with active characters. % \end{function} % % \begin{function}{ \KV_remove_surrounding_spaces:nw | % \KV_remove_surrounding_spaces_auxi:w / (EXP) } % \begin{syntax} % % "\KV_remove_surrounding_spaces:nw" "\q_nil" % "\KV_remove_surrounding_spaces_auxi:w" \verb*" Q"$\sb3$ % \end{syntax} % Removes a possible % leading space plus a possible ending space from a . % The first version (which is not used in the code) stores it in . % \end{function} % % \begin{function}{ \KV_add_value_element:w | \KV_set_key_element:w } % \begin{syntax} % "\KV_set_key_element:w" "\q_nil" % "\KV_add_value_element:w" "\q_stop" "\q_nil" % \end{syntax} % Specialised functions to strip spaces from their input and set the % token registers "\l_KV_currkey_tl" or "\l_KV_currval_tl" % respectively. % \end{function} % % \begin{function}{ \KV_split_key_value_current:w | % \KV_split_key_value_space_removal:w | % \KV_split_key_value_space_removal_detect_error:wTF | % \KV_split_key_value_no_space_removal:w } % \begin{syntax} % "\KV_split_key_value_current:w" \dots % \end{syntax} % These functions split keyval lists into chunks depending which % sanitising method is being used. "\KV_split_key_value_current:w" is "\cs_set_eq:NN" % to whichever is appropriate. % \end{function} % % \subsection{Module code} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \package_check_loaded_expl: % %<*initex|package> % \end{macrocode} % % % \begin{macro}{\l_KV_tmpa_tl} % \begin{macro}{\l_KV_tmpb_tl} % \begin{macro}{\c_KV_single_equal_sign_tl} % Various useful things. % \begin{macrocode} \tl_new:N \l_KV_tmpa_tl \tl_new:N \l_KV_tmpb_tl \tl_const:Nn \c_KV_single_equal_sign_tl { = } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\l_KV_parse_tl} % \begin{macro}{\l_KV_currkey_tl} % \begin{macro}{\l_KV_currval_tl} % Some more useful things. % \begin{macrocode} \tl_new:N \l_KV_parse_tl \tl_new:N \l_KV_currkey_tl \tl_new:N \l_KV_currval_tl % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % %\begin{macro}{\l_KV_level_int} % This is used to track how deeply nested calls to the keyval processor % are, so that the correct functions are always in use. % \begin{macrocode} \int_new:N \l_KV_level_int % \end{macrocode} %\end{macro} % % \begin{macro}{\l_KV_remove_one_level_of_braces_bool} % A boolean to control % \begin{macrocode} \bool_new:N \l_KV_remove_one_level_of_braces_bool \bool_set_true:N \l_KV_remove_one_level_of_braces_bool % \end{macrocode} % \end{macro} % %\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} %\begin{macro}[aux]{\KV_process_aux:NNNn} % The wrapper function takes care of assigning the appropriate % \texttt{elt} functions before and after the parsing step. In that % way there is no danger of a mistake with the wrong functions being % used. % \begin{macrocode} \cs_new_protected_nopar:Npn \KV_process_space_removal_sanitize:NNn { \KV_process_aux:NNNn \KV_parse_space_removal_sanitize:n } \cs_new_protected_nopar:Npn \KV_process_space_removal_no_sanitize:NNn { \KV_process_aux:NNNn \KV_parse_space_removal_no_sanitize:n } \cs_new_protected_nopar:Npn \KV_process_no_space_removal_no_sanitize:NNn { \KV_process_aux:NNNn \KV_parse_no_space_removal_no_sanitize:n } \cs_new_protected:Npn \KV_process_aux:NNNn #1#2#3#4 { \cs_set_eq:cN { KV_key_no_value_elt_ \int_use:N \l_KV_level_int :n } \KV_key_no_value_elt:n \cs_set_eq:cN { KV_key_value_elt_ \int_use:N \l_KV_level_int :nn } \KV_key_value_elt:nn \cs_set_eq:NN \KV_key_no_value_elt:n #2 \cs_set_eq:NN \KV_key_value_elt:nn #3 \int_incr:N \l_KV_level_int #1 {#4} \int_decr:N \l_KV_level_int \cs_set_eq:Nc \KV_key_no_value_elt:n { KV_key_no_value_elt_ \int_use:N \l_KV_level_int :n } \cs_set_eq:Nc \KV_key_value_elt:nn { KV_key_value_elt_ \int_use:N \l_KV_level_int :nn } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % % \begin{macro}{\KV_sanitize_outerlevel_active_equals:N} % \begin{macro}{\KV_sanitize_outerlevel_active_commas:N} % Some functions for sanitizing top level equals and commas. Replace % |=|$\sb{13}$ and |,|$\sb{13}$ with |=|$\sb{12}$ and |,|$\sb{12}$ % resp. % \begin{macrocode} \group_begin: \char_set_catcode:nn{`\=}{13} \char_set_catcode:nn{`\,}{13} \char_set_lccode:nn{`\8}{`\=} \char_set_lccode:nn{`\9}{`\,} \tl_to_lowercase:n{\group_end: \cs_new_protected_nopar:Npn \KV_sanitize_outerlevel_active_equals:N #1{ \tl_replace_all_in:Nnn #1 = 8 } \cs_new_nopar:Npn \KV_sanitize_outerlevel_active_commas:N #1{ \tl_replace_all_in:Nnn #1 , 9 } } % \end{macrocode} % \end{macro} % \end{macro} % % % % % % \begin{macro}{\KV_remove_surrounding_spaces:nw} % \begin{macro}{\KV_remove_surrounding_spaces_auxi:w} % \begin{macro}[aux]{\KV_remove_surrounding_spaces_auxii:w} % \begin{macro}{\KV_set_key_element:w} % \begin{macro}{\KV_add_value_element:w} % The macro |\KV_remove_surrounding_spaces:nw| removes a possible % leading space plus a possible ending space from its second % argument and stores it in the token register |#1|. % % Based on Around the Bend No.~15 but with some enhancements. For % instance, this definition is purely expandable. % % We use a funny token |Q|$\sb3$ as a delimiter. % \begin{macrocode} \group_begin: \char_set_catcode:nn{`\Q}{3} % \end{macrocode} % \begin{macrocode} \cs_new:Npn\KV_remove_surrounding_spaces:nw#1#2\q_nil{ % \end{macrocode} % The idea in this processing is to use a Q with strange catcode to % remove a trailing space. But first, how to get this expansion going? % % If you have read the fine print in the \textsf{l3expan} module, % you'll know that the |f| type expansion will expand until the first % non-expandable token is seen and if this token is a space, it will % be gobbled. Sounds useful for removing a leading space but we also % need to make sure that it does nothing but removing that space! % Therefore we prepend the argument to be trimmed with an % |\exp_not:N|. Now why is that? |\exp_not:N| in itself is an % expandable command so will allow the |f| expansion to continue. If % the first token in the argument to be trimmed is a space, it will be % gobbled and the expansion stop. If the first token isn't a space, % the |\exp_not:N| turns it temporarily into |\scan_stop:| which is % unexpandable. The processing stops but the token following directly % after |\exp_not:N| is now back to normal. % % The function here allows you to insert arbitrary functions in the % first argument but they should all be with an |f| type % expansion. For the application in this module, we use % |\tl_set:Nf|. % % Once the expansion has been kick-started, we apply % |\KV_remove_surrounding_spaces_auxi:w| to the replacement text of % |#2|, adding a leading |\exp_not:N|. Note that no braces are % stripped off of the original argument. % \begin{macrocode} #1{\KV_remove_surrounding_spaces_auxi:w \exp_not:N#2Q~Q} } % \end{macrocode} % |\KV_remove_surrounding_spaces_auxi:w| removes a trailing space if % present, then calls |\KV_remove_surrounding_spaces_auxii:w| to clean % up any leftover bizarre Qs. In order for % |\KV_remove_surrounding_spaces_auxii:w| to work properly we need to % put back a Q first. % \begin{macrocode} \cs_new:Npn\KV_remove_surrounding_spaces_auxi:w#1~Q{ \KV_remove_surrounding_spaces_auxii:w #1 Q } % \end{macrocode} % Now all that is left to do is remove a leading space which should be % taken care of by the function used to initiate the expansion. Simply % return the argument before the funny Q. % \begin{macrocode} \cs_new:Npn\KV_remove_surrounding_spaces_auxii:w#1Q#2{#1} % \end{macrocode} % % Here are some specialized versions of the above. They do exactly % what we want in one go. First trim spaces from the value and then % put the result surrounded in braces onto |\l_KV_parse_tl|. % \begin{macrocode} \cs_new_protected:Npn\KV_add_value_element:w\q_stop#1\q_nil{ \tl_set:Nf\l_KV_currval_tl { \KV_remove_surrounding_spaces_auxi:w \exp_not:N#1Q~Q } \tl_put_right:No\l_KV_parse_tl{ \exp_after:wN { \l_KV_currval_tl } } } % \end{macrocode} % When storing the key we firstly remove spaces plus the prepended % |\q_no_value|. % \begin{macrocode} \cs_new_protected:Npn\KV_set_key_element:w#1\q_nil{ \tl_set:Nf\l_KV_currkey_tl { \exp_last_unbraced:NNo \KV_remove_surrounding_spaces_auxi:w \exp_not:N \use_none:n #1Q~Q } % \end{macrocode} % Afterwards we gobble an extra level of braces if that's what we are % asked to do. % \begin{macrocode} \bool_if:NT \l_KV_remove_one_level_of_braces_bool { \exp_args:NNo \tl_set:No \l_KV_currkey_tl { \exp_after:wN \KV_add_element_aux:w \l_KV_currkey_tl \q_nil } } } \group_end: % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux]{\KV_add_element_aux:w} % A helper function for fixing braces around keys and values. % \begin{macrocode} \cs_new:Npn \KV_add_element_aux:w#1\q_nil{#1} % \end{macrocode} % \end{macro} % % % Parse a list of keyvals, put them into list form with entries like % |\KV_key_no_value_elt:n{key1}| and |\KV_key_value_elt:nn{key2}{val2}|. % % \begin{macro}[aux]{\KV_parse_sanitize_aux:n} % The slow parsing algorithm sanitizes active commas and equal signs % at the top level first. Then uses |#1| as inspector of each % element in the comma list. % \begin{macrocode} \cs_new_protected:Npn \KV_parse_sanitize_aux:n #1 { \group_begin: \tl_clear:N \l_KV_parse_tl \tl_set:Nn \l_KV_tmpa_tl {#1} \KV_sanitize_outerlevel_active_equals:N \l_KV_tmpa_tl \KV_sanitize_outerlevel_active_commas:N \l_KV_tmpa_tl \exp_last_unbraced:NNV \KV_parse_elt:w \q_no_value \l_KV_tmpa_tl , \q_nil , % \end{macrocode} % We evaluate the parsed keys and values outside the group so the % token register is restored to its previous value. % \begin{macrocode} \exp_after:wN \group_end: \l_KV_parse_tl } % \end{macrocode} % \end{macro} % % % \begin{macro}[aux]{\KV_parse_no_sanitize_aux:n} % Like above but we don't waste time sanitizing. This is probably % the one we will use for preamble parsing where catcodes of |=| and % |,| are as expected! % \begin{macrocode} \cs_new_protected:Npn \KV_parse_no_sanitize_aux:n #1{ \group_begin: \tl_clear:N \l_KV_parse_tl \KV_parse_elt:w \q_no_value #1 , \q_nil , \exp_after:wN \group_end: \l_KV_parse_tl } % \end{macrocode} % \end{macro} % % % \begin{macro}[aux]{\KV_parse_elt:w} % This function will always have a |\q_no_value| stuffed in as the % rightmost token in |#1|. In case there was a blank entry in the % comma separated list we just run it again. The |\use_none:n| makes % sure to gobble the quark |\q_no_value|. A similar test is made to % check if we hit the end of the recursion. % \begin{macrocode} \cs_set:Npn \KV_parse_elt:w #1,{ \tl_if_blank:oTF{\use_none:n #1} { \KV_parse_elt:w \q_no_value } { \quark_if_nil:oF {\use_ii:nn #1 } % \end{macrocode} % If we made it to here we can start parsing the key and value. When % done try, try again. % \begin{macrocode} { \KV_split_key_value_current:w #1==\q_nil \KV_parse_elt:w \q_no_value } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\KV_split_key_value_current:w} % The function called to split the keys and values. % \begin{macrocode} \cs_new:Npn \KV_split_key_value_current:w {\ERROR} % \end{macrocode} % \end{macro} % % We provide two functions for splitting keys and values. The reason % being that most of the time, we should probably be in the special % coding regime where spaces are ignored. Hence it makes no sense to % spend time searching for extra space tokens and we can do the % settings directly. When comparing these two versions (neither doing % any sanitizing) the |no_space_removal| version is more than 40\% % faster than |space_removal|. % % It is up to functions like |\DeclareTemplate| to check which catcode % regime is active and then pick up the version best suited for it. % % % % \begin{macro}{\KV_split_key_value_space_removal:w} % \begin{macro}{\KV_split_key_value_space_removal_detect_error:wTF} % \begin{macro}[aux]{\KV_split_key_value_space_removal_aux:w} % The code below removes extraneous spaces around the keys and % values plus one set of braces around the entire value. % % Unlike the version to be used when spaces are ignored, this one % only grabs the key which is everything up to the first = and save % the rest for closer inspection. Reason is that if a user has % entered |mykey={{myval}},| then the outer braces have already been % removed before we even look at what might come after the key. So % this is slightly more tedious (but only slightly) but at least it % always removes only one level of braces. % \begin{macrocode} \cs_new_protected:Npn \KV_split_key_value_space_removal:w #1 = #2\q_nil{ % \end{macrocode} % First grab the key. % \begin{macrocode} \KV_set_key_element:w#1\q_nil % \end{macrocode} % Then we start checking. If only a key was entered, |#2| contains % |=| and nothing else, so we test for that first. % \begin{macrocode} \tl_set:Nn\l_KV_tmpa_tl{#2} \tl_if_eq:NNTF\l_KV_tmpa_tl\c_KV_single_equal_sign_tl % \end{macrocode} % Then we just insert the default key. % \begin{macrocode} { \tl_put_right:No\l_KV_parse_tl{ \exp_after:wN \KV_key_no_value_elt:n \exp_after:wN {\l_KV_currkey_tl} } } % \end{macrocode} % Otherwise we must take a closer look at what is left. The remainder % of the original list up to the comma is now stored in |#2| plus an % additional |==|, which wasn't gobbled during the initial reading of % arguments. If there is an error then we can see at least one more % |=| so we call an auxiliary function to check for this. % \begin{macrocode} { \KV_split_key_value_space_removal_detect_error:wTF#2\q_no_value\q_nil {\KV_split_key_value_space_removal_aux:w \q_stop #2} { \msg_kernel_error:nn { keyval } { misplaced-equals-sign } } } } % \end{macrocode} % The error test. % \begin{macrocode} \cs_new_protected:Npn \KV_split_key_value_space_removal_detect_error:wTF#1=#2#3\q_nil{ \tl_if_head_eq_meaning:nNTF{#3}\q_no_value } % \end{macrocode} % Now we can start extracting the value. Recall that |#1| here starts % with |\q_stop| so all braces are still there! First we try to see % how much is left if we gobble three brace groups from |#1|. If |#1| % is empty or blank, all three quarks are gobbled. If |#1| consists of % exactly one token or brace group, only the latter quark is left. % \begin{macrocode} \cs_new:Npn \KV_val_preserve_braces:NnN #1#2#3{{#2}} \cs_new_protected:Npn\KV_split_key_value_space_removal_aux:w #1=={ \tl_set:Nx\l_KV_tmpa_tl{\exp_not:o{\use_none:nnn#1\q_nil\q_nil}} \tl_put_right:No\l_KV_parse_tl{ \exp_after:wN \KV_key_value_elt:nn \exp_after:wN {\l_KV_currkey_tl} } % \end{macrocode} % If there a blank space or nothing at all, |\l_KV_tmpa_tl| is now % completely empty. % \begin{macrocode} \tl_if_empty:NTF\l_KV_tmpa_tl % \end{macrocode} % We just put an empty value on the stack. % \begin{macrocode} { \tl_put_right:Nn\l_KV_parse_tl{{}} } { % \end{macrocode} % If there was exactly one brace group or token in |#1|, % |\l_KV_tmpa_tl| is now equal to |\q_nil|. Then we can just pick it % up as the second argument of |#1|. This will also take care of any % spaces which might surround it. % \begin{macrocode} \quark_if_nil:NTF\l_KV_tmpa_tl { \bool_if:NTF \l_KV_remove_one_level_of_braces_bool { \tl_put_right:No\l_KV_parse_tl{ \exp_after:wN{\use_ii:nnn #1\q_nil} } } { \tl_put_right:No\l_KV_parse_tl{ \exp_after:wN{\KV_val_preserve_braces:NnN #1\q_nil} } } } % \end{macrocode} % Otherwise we grab the value. % \begin{macrocode} { \KV_add_value_element:w #1\q_nil } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\KV_split_key_value_no_space_removal:w} % This version is for when in the special coding regime where spaces % are ignored so there is no need to do any fancy space hacks, % however fun they may be. Since there are no spaces, a set of % braces around a value is automatically stripped by \TeX. % \begin{macrocode} \cs_new_protected:Npn \KV_split_key_value_no_space_removal:w #1#2=#3=#4\q_nil{ \tl_set:Nn\l_KV_tmpa_tl{#4} \tl_if_empty:NTF \l_KV_tmpa_tl { \tl_put_right:Nn\l_KV_parse_tl{\KV_key_no_value_elt:n{#2}} } { \tl_if_eq:NNTF\c_KV_single_equal_sign_tl\l_KV_tmpa_tl { \tl_put_right:Nn\l_KV_parse_tl{\KV_key_value_elt:nn{#2}{#3}} } { \msg_kernel_error:nn { keyval } { misplaced-equals-sign } } } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\KV_key_no_value_elt:n} % \begin{macro}{\KV_key_value_elt:nn} % \begin{macrocode} \cs_new:Npn \KV_key_no_value_elt:n #1{\ERROR} \cs_new:Npn \KV_key_value_elt:nn #1#2{\ERROR} % \end{macrocode} % \end{macro} % \end{macro} % % % % % \begin{macro}{\KV_parse_no_space_removal_no_sanitize:n} % Finally we can put all the things % together. |\KV_parse_no_space_removal_no_sanitize:n| is the % version that disallows unmatched conditional and does no space % removal. % \begin{macrocode} \cs_new_protected_nopar:Npn \KV_parse_no_space_removal_no_sanitize:n { \cs_set_eq:NN \KV_split_key_value_current:w \KV_split_key_value_no_space_removal:w \KV_parse_no_sanitize_aux:n } % \end{macrocode} % \end{macro} % % \begin{macro}{\KV_parse_space_removal_sanitize:n} % \begin{macro}{\KV_parse_space_removal_no_sanitize:n} % The other varieties can be defined in a similar manner. For the % version needed at the document level, we can use this one. % \begin{macrocode} \cs_new_protected_nopar:Npn \KV_parse_space_removal_sanitize:n { \cs_set_eq:NN \KV_split_key_value_current:w \KV_split_key_value_space_removal:w \KV_parse_sanitize_aux:n } % \end{macrocode} % For preamble use by the non-programmer this is probably best. % \begin{macrocode} \cs_new_protected_nopar:Npn \KV_parse_space_removal_no_sanitize:n { \cs_set_eq:NN \KV_split_key_value_current:w \KV_split_key_value_space_removal:w \KV_parse_no_sanitize_aux:n } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} \msg_kernel_new:nnnn { keyval } { misplaced-equals-sign } {Misplaced~equals~sign~in~key--value~input~\msg_line_context:} { I~am~trying~to~read~some~key--value~input~but~found~two~equals~ signs\\% without~a~comma~between~them. } % \end{macrocode} % % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<*showmemory> \showMemUsage % % \end{macrocode} % % \end{implementation} % \PrintIndex % % \endinput