6

Is there a good way to store the key of an unknown key-val in expl3? I can get \l_keys_key_tl, but it gets overwritten before it's expanded. I suppose I need to expand it when I store it, but I'm having trouble making that work, especially because I don't want to expand the value yet. Also, I expect to get multiple unknowns, and the order is important, so l3prop isn't appropriate.

Here's a MWE:

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\seq_new:N \l_mymod_seq
\keys_define:nn { mymod } {
  a       .tl_set:N = \tmpa, % some keys are treated special
  % others are grouped with unknowns
  b       .code:n = \seq_put_right:Nn \l_mymod_seq {(B~is~\f{#1})},
  unknown .code:n = \seq_put_right:Nn \l_mymod_seq {(unknown~{\l_keys_key_tl}~is~\f{#1})},
}

\newcommand\setkeys[1]{\keys_set:nn { mymod } { #1 }}
\newcommand\printkeys{\seq_use:Nn \l_mymod_seq {,~}}

\ExplSyntaxOff

\begin{document}
\setkeys{a=5,b=6,c=7,d=8}
\newcommand\f[1]{\textbf{#1}}
A is \f{\tmpa}
\renewcommand\f[1]{\textit{#1}}
\printkeys
\end{document}

MWE output showing the \l_keys_key_tl variable printing the same thing twice

0

2 Answers 2

6

You can avoid code duplication by using self-defined functions and variants thereof. Here \mymod_store:Vn is just the same as \mymod_store:nn but the first argument should be a token list variable (other data types could apply as well) whose value is passed to the main macro.

\documentclass{article}
\usepackage{expl3,xparse}

\ExplSyntaxOn

\seq_new:N \l_mymod_keyvalue_seq
\keys_define:nn { mymod }
 {
  % some keys are special
  a       .tl_set:N = \valueofa,
  % others are grouped with unknowns
  b       .code:n = \mymod_store:nn {B} {#1},
  unknown .code:n = \mymod_store:Vn \l_keys_key_tl {#1},
 }
\cs_new_protected:Nn \mymod_store:nn
 {
  \seq_put_right:Nn \l_mymod_keyvalue_seq {(#1~is~\f{#2})}
 }
\cs_generate_variant:Nn \mymod_store:nn { V }

\NewDocumentCommand{\setkeys}{m}
 {
  \keys_set:nn { mymod } { #1 }
 }
\NewExpandableDocumentCommand\printkeys{}
 {
  \seq_use:Nn \l_mymod_keyvalue_seq {,~}
 }

\ExplSyntaxOff

\newcommand\f[1]{\textbf{#1}}

\begin{document}

\setkeys{a=5,b=6,c=7,d=8}

A is \f{\valueofa}

\renewcommand\f[1]{\textit{#1}}

\printkeys

\end{document}

enter image description here

2
  • (or anyone), can you say why you made \printkeys expandable? For that matter, why the protected \mymod_store:nn? As I understand, expandable commands have constraints that allow them to be used in \edefs, and protected commands don't expand in \edefs, but why did you use them here? The xparse docs have many warnings about using \NewExpandableDocumentCommand so I have thus far avoided them, so why are they necessary here?
    – goodmami
    Commented Mar 23, 2018 at 18:13
  • 1
    @goodmami You might want to use \printkeys in an expansion context. Since \seq_use:Nn is fully expandable, there's no problem in doing it so. Of course, in this case, \textit is likely to break, but the issue is solvable in a different way.
    – egreg
    Commented Mar 23, 2018 at 18:23
8

You can use expansion (Nx) to expand the key and then prevent expansion (\exp_not) for the unknown control sequence.

As @egreg mentioned in the comments you might want \exp_not:n { \f{#1} } instead of \exp_not:N, because this does not expand #1 (the curenntly used one does).

expand

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\seq_new:N \l_mymod_seq
\keys_define:nn { mymod } {
  a       .tl_set:N = \tmpa, % some keys are treated special
  % others are grouped with unknowns
  b       .code:n = \seq_put_right:Nn \l_mymod_seq {(B~is~\f{#1})},
  unknown .code:n = \seq_put_right:Nx \l_mymod_seq {(unknown~\l_keys_key_tl\ is~\exp_not:N \f{#1})},
}

\newcommand\setkeys[1]{\keys_set:nn { mymod } { #1 }}
\newcommand\printkeys{\seq_use:Nn \l_mymod_seq {,~}}

\ExplSyntaxOff

\begin{document}
\setkeys{a=5,b=6,c=7,d=8}
\newcommand\f[1]{\textbf{#1}}
A is \f{\tmpa}
\renewcommand\f[1]{\textit{#1}}
\printkeys
\end{document}
4
  • 1
    Possibly \exp_not:n { \f{#1} } or the text in #1 would be expanded in the unknown case, unlike the b case.
    – egreg
    Commented Mar 23, 2018 at 7:24
  • @egreg You're right, I supposed the #1 should be expanded.
    – TeXnician
    Commented Mar 23, 2018 at 13:17
  • This was useful, and I think I'll use the \exp_not:n solution for a related problem, but I think @egreg's answer is more generally useful to the question as posed. One question for you: why does \l_keys_key_tl\ is insert a space before is but \l_keys_key_tl~is does not? I solved this with braces around the macro, but you must have changed it for a reason. Is there a difference between \ and ~ in expl3 syntax mode?
    – goodmami
    Commented Mar 23, 2018 at 16:53
  • 1
    @goodmami ~ is the LaTeX3 equivalent for an ordinary space. That means that it will look to TeX like a space after a control word and hence is eaten. For it to be visible I changed it to a forced space \ . And the grouping (braces) is something I dislike at that point (I do not like to introduce unnecessary groups).
    – TeXnician
    Commented Mar 23, 2018 at 17:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .