1

Here's a little code where the key-value allowed by the macro is an unwanted feature : for example, single[both = OK, only = KO] doesn't give any error as I'd hoped. But on the other hand, I would also like to eat this kind of unwanted keys: see the call of \single inside \both. Concretely, I would like to use \single options when calling \both, but also allows more options from \both.

\documentclass{article}

\ExplSyntaxOn

\tl_new:N \l_mbc_both_tl
\tl_new:N \l_mbc_only_tl

\keys_define:nn { strict-mode } {
  both .tl_set:N  = \l_mbc_both_tl,
  both .groups:n  = { single-use },
  only .tl_set:N  = \l_mbc_only_tl,
  only  .groups:n = { both-use },
}


\NewDocumentCommand{\single}{ O{} }{
  \group_begin:
    \keys_set_groups:nnn { strict-mode }
                         { single-use }
                         { #1 }

    Single used...
  \group_end:
}


\NewDocumentCommand{\both}{ O{} }{
  \group_begin:
    \keys_set:nn { strict-mode } { #1 }

    Both used... ~ \single[#1]
  \group_end:
}

\ExplSyntaxOff


\begin{document}

\single[both = OK]

\single[both = OK, only = KO]

\single[after = math KO]

\both[both = OK]

\both[both = OK, only = OK]

\both[after = math OK]

\end{document}

Is there a way of defining sub-keys that are only allowed for one environment?

4
  • 1
    code snippets are evil, always show something that one can actually test. Commented Sep 22, 2023 at 22:23
  • Thanks for providing the code. Could you explain what output you want? As far as I can see \both allows all the keys and \single processes only a subset. I'm not sure what you want to do, because I thought that's what you were aiming for. (But I'm also not clear what is supposed to be done with the values or what after is meant to do?)
    – cfr
    Commented Sep 23, 2023 at 1:28
  • 1
    Oh, wait, is the point about errors? Why don't you just define one set as { mypkg / strict~mode / single } and the other as { mypkg / strict-mode / both }. Set normally for \single. Set defined for \both and catch the residue, then set the residue against single. That way anything not in the first set will generate an error for \single and anything not in either will generate an error for \both.
    – cfr
    Commented Sep 23, 2023 at 1:33
  • You're right, I was going in the wrong way.
    – projetmbc
    Commented Sep 23, 2023 at 6:21

1 Answer 1

3

One approach is to use two paths. Then process the command which should accept additional keys by first processing known keys against one path and then processing the remainder against the other. This way, both commands will generate errors for completely unknown keys (after) and the more restrictive command will also generate errors for keys defined exclusively for the more expansive command (only).

In the code below, I've prevented errors by typesetting an 'unknown key' message, just for the sake of the example. Obviously, you would either redefine this in your actual code or just delete it and use the default messaging.

\documentclass{article}

\ExplSyntaxOn
% 
% internal names should be double-underscored
\tl_new:N \l__mbc_both_tl
\tl_new:N \l__mbc_only_tl

\keys_define:nn { mbc / strict-mode / common } {% try to guarantee unique name space
  both .tl_set:N  = \l__mbc_both_tl,
  unknown .code:n = { % just for testing
    \texttt{\l_keys_key_str}  ~ not ~ recognised!  \par   
  },
}
\keys_define:nn { mbc / strict-mode / exclusive } {% try to guarantee unique name space
  only .tl_set:N  = \l__mbc_only_tl,
}


\NewDocumentCommand {\single}{ O{} }{
  \group_begin:
    \keys_set:nn { mbc / strict-mode / common  } { #1 }
    \texttt{\textbackslash l \_\_ mbc \_ both \_ tl} ~ is ~ \texttt{\l__mbc_both_tl}\par
    \texttt{\textbackslash  l \_\_ mbc \_ only \_ tl} ~ is ~ \texttt{\l__mbc_only_tl}\par
  \group_end:
}


\NewDocumentCommand {\both}{ O{} }{
  \group_begin:
    \keys_set_known:nnN { mbc / strict-mode / exclusive } { #1 } \l_tmpa_tl
    \keys_set:nV { mbc / strict-mode / common } \l_tmpa_tl
    \texttt{\textbackslash l \_\_ mbc \_ both \_ tl} ~ is ~ \texttt{\l__mbc_both_tl}\par
    \texttt{\textbackslash  l \_\_ mbc \_ only \_ tl} ~ is ~ \texttt{\l__mbc_only_tl}\par
  \group_end:
}

\ExplSyntaxOff


\begin{document}

\single[both = OK]

\single[both = OK, only = KO]

\single[after = math KO]

\both[both = OK]

\both[both = OK, only = OK]

\both[after = math OK]

\end{document}

output (with typeset 'errors')

9
  • My initial conception of the API was totally wrong. Thank you for getting me back on track.
    – projetmbc
    Commented Sep 23, 2023 at 6:50
  • In your solution, you don't call \single from \both. The idea is to call \single with the filtered key-val options.
    – projetmbc
    Commented Sep 23, 2023 at 6:59
  • @projetmbc I just did that for the example. You can call single from \both with the residue of keys. I'm afraid I don't see the problem.
    – cfr
    Commented Sep 23, 2023 at 7:15
  • 1
    @projetmbc \exp_last_unbraced:NNV \single [ \l_tmpa_tl ].
    – Skillmon
    Commented Sep 23, 2023 at 10:11
  • 2
    @projetmbc Not really. There's a few lines of code initially, but then you just update the internal function. As I understand it \NewDocumentCommand etc. should be used for handling user input and then call expl3 functions. People (including me) don't necessarily do that, but that's the picture the l3 framework is aiming at. So you handle expansion, when you can, using variants and the argument specification is in the names of the control sequences. But, as I say, people don't necessarily do that ;).
    – cfr
    Commented Sep 23, 2023 at 18:07

You must log in to answer this question.

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