diff options
author | Norbert Preining <norbert@preining.info> | 2020-03-04 03:02:32 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2020-03-04 03:02:32 +0000 |
commit | 19d25b8009801aa98ea2f46b45c37c257f990491 (patch) | |
tree | c0da70fbab26feff40e87df289ae797917df8419 /macros/latex/contrib/l3kernel/l3keys.dtx | |
parent | b78f5a6705512314d6fe42fd5144c5f8e5d484ce (diff) |
CTAN sync 202003040302
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3keys.dtx')
-rw-r--r-- | macros/latex/contrib/l3kernel/l3keys.dtx | 470 |
1 files changed, 276 insertions, 194 deletions
diff --git a/macros/latex/contrib/l3kernel/l3keys.dtx b/macros/latex/contrib/l3kernel/l3keys.dtx index dcf83e0d87..417837ce37 100644 --- a/macros/latex/contrib/l3kernel/l3keys.dtx +++ b/macros/latex/contrib/l3kernel/l3keys.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-02-25} +% \date{Released 2020-03-03} % % \maketitle % @@ -722,7 +722,7 @@ % (\cs{keys_set_known:nnnN}), the key--value entries are returned % relative to this point in the key tree. When it is absent, only the % key name and value are provided. The correct list is returned by -% nested calls. +% nested calls. % \end{function} % % \section{Selective key setting} @@ -891,7 +891,7 @@ % \end{verbatim} % are treated identically. % -% \begin{function}[updated = 2011-09-08]{\keyval_parse:NNn} +% \begin{function}[EXP,updated = 2020-02-20]{\keyval_parse:NNn} % \begin{syntax} % \cs{keyval_parse:NNn} \meta{function_1} \meta{function_2} \Arg{key--value list} % \end{syntax} @@ -920,6 +920,11 @@ % all). Spaces are trimmed from the ends of the \meta{key} and \meta{value}, % then one \emph{outer} set of braces is removed from the \meta{key} % and \meta{value} as part of the processing. +% \begin{texnote} +% The result is returned within \cs{exp_not:n}, which means that the +% converted input stream does not expand further when appearing in an +% \texttt{x}-type or \texttt{e}-type argument expansion. +% \end{texnote} % \end{function} % % \end{documentation} @@ -946,234 +951,312 @@ %<@@=keyval> % \end{macrocode} % -% \begin{variable}{\l_@@_key_tl, \l_@@_value_tl} -% The current key name and value. +% \begin{variable}{\s_@@_nil,\s_@@_mark,\s_@@_stop,\s_@@_tail} % \begin{macrocode} -\tl_new:N \l_@@_key_tl -\tl_new:N \l_@@_value_tl +\scan_new:N \s_@@_nil +\scan_new:N \s_@@_mark +\scan_new:N \s_@@_stop +\scan_new:N \s_@@_tail % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_@@_sanitise_tl} -% A token list variable for dealing with awkward category codes in the -% input. +% This temporary macro will be used since some of the definitions will need an +% active comma or equals sign. Inside of this macro |#1| will be the active +% comma and |#2| will be the active equals sign. % \begin{macrocode} -\tl_new:N \l_@@_sanitise_tl +\group_begin: + \cs_set_protected:Npn \@@_tmp:NN #1#2 + { % \end{macrocode} -% \end{variable} % -% \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. Notice that by passing the two -% processor commands along the input stack we avoid the need to track these -% at all. +% \begin{macro}[EXP]{\keyval_parse:NNn} +% The main function starts the first of two input loops. The outer loop splits +% the key--value list at active commas, the inner loop will do so at other +% commas. The use of \cs{s_@@_mark} here prevents loss of braces from the key +% argument. % \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> - } + \cs_new:Npn \keyval_parse:NNn ##1 ##2 ##3 + { + \@@_loop_active:NNw ##1 ##2 \s_@@_mark ##3 #1 \s_@@_tail #1 + } % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_sanitise_equals:, \@@_sanitise_comma:} -% \begin{macro} -% { -% \@@_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{macro}[EXP]{\@@_loop_active:NNw} +% First a fast test for the end of the loop is done, it'll gobble everything +% up to an \cs{s_@@_mark} immediately followed by an \cs{s_@@_tail}. The loop +% ending macro will gobble everything to the last \cs{s_@@_mark} in this +% definition. +% If the end isn't reached yet, start the second loop splitting at other +% comments, and after that one iterate the current loop. % \begin{macrocode} -%<*package> -\group_begin: - \char_set_catcode_active:n { `\= } - \char_set_catcode_active:n { `\, } - \cs_new_protected:Npn \@@_sanitise_equals: - { - \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 + \cs_new:Npn \@@_loop_active:NNw ##1 ##2 ##3 #1 + { + \@@_if_recursion_tail:w ##3 + \@@_end_loop_active:w \s_@@_mark \s_@@_tail + \@@_loop_other:NNw ##1 ##2 ##3 , \s_@@_tail , + \@@_loop_active:NNw ##1 ##2 \s_@@_mark + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_loop_other:NNw} +% The second loop uses the same test for its end as the first loop, next it +% tests whether there are other or active equals signs, throwing an error if +% there are both. If there are none, test whether the argument is blank or is +% a single key. If there are only active equals signs split at those, else +% split at others. Finally, iterate the loop. +% \begin{macrocode} + \cs_new:Npn \@@_loop_other:NNw ##1 ##2 ##3 , + { + \@@_if_recursion_tail:w ##3 + \@@_end_loop_other:w \s_@@_mark \s_@@_tail + \@@_if_has_equal_other:w ##3 = \s_@@_stop + \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn { - \exp_not:o \l_@@_sanitise_tl - \token_to_str:N = - \exp_not:n {#1} + \@@_if_has_equal_active:w ##3 #2 \s_@@_stop + \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn + \@@_misplaced_equal_error: + { \@@_split_other:w ##3 = \s_@@_stop ##2 } } - \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} + \@@_if_has_equal_active:w ##3 #2 \s_@@_stop + \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn + { \@@_split_active:w ##3 #2 \s_@@_stop ##2 } + { + \@@_if_blank:w ##3 \s_@@_nil \s_@@_stop + \@@_blank_true:w \s_@@_mark \s_@@_stop \use:n + { \@@_trim:nN { ##3 } \@@_key:nN ##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}{\@@_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 \@@_loop:NNw #1#2#3 , - { - \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:NNw, \@@_split_value:NNw} -% \begin{macro}{\@@_split_tidy:w} -% \begin{macro}{\@@_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 expansion chain. -% \begin{macrocode} -\cs_new_protected:Npn \@@_split:NNw #1#2#3 = - { - \@@_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: - \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #4 } - \scan_stop: - \@@_def:Nn \l_@@_value_tl {#3} - \cs_set:Npx \@@_action: - { - \exp_not:N #2 - { \exp_not:o \l_@@_key_tl } - { \exp_not:o \l_@@_value_tl } - } - \else: - \cs_set:Npn \@@_action: + \@@_loop_other:NNw ##1 ##2 \s_@@_mark + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_split_active:w} +% \begin{macro}[EXP]{\@@_split_active:nw} +% Splits at the first active equals sign and trims the key. Next test whether +% there are any more valid split points, if so throw an error and gobble the +% remaining \meta{function_2}, which will not yet be gobbled. If there was +% only one active equals sign start trimming the spaces off the value and give +% control to \cs[no-index]{@@_key_val:nnN}. +% \begin{macrocode} + \cs_new:Npn \@@_split_active:w ##1 #2 + { + \@@_trim:nN { ##1 } \@@_split_active:nw \s_@@_mark + } + \cs_new:Npn \@@_split_active:nw ##1 ##2 #2 ##3 \s_@@_stop { - \__kernel_msg_error:nn { kernel } - { misplaced-equals-sign } + \@@_if_empty:w \s_@@_mark ##3 \s_@@_stop + \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn + { \@@_misplaced_equal_error: \use_none:n } + { \@@_trim:nN { ##2 } \@@_key_val:nnN { ##1 } } } - \fi: - \fi: - \@@_action: +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_if_has_equal_active:w} +% The test for an active equals sign just gobbles tokens until the first +% active equals sign and then runs the test for an empty argument. +% \begin{macrocode} + \cs_new:Npn \@@_if_has_equal_active:w ##1 #2 + { + \@@_if_empty:w \s_@@_mark + } +% \end{macrocode} +% \end{macro} +% +% We're done with the macros which need active equals signs or commas in their +% definition, so we can end that scope and call the temporary macro which will +% do the definitions. +% \begin{macrocode} + } + \char_set_catcode_active:n { `\, } + \char_set_catcode_active:n { `\= } + \@@_tmp:NN , = +\group_end: +% \end{macrocode} +% +% \begin{macro}[EXP]{\@@_end_loop_active:w,\@@_end_loop_other:w} +% Both of these macros just have to gobble a few tokens to remove the reminder +% of the loops current iteration. We do this in a pretty static manner, +% explicitly stating every token we know beforehand because this is slightly +% faster. +% \begin{macrocode} +\cs_new:Npn \@@_end_loop_active:w + \s_@@_mark \s_@@_tail + \@@_loop_other:NNw #1 , \s_@@_tail , + \@@_loop_active:NNw #2 \s_@@_mark + {} +\cs_new:Npn \@@_end_loop_other:w + \s_@@_mark \s_@@_tail + \@@_if_has_equal_other:w #1 = \s_@@_stop + \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn + #2 + \@@_loop_other:NNw #3 \s_@@_mark + {} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_split_other:w} +% \begin{macro}[EXP]{\@@_split_other:nw} +% These work exactly as \cs[no-index]{@@_split_active:wN}, just for +% equals signs of category other. +% \begin{macrocode} +\cs_new:Npn \@@_split_other:w #1 = + { + \@@_trim:nN { #1 } \@@_split_other:nw \s_@@_mark } -\cs_new_protected:Npn \@@_split_tidy:w #1 \q_stop + \cs_new:Npn \@@_split_other:nw #1 #2 = #3 \s_@@_stop + { + \@@_if_empty:w \s_@@_mark #3 \s_@@_stop + \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn + { \@@_misplaced_equal_error: \use_none:n } + { \@@_trim:nN { #2 } \@@_key_val:nnN { #1 } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_key:nN} +% This will get the current key with spaces trimmed and \meta{function_1} as +% its arguments. All it has to do is put them in an \cs{exp_not:n} and reorder +% them. +% \begin{macrocode} +\cs_new:Npn \@@_key:nN #1 #2 { - \if:w - \scan_stop: - \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #1 } - \scan_stop: - \else: - \exp_after:wN \@@_empty_key: - \fi: + \exp_not:n { #2 { #1 } } } -\cs_new:Npn \@@_action: { } -\cs_new_protected:Npn \@@_empty_key: - { \__kernel_msg_error:nn { kernel } { misplaced-equals-sign } } % \end{macrocode} % \end{macro} +% +% \begin{macro}[EXP]{\@@_key_val:nnN} +% This will get the key name and value with spaces trimmed. It has to +% assert that the key name isn't empty. Afterwards put them into an +% \cs{exp_not:n} together with \meta{function_2}. If the key is empty they are +% gobbled instead. +% \begin{macrocode} +\cs_new:Npn \@@_key_val:nnN #1 #2 #3 + { + \@@_if_empty:w \s_@@_mark #2 \s_@@_stop + \@@_empty_key:w \s_@@_mark \s_@@_stop + \exp_not:n { #3 { #2 } { #1 } } + } +% \end{macrocode} % \end{macro} +% +% \begin{macro}[EXP]{\@@_if_empty:w,\@@_if_blank:w,\@@_if_recursion_tail:w} +% All these tests work by gobbling tokens until a certain combination is met, +% which makes them pretty fast. The test for a blank argument should be called +% with an arbitrary token following the argument. Each of these utilize the +% fact that the argument will contain a leading \cs{s_@@_mark}. +% \begin{macrocode} +\cs_new:Npn \@@_if_empty:w #1 \s_@@_mark \s_@@_stop {} +\cs_new:Npn \@@_if_blank:w \s_@@_mark #1 { \@@_if_empty:w \s_@@_mark } +\cs_new:Npn \@@_if_recursion_tail:w #1 \s_@@_mark \s_@@_tail {} +% \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_def:Nn} -% \begin{macro}[EXP]{\@@_def_aux:n} -% \begin{macro}[EXP]{\@@_def_aux:w} -% First remove the leading quark, then trim spaces off, and finally remove -% a set of braces. +% \begin{macro}[EXP]{\@@_has_false:w,\@@_blank_true:w,\@@_empty_key:w} +% These macros will be called if the tests above didn't gobble them, they +% execute the branching. % \begin{macrocode} -\cs_new_protected:Npn \@@_def:Nn #1#2 +\cs_new:Npn \@@_has_false:w \s_@@_mark \s_@@_stop \use_i:nn #1 #2 { #2 } +\cs_new:Npn \@@_blank_true:w \s_@@_mark \s_@@_stop \use:n #1 {} +\cs_new:Npn \@@_empty_key:w \s_@@_mark \s_@@_stop \exp_not:n #1 { - \tl_set:Nx #1 - { \tl_trim_spaces_apply:oN { \use_none:n #2 } \@@_def_aux:n } + \@@_misplaced_equal_error: } -\cs_new:Npn \@@_def_aux:n #1 - { \@@_def_aux:w #1 \q_stop } -\cs_new:Npn \@@_def_aux:w #1 \q_stop { \exp_not:n {#1} } % \end{macrocode} % \end{macro} +% +% \begin{macro}[EXP]{\@@_if_has_equal_other:w} +% Another test that works by gobbling tokens until a specific one is hit. +% \begin{macrocode} +\cs_new:Npn \@@_if_has_equal_other:w #1 = + { + \@@_if_empty:w \s_@@_mark + } +% \end{macrocode} % \end{macro} +% +% \begin{macro}[EXP]{\@@_misplaced_equal_error:} +% Just throw an error expandably. This is hid inside a macro so that other +% macros don't have to gobble so many tokens, which increases speed for +% correct input. This will marginally slow down the error case, but that +% doesn't have to be fast anyway. +% \begin{macrocode} +\cs_new:Npn \@@_misplaced_equal_error: + { + \__kernel_msg_expandable_error:nn { kernel } { misplaced-equals-sign } + } +% \end{macrocode} % \end{macro} % % One message for the low level parsing system. % \begin{macrocode} -\__kernel_msg_new:nnnn { kernel } { misplaced-equals-sign } +\__kernel_msg_new:nnn { kernel } { misplaced-equals-sign } { Misplaced~equals~sign~in~key-value~input~\msg_line_context: } - { - LaTeX~is~attempting~to~parse~some~key-value~input~but~found~ - two~equals~signs~not~separated~by~a~comma. - } % \end{macrocode} % +% \begin{macro}[EXP]{\@@_trim:nN} +% \begin{macro}[EXP] +% {\@@_trim_auxi:w,\@@_trim_auxii:w,\@@_trim_auxiii:w,\@@_trim_auxiv:w} +% And an adapted version of \cs{__tl_trim_spaces:nn} which is a bit faster for +% our use case, as it can strip the braces at the end. This is pretty much the +% same concept, so I won't comment on it here. The speed gain by using this +% instead of \cs{tl_trim_spaces_apply:nN} is about 10\,\% of the total time for +% \cs{keyval_parse:NNn} with one key and one key--value pair, so I think it's +% worth it. +% \begin{macrocode} +\group_begin: + \cs_set_protected:Npn \@@_tmp:n #1 + { + \cs_new:Npn \@@_trim:nN ##1 + { + \@@_trim_auxi:w + ##1 + \s_@@_nil + \s_@@_mark #1 {} + \s_@@_mark \@@_trim_auxii:w + \@@_trim_auxiii:w + #1 \s_@@_nil + \@@_trim_auxiv:w + \s_@@_stop + } + \cs_new:Npn \@@_trim_auxi:w ##1 \s_@@_mark #1 ##2 \s_@@_mark ##3 + { + ##3 + \@@_trim_auxi:w + \s_@@_mark + ##2 + \s_@@_mark #1 {##1} + } + \cs_new:Npn \@@_trim_auxii:w \@@_trim_auxi:w \s_@@_mark \s_@@_mark ##1 + { + \@@_trim_auxiii:w + ##1 + } + \cs_new:Npn \@@_trim_auxiii:w ##1 #1 \s_@@_nil ##2 + { + ##2 + ##1 \s_@@_nil + \@@_trim_auxiii:w + } +% \end{macrocode} +% This is the one macro which differs from the original definition. +% \begin{macrocode} + \cs_new:Npn \@@_trim_auxiv:w \s_@@_mark ##1 \s_@@_nil ##2 \s_@@_stop ##3 + { ##3 { ##1 } } + } + \@@_tmp:n { ~ } +\group_end: +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \subsection{Constants and variables} % % \begin{macrocode} @@ -2670,7 +2753,7 @@ { \tl_set:Nx \l_@@_relative_tl { \exp_args:No \@@_trim_spaces:n \l_@@_relative_tl } - \use:x + \use:x { \cs_set_protected:Npn \@@_store_unused:w ####1 \l_@@_relative_tl / @@ -2772,7 +2855,6 @@ { \exp_after:wN \@@_trim_spaces_auxi:w \tl_to_str:n {#1} / \q_nil \q_stop - } \cs_new:Npn \@@_trim_spaces_auxi:w #1 / #2 \q_stop { |