3

I have two families of keyval arguments, and two functions, and I want them to behave like on toy MWE below.

\documentclass{article}

\usepackage{xkeyval}
\usepackage{xparse}

\makeatletter
\define@key{A}{a1}{\def\a@aOne{#1}}
\define@key{A}{a2}{\def\a@aTwo{#1}}

\DeclareDocumentCommand{\A}{ m }{%
    \begingroup
        \setkeys{A}{a1={}, a2={}, #1}
        \fbox{$a_1$ = \a@aOne, $a_2$ = \a@aTwo}
    \endgroup
}

\define@key{B}{left}{\def\b@left{#1}}
\define@key{B}{right}{\def\b@right{#1}}

\DeclareDocumentCommand{\B}{ m }{%
    \begingroup
        \setkeys{B}{left={}, right={}, #1}
        \A{\b@left} \A{\b@right}
    \endgroup
}

\makeatother


\begin{document}

    \A{a1=1, a2=2}

    \B{left={a1=1, a2=2}, right={a1=1, a2=2}}

\end{document}

However I get an error that ! Package xkeyval Error: 'a1=1, a2=2' undefined in families 'A' at line with using commandB`.

After reading this question I understood why it happens, but could not apply given suggestions in my case. How to make proper parsing in this case?

Thanks.

3

For this case the solution is simply to expand the \b@... keys using \expandafter such that their expanded values are passed to \A. So change your \B definition to the following:

\DeclareDocumentCommand{\B}{ m }{%
    \begingroup
        \setkeys{B}{left={}, right={}, #1}%
        \expandafter\A\expandafter{\b@left}%
        \expandafter\A\expandafter{\b@right}%
    \endgroup
}

Full updated example (with some extra %s added at the end of lines to prevent extra spaces in the output):

\documentclass{article}

\usepackage{xkeyval}
\usepackage{xparse}

\makeatletter
\define@key{A}{a1}{\def\a@aOne{#1}}
\define@key{A}{a2}{\def\a@aTwo{#1}}

\DeclareDocumentCommand{\A}{ m }{%
    \begingroup
        \setkeys{A}{a1={}, a2={}, #1}%
        \fbox{$a_1$ = \a@aOne, $a_2$ = \a@aTwo}%
    \endgroup
}

\define@key{B}{left}{\def\b@left{#1}}
\define@key{B}{right}{\def\b@right{#1}}

\DeclareDocumentCommand{\B}{ m }{%
    \begingroup
        \setkeys{B}{left={}, right={}, #1}%
        \expandafter\A\expandafter{\b@left}%
        \expandafter\A\expandafter{\b@right}%
    \endgroup
}

\makeatother

\begin{document}

    \A{a1=1, a2=2}

    \B{left={a1=1, a2=2}, right={a1=3, a2=4}}

\end{document}

outputs

enter image description here

6

I would go with l3keys directly:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\evalA}{m}
 {
  \robur_eval_a:n { #1 }
 }
\NewDocumentCommand{\evalB}{m}
 {
  \robur_eval_b:n { #1 }
 }

\keys_define:nn { robur/A }
 {
  a1 .tl_set:N = \l__robur_A_aone_tl,
  a2 .tl_set:N = \l__robur_A_atwo_tl,
 }
\keys_define:nn { robur/B }
 {
  left .tl_set:N = \l__robur_B_left_tl,
  right .tl_set:N = \l__robur_B_right_tl,
 }

\cs_new_protected:Nn \robur_eval_a:n
 {
  \group_begin:
  \keys_set:nn { robur/A } { a1=, a2=, #1 }
  \fbox{$a\sb{1}=\l__robur_A_aone_tl$,~$a\sb{2}=\l__robur_A_atwo_tl$}
  \group_end:
 }
\cs_generate_variant:Nn \robur_eval_a:n { V }

\cs_new_protected:Nn \robur_eval_b:n
 {
  \group_begin:
  \keys_set:nn { robur/B } { left=, right=, #1 }
  \robur_eval_a:V \l__robur_B_left_tl
  \robur_eval_a:V \l__robur_B_right_tl
  \group_end:
}
\ExplSyntaxOff

\begin{document}

\evalA{a1=1, a2=2}

\evalB{left={a1=1, a2=2}, right={a1=3, a2=4}}

\end{document}

enter image description here

No \expandafter, thanks to the V variant: \robur_eval_a:V <tl variable> is the same as

\robur_eval_a:n { <contents of the tl variable> }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.