TeX - LaTeX Asked on May 14, 2021
interface3.pdf says:
keyval_parse:NNn ⟨function1⟩ ⟨function2⟩ {⟨key–value list⟩}
Parses the ⟨key–value
list⟩ into a series of ⟨keys⟩ and
associated ⟨values⟩, or keys alone (if no
⟨value⟩ was given).
⟨function1⟩ should take one argument, while
⟨function2⟩ should absorb two arguments. After
keyval_parse:NNn
has parsed the ⟨key–value
list⟩, ⟨function1⟩ is used to process
keys given with no value and ⟨function2⟩ is used
to process keys given with a value. The order of the
⟨keys⟩ in the ⟨key–value
list⟩ is preserved.
…
My question is:
Why are you urged to use functions only which absorb exactly 1 respective 2 arguments?
I ask this because this prevents you from placing keyval_parse:NNn
into macro-definitions where
I would have liked to do something like this:
cs_new:Nn MyStuff_ProcessInCaseOnlyKey:nnn {
This~is~the~first~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~exp_args:Notexttt{tl_to_str:n{#1}}.
This~is~the~second~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~exp_args:Notexttt{tl_to_str:n{#2}}.
This~is~the~key~which~was~passed~on~by~the~keyval~parser:~exp_args:Notexttt{tl_to_str:n{#3}}.
}
cs_new:Nn MyStuff_ProcessInCaseKeyAndValue:nnnn {
This~is~the~first~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~exp_args:Notexttt{tl_to_str:n{#1}}.
This~is~the~second~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~exp_args:Notexttt{tl_to_str:n{#2}}.
This~is~the~key~which~was~passed~on~by~the~keyval~parser:~exp_args:Notexttt{tl_to_str:n{#3}}.
This~is~the~value~which~was~passed~on~by~the~keyval~parser:~exp_args:Notexttt{tl_to_str:n{#4}}.
}
cs_new:Nn MyStuff_ProcessAsKeyval:nnn {
keyval_parse:NNn { MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2} }
{ MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2} }
{#3}
}
I would have liked to be able to use it without the need of doubling hashes in case MyStuff_ProcessAsKeyval:nnn
is something whose 1st and 2nd argument are to be used for providing "inline-code" with arguments.
For example I would have liked to be able to do
MyStuff_ProcessAsKeyval:nnn
{cs_set:Nn MyStuff_tempa:n{This~is~a~temporary~macro's~argument:~#1}}
{cs_set:Nn MyStuff_tempb:n{This~is~another~temporary~macro's~argument:~#1}}
{keyA=valueA, keyB=valueB}
instead of
MyStuff_ProcessAsKeyval:nnn
{cs_set:Nn MyStuff_tempa:n{This~is~a~temporary~macro's~argument:~##1}}
{cs_set:Nn MyStuff_tempb:n{This~is~another~temporary~macro's~argument:~##1}}
{keyA=valueA, keyB=valueB}
.
(Of course it should not be keyval_parse:NNn
but keyval_parse:nnn
. But keyval_parse:nnn
is not available and cannot be derived as a variant. Of course – instead of passing on #1
and #2
– I could have TeX define two temporary macros with each execution of MyStuff_ProcessAsKeyval:nnn
and use them in the definitions of MyStuff_ProcessInCaseOnlyKey:n
and MyStuff_ProcessInCaseKeyAndValue:nn
, but this is cumbersome and seems inefficient.)
I don’t provide MWE because I ask about the reason for things being implemented as they are.
I am not asking for help with bug-tracking.
The main reason for keyval_parse:NNn
to take N
-type arguments instead of a group of tokens (n
-type) is performance; and the fact that this simply wasn't needed.
Quick and dirty proof of concept that keyval_parse:NNn
is rather easy to adapt to this use. All I did was to search for NN
and replace it with nn
as well as putting the missing braces around the arguments in the first few steps of parsing. (well, and I used new names, instead of keyval
the new module is named ulrichkeyval
).
I'm not sure whether I spotted every place the braces are missing, but the code works, so I guess so...
The result is code that is roughly 5% slower for the uses with a single token (benchmarked for 100 keys and 100 key=value pair, as well as 1 blank element).
documentclass[]{article}
ExplSyntaxOn
scan_new:N s__ulrichkeyval_nil
scan_new:N s__ulrichkeyval_mark
scan_new:N s__ulrichkeyval_stop
scan_new:N s__ulrichkeyval_tail
group_begin:
cs_set_protected:Npn __ulrichkeyval_tmp:NN #1#2
{
cs_new:Npn ulrichkeyval_parse:nnn ##1 ##2 ##3
{ __ulrichkeyval_loop_active:nnw {##1} {##2} s__ulrichkeyval_mark ##3 #1 s__ulrichkeyval_tail #1 }
cs_new:Npn __ulrichkeyval_loop_active:nnw ##1 ##2 ##3 #1
{
__ulrichkeyval_if_recursion_tail:w ##3
__ulrichkeyval_end_loop_active:w s__ulrichkeyval_tail
__ulrichkeyval_loop_other:nnw {##1} {##2} ##3 , s__ulrichkeyval_tail ,
__ulrichkeyval_loop_active:nnw {##1} {##2} s__ulrichkeyval_mark
}
cs_new:Npn __ulrichkeyval_split_other:w ##1 = ##2 s__ulrichkeyval_mark ##3 ##4 s__ulrichkeyval_stop
{ ##3 ##1 s__ulrichkeyval_stop s__ulrichkeyval_mark ##2 }
cs_new:Npn __ulrichkeyval_split_active:w ##1 #2 ##2 s__ulrichkeyval_mark ##3 ##4 s__ulrichkeyval_stop
{ ##3 ##1 s__ulrichkeyval_stop s__ulrichkeyval_mark ##2 }
cs_new:Npn __ulrichkeyval_loop_other:nnw ##1 ##2 ##3 ,
{
__ulrichkeyval_if_recursion_tail:w ##3
__ulrichkeyval_end_loop_other:w s__ulrichkeyval_tail
__ulrichkeyval_split_active:w ##3 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_split_active_auxi:w
#2 s__ulrichkeyval_mark __ulrichkeyval_clean_up_active:w
s__ulrichkeyval_stop
{##1} {##2}
__ulrichkeyval_loop_other:nnw {##1} {##2} s__ulrichkeyval_mark
}
cs_new:Npn __ulrichkeyval_split_active_auxi:w ##1 s__ulrichkeyval_stop
{
__ulrichkeyval_split_other:w ##1 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_after_active_error:w
= s__ulrichkeyval_mark __ulrichkeyval_split_active_auxii:w
s__ulrichkeyval_stop
}
cs_new:Npn __ulrichkeyval_split_active_auxii:w
##1 s__ulrichkeyval_nil s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_after_active_error:w
s__ulrichkeyval_stop s__ulrichkeyval_mark
{ __ulrichkeyval_trim:nN { ##1 } __ulrichkeyval_split_active_auxiii:w }
cs_new:Npn __ulrichkeyval_split_active_auxiii:w ##1 ##2 s__ulrichkeyval_nil
{
__ulrichkeyval_split_active:w ##2 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_in_split_error:w
#2 s__ulrichkeyval_mark __ulrichkeyval_split_active_auxiv:w
s__ulrichkeyval_stop
{ ##1 }
}
cs_new:Npn __ulrichkeyval_split_active_auxiv:w
##1 s__ulrichkeyval_nil s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_in_split_error:w
s__ulrichkeyval_stop s__ulrichkeyval_mark
{
__ulrichkeyval_split_other:w ##1 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_in_split_error:w
= s__ulrichkeyval_mark __ulrichkeyval_split_active_auxv:w
s__ulrichkeyval_stop
}
cs_new:Npn __ulrichkeyval_split_active_auxv:w
##1 s__ulrichkeyval_nil s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_in_split_error:w
s__ulrichkeyval_stop s__ulrichkeyval_mark
{ __ulrichkeyval_trim:nN { ##1 } __ulrichkeyval_pair:nnnn }
cs_new:Npn __ulrichkeyval_clean_up_active:w
##1 s__ulrichkeyval_nil s__ulrichkeyval_mark __ulrichkeyval_split_active_auxi:w s__ulrichkeyval_stop s__ulrichkeyval_mark
{
__ulrichkeyval_split_other:w ##1 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_split_other_auxi:w
= s__ulrichkeyval_mark __ulrichkeyval_clean_up_other:w
s__ulrichkeyval_stop
}
cs_new:Npn __ulrichkeyval_split_other_auxi:w ##1 s__ulrichkeyval_stop
{ __ulrichkeyval_trim:nN { ##1 } __ulrichkeyval_split_other_auxii:w }
cs_new:Npn __ulrichkeyval_split_other_auxii:w ##1 ##2 s__ulrichkeyval_nil
{
__ulrichkeyval_split_other:w ##2 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_in_split_error:w
= s__ulrichkeyval_mark __ulrichkeyval_split_other_auxiii:w
s__ulrichkeyval_stop
{ ##1 }
}
cs_new:Npn __ulrichkeyval_split_other_auxiii:w
##1 s__ulrichkeyval_nil s__ulrichkeyval_mark __ulrichkeyval_misplaced_equal_in_split_error:w
s__ulrichkeyval_stop s__ulrichkeyval_mark
{ __ulrichkeyval_trim:nN { ##1 } __ulrichkeyval_pair:nnnn }
cs_new:Npn __ulrichkeyval_clean_up_other:w
##1 s__ulrichkeyval_nil s__ulrichkeyval_mark __ulrichkeyval_split_other_auxi:w s__ulrichkeyval_stop s__ulrichkeyval_mark
{
__ulrichkeyval_if_blank:w ##1 s__ulrichkeyval_nil s__ulrichkeyval_stop __ulrichkeyval_blank_true:w
s__ulrichkeyval_mark s__ulrichkeyval_stop use:n
{ __ulrichkeyval_trim:nN { ##1 } __ulrichkeyval_key:nnn }
}
cs_new:Npn __ulrichkeyval_misplaced_equal_after_active_error:w
s__ulrichkeyval_mark ##1 s__ulrichkeyval_stop s__ulrichkeyval_mark ##2 s__ulrichkeyval_nil
s__ulrichkeyval_mark ##3 s__ulrichkeyval_nil ##4 ##5
{
__kernel_msg_expandable_error:nn
{ kernel } { misplaced-equals-sign }
}
cs_new:Npn __ulrichkeyval_misplaced_equal_in_split_error:w
s__ulrichkeyval_mark ##1 s__ulrichkeyval_stop s__ulrichkeyval_mark ##2 s__ulrichkeyval_nil
##3 ##4 ##5
{
__kernel_msg_expandable_error:nn
{ kernel } { misplaced-equals-sign }
}
cs_new:Npn __ulrichkeyval_end_loop_other:w
s__ulrichkeyval_tail
__ulrichkeyval_split_active:w ##1 s__ulrichkeyval_nil
s__ulrichkeyval_mark __ulrichkeyval_split_active_auxi:w
#2 s__ulrichkeyval_mark __ulrichkeyval_clean_up_active:w
s__ulrichkeyval_stop
##2 ##3
__ulrichkeyval_loop_other:nnw ##4 s__ulrichkeyval_mark
{ }
cs_new:Npn __ulrichkeyval_end_loop_active:w
s__ulrichkeyval_tail
__ulrichkeyval_loop_other:nnw ##1 , s__ulrichkeyval_tail ,
__ulrichkeyval_loop_active:nnw ##2 s__ulrichkeyval_mark
{ }
}
char_set_catcode_active:n { `, }
char_set_catcode_active:n { `= }
__ulrichkeyval_tmp:NN , =
group_end:
cs_new:Npn __ulrichkeyval_pair:nnnn #1 #2 #3 #4
{
__ulrichkeyval_if_blank:w s__ulrichkeyval_mark #2 s__ulrichkeyval_nil s__ulrichkeyval_stop __ulrichkeyval_blank_key_error:w
s__ulrichkeyval_mark s__ulrichkeyval_stop
exp_not:n { #4 { #2 } { #1 } }
}
cs_new:Npn __ulrichkeyval_key:nnn #1 #2 #3
{
__ulrichkeyval_if_blank:w s__ulrichkeyval_mark #1 s__ulrichkeyval_nil s__ulrichkeyval_stop __ulrichkeyval_blank_key_error:w
s__ulrichkeyval_mark s__ulrichkeyval_stop
exp_not:n { #2 { #1 } }
}
cs_new:Npn __ulrichkeyval_if_empty:w #1 s__ulrichkeyval_mark s__ulrichkeyval_stop { }
cs_new:Npn __ulrichkeyval_if_blank:w s__ulrichkeyval_mark #1 { __ulrichkeyval_if_empty:w s__ulrichkeyval_mark }
cs_new:Npn __ulrichkeyval_if_recursion_tail:w s__ulrichkeyval_mark #1 s__ulrichkeyval_tail { }
cs_new:Npn __ulrichkeyval_blank_true:w s__ulrichkeyval_mark s__ulrichkeyval_stop use:n #1 #2 #3 { }
cs_new:Npn __ulrichkeyval_blank_key_error:w s__ulrichkeyval_mark s__ulrichkeyval_stop exp_not:n #1
{
__kernel_msg_expandable_error:nn
{ kernel } { blank-key-name }
}
group_begin:
cs_set_protected:Npn __ulrichkeyval_tmp:n #1
{
cs_new:Npn __ulrichkeyval_trim:nN ##1
{
__ulrichkeyval_trim_auxi:w
##1
s__ulrichkeyval_nil
s__ulrichkeyval_mark #1 { }
s__ulrichkeyval_mark __ulrichkeyval_trim_auxii:w
__ulrichkeyval_trim_auxiii:w
#1 s__ulrichkeyval_nil
__ulrichkeyval_trim_auxiv:w
s__ulrichkeyval_stop
}
cs_new:Npn __ulrichkeyval_trim_auxi:w ##1 s__ulrichkeyval_mark #1 ##2 s__ulrichkeyval_mark ##3
{
##3
__ulrichkeyval_trim_auxi:w
s__ulrichkeyval_mark
##2
s__ulrichkeyval_mark #1 {##1}
}
cs_new:Npn __ulrichkeyval_trim_auxii:w __ulrichkeyval_trim_auxi:w s__ulrichkeyval_mark s__ulrichkeyval_mark ##1
{
__ulrichkeyval_trim_auxiii:w
##1
}
cs_new:Npn __ulrichkeyval_trim_auxiii:w ##1 #1 s__ulrichkeyval_nil ##2
{
##2
##1 s__ulrichkeyval_nil
__ulrichkeyval_trim_auxiii:w
}
cs_new:Npn __ulrichkeyval_trim_auxiv:w s__ulrichkeyval_mark ##1 s__ulrichkeyval_nil ##2 s__ulrichkeyval_stop ##3
{ ##3 { ##1 } }
}
__ulrichkeyval_tmp:n { ~ }
group_end:
cs_new:Nn MyStuff_ProcessInCaseOnlyKey:nnn {
This~is~the~first~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#1.
This~is~the~second~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#2.
This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.par
}
cs_new:Nn MyStuff_ProcessInCaseKeyAndValue:nnnn {
This~is~the~first~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#1.
This~is~the~second~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#2.
This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.
This~is~the~value~which~was~passed~on~by~the~keyval~parser:~#4.par
}
cs_new:Nn MyStuff_ProcessAsKeyval:nnn {
ulrichkeyval_parse:nnn
{ MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2} }
{ MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2} }
{#3}
}
cs_new_eq:NN MyStuff MyStuff_ProcessAsKeyval:nnn
ExplSyntaxOff
begin{document}
MyStuff{A}{B}{key,key=val,val,val=key}
end{document}
Correct answer by Skillmon on May 14, 2021
You should have all arguments expressed:
MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2}{##1}
and
MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2}{##1}{##2}
I mean really: there is no rule why the key and value should be passed as the last arguments. And the code is clearer.
documentclass{article}
ExplSyntaxOn
cs_new_protected:Nn ulrich_keyval_parse:nnn
{
cs_set:Nn __ulrich_keyval_parse_single:n { #1 }
cs_set:Nn __ulrich_keyval_parse_single:nn { #2 }
keyval_parse:NNn __ulrich_keyval_parse_single:n __ulrich_keyval_parse_single:nn { #3 }
}
ulrich_keyval_parse:nnn
{ iow_term:n { key~is~#1 } }
{ iow_term:n { key~is~#1;~value~is~#2 } }
{key,key=val,val,val=key}
begin{document}
cs_new_protected:Nn MyStuff_ProcessInCaseOnlyKey:nnn
{
This~is~the~first~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#1.
This~is~the~second~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#2.
This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.
}
cs_new_protected:Nn MyStuff_ProcessInCaseKeyAndValue:nnnn
{
This~is~the~first~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#1.
This~is~the~second~argument~of~texttt{token_to_str:NMyStuff_ProcessAsKeyval:nnn}:~#2.
This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.
This~is~the~value~which~was~passed~on~by~the~keyval~parser:~#4.
}
cs_new:Nn MyStuff_ProcessAsKeyval:nnn
{
ulrich_keyval_parse:nnn
{ MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2}{##1} }
{ MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2}{##1}{##2} }
{#3}
}
MyStuff_ProcessAsKeyval:nnn { A } { B } { key,key=val }
end{document}
Answered by egreg on May 14, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP