summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3keys.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3keys.dtx665
1 files changed, 364 insertions, 301 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx b/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
index 7b834f6c7a7..0f109a3d695 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: l3keys.dtx Copyright (C) 2006-2015 The LaTeX3 Project
+%% File: l3keys.dtx Copyright (C) 2006-2016 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
@@ -37,7 +37,7 @@
\documentclass[full]{l3doc}
%</driver>
%<*driver|package>
-\GetIdInfo$Id: l3keys.dtx 6284 2015-11-17 10:10:56Z bruno $
+\GetIdInfo$Id: l3keys.dtx 6711 2016-09-21 07:04:35Z joseph $
{L3 Key-value interfaces}
%</driver|package>
%<*driver>
@@ -179,7 +179,7 @@
% Some other properties are mutually exclusive, notably |.value_required:n|
% and |.value_forbidden:n|, and so will replace one another. However,
% properties covering non-exclusive behaviours may be given in any order. Thus
-% for example the following defintions are equivalent.
+% for example the following definitions are equivalent.
% \begin{verbatim}
% \keys_define:nn { mymodule }
% {
@@ -873,20 +873,18 @@
%
% \subsection{Low-level interface}
%
+% The low-level key parser is based heavily on \pkg{keyval}, but with a number
+% of additional \enquote{safety} requirements and with the idea that the
+% parsed list of key--value pairs can be processed in a variety of ways.
+% The net result is that this code needs around twice the amount of time
+% as \pkg{keyval} to parse the same list of keys. To optimise speed as far
+% as reasonably practical, a number of lower-level approaches are taken
+% rather than using the higher-level \pkg{expl3} interfaces.
+%
% \begin{macrocode}
%<@@=keyval>
% \end{macrocode}
%
-% For historical reasons this code uses the `keyval' module prefix.
-%
-% \begin{variable}{\g_@@_level_int}
-% To allow nesting of \cs{keyval_parse:NNn}, 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}
@@ -896,166 +894,206 @@
% \end{variable}
%
% \begin{variable}{\l_@@_sanitise_tl}
-% \begin{variable}{\l_@@_parse_tl}
-% Token list variables for dealing with awkward category codes in the
+% A token list variable 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{macro}{\keyval_parse:NNn}
+% The main function starts off by normalising category codes in package mode.
+% That's relatively \enquote{expensive} so is skipped (hopefully) in format
+% mode. We then hand off to the parser. The use of \cs{q_mark} here prevents
+% loss of braces from the key argument. This particular quark is chosen as
+% it fits in with \cs{__tl_trim_spaces:nn} and allows a performance enhancement
+% as the token can be carried through. Notice that by passing the two
+% processor commands along the input stack we avoid the need to track these
+% at all.
+% \begin{macrocode}
+\cs_new_protected:Npn \keyval_parse:NNn #1#2#3
+ {
+%<*initex>
+ \@@_loop:NNw #1#2 \q_mark #3 , \q_recursion_tail ,
+%</initex>
+%<*package>
+ \tl_set:Nn \l_@@_sanitise_tl {#3}
+ \@@_sanitise_equals:
+ \@@_sanitise_comma:
+ \exp_after:wN \@@_loop:NNw \exp_after:wN #1 \exp_after:wN #2
+ \exp_after:wN \q_mark \l_@@_sanitise_tl , \q_recursion_tail ,
+%</package>
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_sanitise_equals:, \@@_sanitise_comma:}
+% \begin{macro}[aux]
+% {
+% \@@_sanitise_equals_auxi:w, \@@_sanitise_equals_auxii:w,
+% \@@_sanitise_comma_auxi:w, \@@_sanitise_comma_auxii:w,
+% \@@_sanitise_aux:w
+% }
+% A reasonably fast search and replace set up specifically for the active
+% tokens. The nature of the input is known so everything is hard-coded.
+% With only two tokens to cover, the speed gain from using dedicated
+% functions is worth it.
% \begin{macrocode}
+%<*package>
\group_begin:
\char_set_catcode_active:n { `\= }
\char_set_catcode_active:n { `\, }
- \cs_new_protected:Npx \@@_parse:n #1
+ \cs_new_protected:Npn \@@_sanitise_equals:
{
- \group_begin:
- \tl_set:Nn \exp_not:N \l_@@_sanitise_tl {#1}
- \tl_replace_all:Nnn \exp_not:N \l_@@_sanitise_tl
- { \exp_not:N = } { \token_to_str:N = }
- \tl_replace_all:Nnn \exp_not:N \l_@@_sanitise_tl
- { \exp_not:N , } { \token_to_str:N , }
- \tl_clear:N \exp_not:N \l_@@_parse_tl
- \exp_not:N \exp_after:wN
- \exp_not:N \@@_parse_elt:w \exp_not:N \exp_after:wN
- \exp_not:N \q_nil \exp_not:N \l_@@_sanitise_tl
- \token_to_str:N , \exp_not:N \q_recursion_tail
- \token_to_str:N , \exp_not:N \q_recursion_stop
- \exp_not:N \exp_after:wN \group_end:
- \exp_not:N \l_@@_parse_tl
+ \exp_after:wN \@@_sanitise_equals_auxi:w \l_@@_sanitise_tl
+ \q_mark = \q_nil =
+ \exp_after:wN \@@_sanitise_aux:w \l_@@_sanitise_tl
}
+ \cs_new_protected:Npn \@@_sanitise_equals_auxi:w #1 =
+ {
+ \tl_set:Nn \l_@@_sanitise_tl {#1}
+ \@@_sanitise_equals_auxii:w
+ }
+ \cs_new_protected:Npn \@@_sanitise_equals_auxii:w #1 =
+ {
+ \if_meaning:w \q_nil #1 \scan_stop:
+ \else:
+ \tl_set:Nx \l_@@_sanitise_tl
+ {
+ \exp_not:o \l_@@_sanitise_tl
+ \token_to_str:N =
+ \exp_not:n {#1}
+ }
+ \exp_after:wN \@@_sanitise_equals_auxii:w
+ \fi:
+ }
+ \cs_new_protected:Npn \@@_sanitise_comma:
+ {
+ \exp_after:wN \@@_sanitise_comma_auxi:w \l_@@_sanitise_tl
+ \q_mark , \q_nil ,
+ \exp_after:wN \@@_sanitise_aux:w \l_@@_sanitise_tl
+ }
+ \cs_new_protected:Npn \@@_sanitise_comma_auxi:w #1 ,
+ {
+ \tl_set:Nn \l_@@_sanitise_tl {#1}
+ \@@_sanitise_comma_auxii:w
+ }
+ \cs_new_protected:Npn \@@_sanitise_comma_auxii:w #1 ,
+ {
+ \if_meaning:w \q_nil #1 \scan_stop:
+ \else:
+ \tl_set:Nx \l_@@_sanitise_tl
+ {
+ \exp_not:o \l_@@_sanitise_tl
+ \token_to_str:N ,
+ \exp_not:n {#1}
+ }
+ \exp_after:wN \@@_sanitise_comma_auxii:w
+ \fi:
+ }
\group_end:
+\cs_new_protected:Npn \@@_sanitise_aux:w #1 \q_mark
+ { \tl_set:Nn \l_@@_sanitise_tl {#1} }
+%</package>
% \end{macrocode}
% \end{macro}
+% \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. To allow rapid matching for an |=| while not stripping
-% any braces, another \cs{q_nil} needed before the next phase of the
-% parser. Finally, loop around for the next item, adding in the
-% \cs{q_nil}: this happens whatever the nature of the current argument
-% as the end-of-recursion will clear up in all cases.
+% \begin{macro}[aux]{\@@_loop:NNw}
+% A fast test for the end of the loop, remembering to remove the leading
+% quark first. Assuming that is not the case, look for a key and value then
+% loop around, re-inserting a leading quark in front of the next position.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_elt:w #1 ,
+\cs_new_protected:Npn \@@_loop:NNw #1#2#3 ,
{
- \tl_if_blank:oF { \use_none:n #1 }
- {
- \quark_if_recursion_tail_stop:o { \use_none:n #1 }
- \@@_split_key_value:w #1 \q_nil = = \q_stop
- }
- \@@_parse_elt:w \q_nil
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_recursion_tail
+ \use_none:n #3 \prg_do_nothing:
+ \else:
+ \@@_split:NNw #1#2#3 == \q_stop
+ \exp_after:wN \@@_loop:NNw \exp_after:wN #1 \exp_after:wN #2
+ \exp_after:wN \q_mark
+ \fi:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_split_key_value:w}
-% \begin{macro}[aux]{\@@_split_key:w}
-% Split the key and value using a delimited argument. The \cs{q_nil}
-% values added earlier ensure that no braces will be stripped as part
-% of this process. A blank test can then be used on |#3|: it is only
-% empty if there was no |=| in the original input. In that case, strip
-% a \cs{q_nil} from the end of the key name then hand on to remove other
-% things and store as \cs{l_@@_key_tl} before adding to the output token
-% list. In the case where there is an |=|, first tidy up the key, this time
-% without a trailing \cs{q_nil}, then do a check to ensure that |#3| is
-% exactly one token (|=|). With that done, the final stage is to hand off
-% to tidy up the value.
+% \begin{macro}[aux]{\@@_split:NNw, \@@_split_value:NNw}
+% \begin{macro}[aux]{\@@_split_tidy:w}
+% \begin{macro}[aux]{\@@_action:}
+% The value is picked up separately from the key so there can be another
+% quark inserted at the front, keeping braces and allowing both parts to
+% share the same code paths. The key is found first then there's a check
+% that there is something there: this is biased to the common case of there
+% actually being a key. For the value, we first need to see if there is
+% anything to do: if there is, extract it. The appropriate action is then
+% inserted in front of the key and value. Doing this using an assignment is
+% marginally faster than an an expansion chain.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_split_key_value:w #1 = #2 = #3 \q_stop
+\cs_new_protected:Npn \@@_split:NNw #1#2#3 =
{
- \tl_if_blank:nTF {#3}
- {
- \@@_split_key:w #1 \q_stop
- \tl_put_right:Nx \l_@@_parse_tl
+ \@@_def:Nn \l_@@_key_tl {#3}
+ \if_meaning:w \l_@@_key_tl \c_empty_tl
+ \exp_after:wN \@@_split_tidy:w
+ \else:
+ \exp_after:wN \@@_split_value:NNw \exp_after:wN #1 \exp_after:wN #2
+ \exp_after:wN \q_mark
+ \fi:
+ }
+\cs_new_protected:Npn \@@_split_value:NNw #1#2#3 = #4 \q_stop
+ {
+ \if:w \scan_stop: \tl_to_str:n {#4} \scan_stop:
+ \cs_set:Npx \@@_action:
+ { \exp_not:N #1 { \exp_not:o \l_@@_key_tl } }
+ \else:
+ \if:w \scan_stop: \etex_detokenize:D \exp_after:wN { \use_none:n #4 }
+ \scan_stop:
+ \@@_def:Nn \l_@@_value_tl {#3}
+ \cs_set:Npx \@@_action:
{
- \exp_not:c
- {
- @@_key_no_value_elt_
- \int_use:N \g_@@_level_int
- :n
- }
+ \exp_not:N #2
{ \exp_not:o \l_@@_key_tl }
+ { \exp_not:o \l_@@_value_tl }
}
- }
- {
- \@@_split:Nn \l_@@_key_tl {#1}
- \tl_if_blank:oTF { \use_none:n #3 }
- { \@@_split_value:w \q_nil #2 \q_stop }
+ \else:
+ \cs_set:Npn \@@_action:
{ \__msg_kernel_error:nn { kernel } { misplaced-equals-sign } }
- }
+ \fi:
+ \fi:
+ \@@_action:
}
-\cs_new_protected:Npn \@@_split_key:w #1 \q_nil \q_stop
- { \@@_split:Nn \l_@@_key_tl {#1} }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
-% \begin{macro}{\@@_split:Nn}
-% \begin{macro}[aux]{\@@_split:Nw}
-% 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.
-% \begin{macrocode}
-\cs_new_protected:Npn \@@_split:Nn #1#2
+\cs_new_protected:Npn \@@_split_tidy:w #1 \q_stop
{
- \quark_if_nil:oTF { \use_none:nnn #2 \q_nil \q_nil }
- { \tl_set:Nx #1 { \exp_not:o { \use_ii:nnn #2 \q_nil } } }
- { \@@_split:Nw #1 #2 \q_stop }
+ \if:w \scan_stop: \etex_detokenize:D \exp_after:wN { \use_none:n #1 }
+ \scan_stop:
+ \else:
+ \exp_after:wN \@@_empty_key:
+ \fi:
}
-\cs_new_protected:Npn \@@_split:Nw #1 \q_nil #2 \q_stop
- { \tl_set:Nx #1 { \tl_trim_spaces:n {#2} } }
+\cs_new:Npn \@@_action: { }
+\cs_new_protected:Npn \@@_empty_key:
+ { \__msg_kernel_error:nn { kernel } { misplaced-equals-sign } }
% \end{macrocode}
% \end{macro}
% \end{macro}
-%
-% \begin{macro}{\@@_split_value:w}
-% As this stage there is just the value to deal with. The leading and
-% trailing \cs{q_nil} tokens are removed in two steps before storing the
-% value with spaces stripped (see \cs{@@_split:Nn}). Doing the storage
-% of key and value in one shot will put exactly the right number of
-% brace groups into the output.
-% \begin{macrocode}
-\cs_new_protected:Npn \@@_split_value:w #1 \q_nil \q_stop
- {
- \@@_split:Nn \l_@@_value_tl {#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 }
- { \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{macro}[aux]{\@@_def:Nn}
+% \begin{macro}[aux, EXP]{\@@_def_aux:n}
+% \begin{macro}[aux, EXP]{\@@_def_aux:w}
+% First trim spaces off, then potentially remove a set of braces. By using
+% the internal interface |\__tl_trim_spaces:nn| we can take advantage of the
+% fact it needs a leading |\q_mark| in this process. The |\exp_after:wN|
+% removes the quark, the delimited argument deals with any braces.
% \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
- }
+\cs_new_protected:Npn \@@_def:Nn #1#2
+ { \tl_set:Nx #1 { \__tl_trim_spaces:nn {#2} \@@_def_aux:n } }
+\cs_new:Npn \@@_def_aux:n #1
+ { \exp_after:wN \@@_def_aux:w #1 \q_stop }
+\cs_new:Npn \@@_def_aux:w #1 \q_stop { \exp_not:n {#1} }
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% One message for the low level parsing system.
% \begin{macrocode}
@@ -1073,11 +1111,21 @@
%<@@=keys>
% \end{macrocode}
%
-% \begin{variable}{\c_@@_code_root_tl, \c_@@_info_root_tl}
-% The prefixes for the code and variables of the keys themselves.
+% \begin{variable}
+% {
+% \c_@@_code_root_tl ,
+% \c_@@_default_root_tl ,
+% \c_@@_groups_root_tl,
+% \c_@@_info_root_tl ,
+% \c_@@_validate_root_tl
+% }
+% Various storage areas for the different data which make up keys.
% \begin{macrocode}
-\tl_const:Nn \c_@@_code_root_tl { key~code~>~ }
-\tl_const:Nn \c_@@_info_root_tl { key~info~>~ }
+\tl_const:Nn \c_@@_code_root_tl { key~code~>~ }
+\tl_const:Nn \c_@@_default_root_tl { key~default~>~ }
+\tl_const:Nn \c_@@_groups_root_tl { key~groups~>~ }
+\tl_const:Nn \c_@@_info_root_tl { key~info~>~ }
+\tl_const:Nn \c_@@_validate_root_tl { key~validate~>~ }
% \end{macrocode}
% \end{variable}
%
@@ -1203,7 +1251,7 @@
\cs_new_protected:Npn \@@_define:nnn #1#2#3
{
\tl_set:Nx \l_@@_module_tl { \@@_remove_spaces:n {#2} }
- \keyval_parse:NNn \@@_define_elt:n \@@_define_elt:nn {#3}
+ \keyval_parse:NNn \@@_define:n \@@_define:nn {#3}
\tl_set:Nn \l_@@_module_tl {#1}
}
\cs_generate_variant:Nn \@@_define:nnn { o }
@@ -1211,33 +1259,33 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[int]{\@@_define_elt:n}
-% \begin{macro}[int]{\@@_define_elt:nn}
-% \begin{macro}[aux]{\@@_define_elt_aux:nn}
+% \begin{macro}[int]{\@@_define:n}
+% \begin{macro}[int]{\@@_define:nn}
+% \begin{macro}[aux]{\@@_define_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
+\cs_new_protected:Npn \@@_define:n #1
{
\bool_set_true:N \l_@@_no_value_bool
- \@@_define_elt_aux:nn {#1} { }
+ \@@_define_aux:nn {#1} { }
}
-\cs_new_protected:Npn \@@_define_elt:nn #1#2
+\cs_new_protected:Npn \@@_define:nn #1#2
{
\bool_set_false:N \l_@@_no_value_bool
- \@@_define_elt_aux:nn {#1} {#2}
+ \@@_define_aux:nn {#1} {#2}
}
-\cs_new_protected:Npn \@@_define_elt_aux:nn #1#2
+\cs_new_protected:Npn \@@_define_aux:nn #1#2
{
\@@_property_find:n {#1}
\cs_if_exist:cTF { \c_@@_props_root_tl \l_@@_property_tl }
- { \@@_define_key:n {#2} }
+ { \@@_define_code:n {#2} }
{
- \str_if_eq_x:nnF { \l_@@_property_tl } { .abort: }
- {
- \__msg_kernel_error:nnxx { kernel } { property-unknown }
+ \tl_if_empty:NF \l_@@_property_tl
+ {
+ \__msg_kernel_error:nnxx { kernel } { property-unknown }
{ \l_@@_property_tl } { \l_keys_path_tl }
}
}
@@ -1255,45 +1303,57 @@
% \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 }
+ \tl_set:Nx \l_@@_property_tl { \@@_remove_spaces:n {#1} }
+ \exp_after:wN \@@_property_find:w \l_@@_property_tl . . \q_stop {#1}
+ }
+\cs_new_protected:Npn \@@_property_find:w #1 . #2 . #3 \q_stop #4
+ {
+ \tl_if_blank:nTF {#3}
+ {
+ \tl_clear:N \l_@@_property_tl
+ \__msg_kernel_error:nnn { kernel } { key-no-property } {#4}
+ }
{
- \__msg_kernel_error:nnx { kernel } { key-no-property } {#1}
- \tl_set:Nn \l_@@_property_tl { .abort: }
+ \str_if_eq:nnTF {#3} { . }
+ {
+ \tl_set:Nx \l_keys_path_tl { \l_@@_module_tl / #1 }
+ \tl_set:Nn \l_@@_property_tl { . #2 }
+ }
+ {
+ \tl_set:Nx \l_keys_path_tl { \l_@@_module_tl / #1 . #2 }
+ \@@_property_search:w #3 \q_stop
+ }
}
}
-\cs_new_protected:Npn \@@_property_find:w #1 . #2 \q_stop
+\cs_new_protected:Npn \@@_property_search:w #1 . #2 \q_stop
{
- \tl_set:Nx \l_keys_path_tl
+ \str_if_eq:nnTF {#2} { . }
{
- \l_keys_path_tl
- \@@_remove_spaces:n {#1}
+ \tl_set:Nx \l_keys_path_tl { \l_keys_path_tl }
+ \tl_set:Nn \l_@@_property_tl { . #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:Nx \l_keys_path_tl { \l_keys_path_tl . #1 }
+ \@@_property_search: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}
+% \begin{macro}[int]{\@@_define_code:n}
+% \begin{macro}[aux, EXP]{\@@_define_code: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
+\cs_new_protected:Npn \@@_define_code:n #1
{
\bool_if:NTF \l_@@_no_value_bool
{
- \exp_after:wN \@@_define_key:w
+ \exp_after:wN \@@_define_code:w
\l_@@_property_tl \q_stop
{ \use:c { \c_@@_props_root_tl \l_@@_property_tl } }
{
@@ -1304,7 +1364,11 @@
}
{ \use:c { \c_@@_props_root_tl \l_@@_property_tl } {#1} }
}
-\cs_new_protected:Npn \@@_define_key:w #1 : #2 \q_stop
+\use:x
+ {
+ \cs_new:Npn \exp_not:N \__keys_define_code:w
+ ##1 \c_colon_str ##2 \exp_not:N \q_stop
+ }
{ \tl_if_empty:nTF {#2} }
% \end{macrocode}
% \end{macro}
@@ -1322,7 +1386,7 @@
% current scope but exists at a higher level, we do not use \cs{prop_new:c}
% but rather \cs{prop_set_eq:cN}. The function \cs{__chk_log:x} only
% writes to the log file if logging all new functions is active: without
-% it keys would not show up (as we are not using \cs{..._new}).
+% it keys would not show up (as we are not using \cs[no-index]{..._new}).
% \begin{macrocode}
\cs_new_protected:Npn \@@_ensure_exist:n #1
{
@@ -1330,6 +1394,7 @@
{
\prop_set_eq:cN { \c_@@_info_root_tl #1 } \c_empty_prop
}
+
\cs_if_exist:cF { \c_@@_code_root_tl #1 }
{
\__chk_log:x { Defining~key~#1~ \msg_line_context: }
@@ -1396,11 +1461,11 @@
% nested! As multichoices and choices are essentially the same bar one
% function, the code is given together.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_choice_make:
+\cs_new_protected:Npn \@@_choice_make:
{ \@@_choice_make:N \@@_choice_find:n }
-\cs_new_protected_nopar:Npn \@@_multichoice_make:
+\cs_new_protected:Npn \@@_multichoice_make:
{ \@@_choice_make:N \@@_multichoice_find:n }
-\cs_new_protected_nopar:Npn \@@_choice_make:N #1
+\cs_new_protected:Npn \@@_choice_make:N #1
{
\prop_if_exist:cTF
{ \c_@@_info_root_tl \@@_parent:o \l_keys_path_tl }
@@ -1416,7 +1481,7 @@
}
{ \@@_choice_make_aux:N #1 }
}
-\cs_new_protected_nopar:Npn \@@_choice_make_aux:N #1
+\cs_new_protected:Npn \@@_choice_make_aux:N #1
{
\@@_cmd_set:nn { \l_keys_path_tl } { #1 {##1} }
\prop_put:cnn { \c_@@_info_root_tl \l_keys_path_tl } { choice }
@@ -1450,9 +1515,9 @@
% Auto-generating choices means setting up the root key as a choice, then
% defining each choice in turn.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_choices_make:nn
+\cs_new_protected:Npn \@@_choices_make:nn
{ \@@_choices_make:Nnn \@@_choice_make: }
-\cs_new_protected_nopar:Npn \@@_multichoices_make:nn
+\cs_new_protected:Npn \@@_multichoices_make:nn
{ \@@_choices_make:Nnn \@@_multichoice_make: }
\cs_new_protected:Npn \@@_choices_make:Nnn #1#2#3
{
@@ -1489,19 +1554,21 @@
% \end{macro}
%
% \begin{macro}[int]{\@@_default_set:n}
-% Setting a default value is easy.
+% Setting a default value is easy. These are stored using \cs{cs_set:cpx} as this
+% avoids any worries about whether a token list exists.
% \begin{macrocode}
\cs_new_protected:Npn \@@_default_set:n #1
{
- \@@_ensure_exist:V \l_keys_path_tl
\tl_if_empty:nTF {#1}
{
- \prop_remove:cn { \c_@@_info_root_tl \l_keys_path_tl }
- { default }
+ \cs_set_eq:cN
+ { \c_@@_default_root_tl \l_keys_path_tl }
+ \tex_undefined:D
}
{
- \prop_put:cnn { \c_@@_info_root_tl \l_keys_path_tl }
- { default } {#1}
+ \cs_set:cpx
+ { \c_@@_default_root_tl \l_keys_path_tl }
+ { \exp_not:n {#1} }
}
}
% \end{macrocode}
@@ -1530,21 +1597,14 @@
% \end{macro}
%
% \begin{macro}[int]{\@@_initialise:n}
-% \begin{macro}[aux]{\@@_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.
+% A set up for initialisation: just run the code if it exists.
% \begin{macrocode}
\cs_new_protected:Npn \@@_initialise:n #1
{
- \@@_ensure_exist:V \l_keys_path_tl
- \exp_after:wN \@@_initialise:wn \l_keys_path_tl \q_stop {#1}
+ \cs_if_exist_use:cT { \c_@@_code_root_tl \l_keys_path_tl } { {#1} }
}
-\cs_new_protected:Npn \@@_initialise:wn #1 / #2 \q_stop #3
- { \keys_set:nn {#1} { #2 = {#3} } }
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
% \begin{macro}[int]{\@@_meta_make:n}
% \begin{macro}[int]{\@@_meta_make:nn}
@@ -1568,40 +1628,78 @@
% Undefining a key has to be done without \cs{cs_undefine:c} as that
% function acts globally.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_undefine:
+\cs_new_protected:Npn \@@_undefine:
{
- \cs_set_eq:cN { \c_@@_code_root_tl \l_keys_path_tl } \tex_undefined:D
- \cs_set_eq:cN { \c_@@_info_root_tl \l_keys_path_tl } \tex_undefined:D
+ \clist_map_inline:nn
+ { code , default , groups , info , validate }
+ {
+ \cs_set_eq:cN
+ { \tl_use:c { c_@@_ ##1 _root_tl } \l_keys_path_tl }
+ \tex_undefined:D
+ }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@@_value_requirement:nn}
-% Values can be required or forbidden by having the appropriate marker
-% set. First, both the required and forbidden ones are clear, just in case!
+% \begin{macro}[aux]{\@@_validate_forbidden:, \@@_validate_required:}
+% \begin{macro}[aux]{\@@_validate_cleanup:w}
+% Validating key input is done using a second function which runs before
+% the main key code. Setting that up means setting it equal to a generic
+% stub which does the check. This approach makes the lookup very fast at
+% the cost of one additional csname per key that needs it. The cleanup here
+% has to know the structure of the following code.
% \begin{macrocode}
\cs_new_protected:Npn \@@_value_requirement:nn #1#2
{
- \@@_ensure_exist:V \l_keys_path_tl
- \prop_remove:cn { \c_@@_info_root_tl \l_keys_path_tl }
- { required }
- \prop_remove:cn { \c_@@_info_root_tl \l_keys_path_tl }
- { forbidden }
- \str_if_eq:nnTF {#2} { true }
- {
- \prop_put:cnn { \c_@@_info_root_tl \l_keys_path_tl }
- {#1} { true }
- }
+ \str_case:nnF {#2}
{
- \str_if_eq:nnF {#2} { false }
+ { true }
{
- \__msg_kernel_error:nnx { kernel } { property-boolean-values-only }
- { .value_ #1 :n }
+ \cs_set_eq:cc
+ { \c_@@_validate_root_tl \l_keys_path_tl }
+ { @@_validate_ #1 : }
}
+ { false }
+ {
+ \cs_if_eq:ccT
+ { \c_@@_validate_root_tl \l_keys_path_tl }
+ { @@_validate_ #1 : }
+ {
+ \cs_set_eq:cN
+ { \c_@@_validate_root_tl \l_keys_path_tl }
+ \tex_undefined:D
+ }
+ }
+ }
+ {
+ \__msg_kernel_error:nnx { kernel } { property-boolean-values-only }
+ { .value_ #1 :n }
}
}
+\cs_new_protected:Npn \@@_validate_forbidden:
+ {
+ \bool_if:NF \l_@@_no_value_bool
+ {
+ \__msg_kernel_error:nnxx { kernel } { value-forbidden }
+ { \l_keys_path_tl } { \l_keys_value_tl }
+ \@@_validate_cleanup:w
+ }
+ }
+\cs_new_protected:Npn \@@_validate_required:
+ {
+ \bool_if:NT \l_@@_no_value_bool
+ {
+ \__msg_kernel_error:nnx { kernel } { value-required }
+ { \l_keys_path_tl }
+ \@@_validate_cleanup:w
+ }
+ }
+\cs_new_protected:Npn \@@_validate_cleanup:w #1 \cs_end: #2#3 { }
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[int]{\@@_variable_set:NnnN, \@@_variable_set:cnnN}
% Setting a variable takes the type and scope separately so that
@@ -1668,7 +1766,7 @@
% 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: }
+\cs_new_protected:cpn { \c_@@_props_root_tl .choice: }
{ \@@_choice_make: }
% \end{macrocode}
% \end{macro}
@@ -1824,7 +1922,7 @@
% 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: }
+\cs_new_protected:cpn { \c_@@_props_root_tl .multichoice: }
{ \@@_multichoice_make: }
\cs_new_protected:cpn { \c_@@_props_root_tl .multichoices:nn } #1
{ \@@_multichoices_make:nn #1 }
@@ -1854,7 +1952,6 @@
% \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}
@@ -1886,7 +1983,7 @@
% \begin{macro}{.undefine:}
% Another simple wrapper.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { \c_@@_props_root_tl .undefine: }
+\cs_new_protected:cpn { \c_@@_props_root_tl .undefine: }
{ \@@_undefine: }
% \end{macrocode}
% \end{macro}
@@ -1909,12 +2006,12 @@
% \begin{macro}[aux]{\@@_set:nnn, \@@_set:onn}
% A simple wrapper again.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \keys_set:nn
+\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 { \@@_remove_spaces:n {#2} }
- \keyval_parse:NNn \@@_set_elt:n \@@_set_elt:nn {#3}
+ \keyval_parse:NNn \@@_set:n \@@_set:nn {#3}
\tl_set:Nn \l_@@_module_tl {#1}
}
\cs_generate_variant:Nn \keys_set:nn { nV , nv , no }
@@ -1937,10 +2034,10 @@
% Setting known keys simply means setting the appropriate flag, then
% running the standard code. To allow for nested setting, any existing
% value of \cs{l_@@_unused_clist} is saved on the stack and reset
-% afterwards.Note that for speed/simplicity reasons we use a \texttt{tl}
+% afterwards. Note that for speed/simplicity reasons we use a \texttt{tl}
% operation to set the \texttt{clist} here!
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \keys_set_known:nnN
+\cs_new_protected:Npn \keys_set_known:nnN
{ \@@_set_known:onnN \l_@@_unused_clist }
\cs_generate_variant:Nn \keys_set_known:nnN { nV , nv , no }
\cs_new_protected:Npn \@@_set_known:nnnN #1#2#3#4
@@ -1965,25 +2062,25 @@
%
% \begin{macro}
% {
-% \keys_set_filter:nnnN, \keys_set_filter:nnVN, \keys_set_filter:nnvN
+% \keys_set_filter:nnnN, \keys_set_filter:nnVN, \keys_set_filter:nnvN,
% \keys_set_filter:nnoN
% }
% \begin{macro}[aux]{\@@_set_filter:nnnnN, \@@_set_filter:onnnN}
% \begin{macro}
% {
-% \keys_set_filter:nnn, \keys_set_filter:nnV, \keys_set_filter:nnv
+% \keys_set_filter:nnn, \keys_set_filter:nnV, \keys_set_filter:nnv,
% \keys_set_filter:nno
% }
% \begin{macro}
% {
-% \keys_set_groups:nnn, \keys_set_groups:nnV, \keys_set_groups:nnv
+% \keys_set_groups:nnn, \keys_set_groups:nnV, \keys_set_groups:nnv,
% \keys_set_groups:nno
% }
% The idea of setting keys in a selective manner again uses flags
% wrapped around the basic code. The comments on \cs{keys_set_known:nnN}
% also apply here.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \keys_set_filter:nnnN
+\cs_new_protected:Npn \keys_set_filter:nnnN
{ \@@_set_filter:onnnN \l_@@_unused_clist }
\cs_generate_variant:Nn \keys_set_filter:nnnN { nnV , nnv , nno }
\cs_new_protected:Npn \@@_set_filter:nnnnN #1#2#3#4#5
@@ -2018,24 +2115,24 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[int]{\@@_set_elt:n, \@@_set_elt:nn}
-% \begin{macro}[aux]{\@@_set_elt_aux:nnn, \@@_set_elt_aux:onn}
+% \begin{macro}[int]{\@@_set:n, \@@_set:nn}
+% \begin{macro}[aux]{\@@_set_aux:nnn, \@@_set_aux:onn}
% \begin{macro}[aux]{\@@_find_key_module:w}
-% \begin{macro}[aux]{\@@_set_elt_aux:, \@@_set_elt_selective:}
+% \begin{macro}[aux]{\@@_set_aux:, \@@_set_selective:}
% 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
+\cs_new_protected:Npn \@@_set:n #1
{
\bool_set_true:N \l_@@_no_value_bool
- \@@_set_elt_aux:onn \l_@@_module_tl {#1} { }
+ \@@_set_aux:onn \l_@@_module_tl {#1} { }
}
-\cs_new_protected:Npn \@@_set_elt:nn #1#2
+\cs_new_protected:Npn \@@_set:nn #1#2
{
\bool_set_false:N \l_@@_no_value_bool
- \@@_set_elt_aux:onn \l_@@_module_tl {#1} {#2}
+ \@@_set_aux:onn \l_@@_module_tl {#1} {#2}
}
% \end{macrocode}
% The key path here can be fully defined, after which there is a search
@@ -2044,19 +2141,18 @@
% that happens on a per-key basis, we use the stack approach to restore
% the module name without a group.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_set_elt_aux:nnn #1#2#3
+\cs_new_protected:Npn \@@_set_aux:nnn #1#2#3
{
- \tl_set:Nx \l_keys_path_tl
- { \l_@@_module_tl / \@@_remove_spaces:n {#2} }
+ \tl_set:Nx \l_keys_path_tl { #1 / \@@_remove_spaces:n {#2} }
\tl_clear:N \l_@@_module_tl
\exp_after:wN \@@_find_key_module:w \l_keys_path_tl / \q_stop
\@@_value_or_default:n {#3}
\bool_if:NTF \l_@@_selective_bool
- { \@@_set_elt_selective: }
- { \@@_set_elt_aux: }
+ { \@@_set_selective: }
+ { \@@_execute: }
\tl_set:Nn \l_@@_module_tl {#1}
}
-\cs_generate_variant:Nn \@@_set_elt_aux:nnn { o }
+\cs_generate_variant:Nn \@@_set_aux:nnn { o }
\cs_new_protected:Npn \@@_find_key_module:w #1 / #2 \q_stop
{
\tl_if_blank:nTF {#2}
@@ -2070,37 +2166,13 @@
\@@_find_key_module:w #2 \q_stop
}
}
-\cs_new_protected_nopar:Npn \@@_set_elt_aux:
- {
- \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}
% If selective setting is active, there are a number of possible sub-cases
% to consider. The key name may not be known at all or if it is, it may not
% have any groups assigned. There is then the question of whether the
% selection is opt-in or opt-out.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_set_elt_selective:
+\cs_new_protected:Npn \@@_set_selective:
{
\prop_if_exist:cTF { \c_@@_info_root_tl \l_keys_path_tl }
{
@@ -2109,13 +2181,13 @@
{ \@@_check_groups: }
{
\bool_if:NTF \l_@@_filtered_bool
- { \@@_set_elt_aux: }
+ { \@@_execute: }
{ \@@_store_unused: }
}
}
{
\bool_if:NTF \l_@@_filtered_bool
- { \@@_set_elt_aux: }
+ { \@@_execute: }
{ \@@_store_unused: }
}
}
@@ -2125,7 +2197,7 @@
% set active. That requires two mappings, and again a different outcome
% depending on whether opt-in or opt-out is set.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_check_groups:
+\cs_new_protected:Npn \@@_check_groups:
{
\bool_set_false:N \l_@@_tmp_bool
\seq_map_inline:Nn \l_@@_selective_seq
@@ -2143,11 +2215,11 @@
{
\bool_if:NTF \l_@@_filtered_bool
{ \@@_store_unused: }
- { \@@_set_elt_aux: }
+ { \@@_execute: }
}
{
\bool_if:NTF \l_@@_filtered_bool
- { \@@_set_elt_aux: }
+ { \@@_execute: }
{ \@@_store_unused: }
}
}
@@ -2165,8 +2237,12 @@
{
\bool_if:NTF \l_@@_no_value_bool
{
- \prop_get:cnNF { \c_@@_info_root_tl \l_keys_path_tl }
- { default } \l_keys_value_tl
+ \cs_if_exist:cTF { \c_@@_default_root_tl \l_keys_path_tl }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c_@@_default_root_tl \l_keys_path_tl }
+ }
{ \tl_clear:N \l_keys_value_tl }
}
{ \tl_set:Nn \l_keys_value_tl {#1} }
@@ -2174,25 +2250,7 @@
% \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 }
- {
- \prop_if_exist:cTF { \c_@@_info_root_tl \l_keys_path_tl }
- {
- \prop_if_in:cnTF { \c_@@_info_root_tl \l_keys_path_tl } {#1}
- { \prg_return_true: }
- { \prg_return_false: }
- }
- { \prg_return_false: }
- }
-% \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[int]{\@@_execute:}
-% \begin{macro}[aux]{\@@_execute_unknown:}
+% \begin{macro}[aux]{\@@_execute:, \@@_execute_unknown:}
% \begin{macro}[aux, EXP]{\@@_execute:nn}
% \begin{macro}[aux]{\@@_store_unused:}
% Actually executing a key is done in two parts. First, look for the
@@ -2201,14 +2259,26 @@
% is unknown depends on whether unknown keys are being skipped or if
% an error should be raised.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_execute:
- { \@@_execute:nn { \l_keys_path_tl } { \@@_execute_unknown: } }
-\cs_new_protected_nopar:Npn \@@_execute_unknown:
+\cs_new_protected:Npn \@@_execute:
+ {
+ \cs_if_exist:cTF { \c_@@_code_root_tl \l_keys_path_tl }
+ {
+ \cs_if_exist_use:c { \c_@@_validate_root_tl \l_keys_path_tl }
+ \cs:w \c_@@_code_root_tl \l_keys_path_tl \exp_after:wN \cs_end:
+ \exp_after:wN { \l_keys_value_tl }
+ }
+ { \@@_execute_unknown: }
+ }
+\cs_new_protected:Npn \@@_execute_unknown:
{
\bool_if:NTF \l_@@_only_known_bool
{ \@@_store_unused: }
{
- \@@_execute:nn { \l_@@_module_tl / unknown }
+ \cs_if_exist:cTF { \c_@@_code_root_tl \l_@@_module_tl / unknown }
+ {
+ \cs:w \c_@@_code_root_tl \l_@@_module_tl / unknown \exp_after:wN
+ \cs_end: \exp_after:wN { \l_keys_value_tl }
+ }
{
\__msg_kernel_error:nnxx { kernel } { key-unknown }
{ \l_keys_path_tl } { \l_@@_module_tl }
@@ -2224,7 +2294,7 @@
}
{#2}
}
-\cs_new_protected_nopar:Npn \@@_store_unused:
+\cs_new_protected:Npn \@@_store_unused:
{
\clist_put_right:Nx \l_@@_unused_clist
{
@@ -2237,7 +2307,6 @@
% \end{macro}
% \end{macro}
% \end{macro}
-% \end{macro}
%
% \begin{macro}[int, EXP]{\@@_choice_find:n}
% \begin{macro}[int, EXP]{\@@_multichoice_find:n}
@@ -2292,7 +2361,7 @@
% \end{macro}
%
% \begin{macro}[EXP,pTF]{\keys_if_choice_exist:nnn}
-% Just an alternative view on \cs{keys_if_exist:nn(TF)}.
+% Just an alternative view on \cs{keys_if_exist:nnTF}.
% \begin{macrocode}
\prg_new_conditional:Npnn \keys_if_choice_exist:nnn #1#2#3
{ p , T , F , TF }
@@ -2354,12 +2423,6 @@
\__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 } { key-choice-unknown }
{ Key~'#1'~accepts~only~a~fixed~set~of~choices. }
{
@@ -2430,13 +2493,13 @@
%
% \subsection{Deprecated functions}
%
-% \begin{macro}{.value_forbidden:}
+% \begin{macro}[deprecated = 2017]{.value_forbidden:}
% \begin{macro}{.value_required:}
% Deprecated 2015-07-14.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { \c_@@_props_root_tl .value_forbidden: }
+\cs_new_protected:cpn { \c_@@_props_root_tl .value_forbidden: }
{ \@@_value_requirement:nn { forbidden } { true } }
-\cs_new_protected_nopar:cpn { \c_@@_props_root_tl .value_required: }
+\cs_new_protected:cpn { \c_@@_props_root_tl .value_required: }
{ \@@_value_requirement:nn { required } { true } }
% \end{macrocode}
% \end{macro}