3

I'm new to LaTeX. I learn and I use article class. I struggle learning working with keyval package:

I try to write macro \macro which only uses one mandatory argument where user can provide key value list.

Possible keys should be keyA and keyB.

  • keyA may take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).
  • keyA default should be yes branch.

I'd like keyB to take an arbitrary value and to append this value between parentheses to macro \foo if keyA denotes yes-branch and to append it between square brackets to the macro \bar if keyA denotes no-branch.
Besides this I'd like that keyA and keyB at every call to \macro must be in the key value list and that they can be in the key value list only once - setting one of the keys more often or not at all → error-message.

I don't know how to do this because when \setkeys processes the key and value pairs one by one, it is not known what other key value pair follow and with key value pairs order is not firmly prescribed.

I tried this without success:

\documentclass{article}
\usepackage{keyval}
\def\foo{}
\def\bar{}
\newif\ifkeyAtrue\keyAtruefalse
\makeatletter
\define@key{MyFamily}{keyA}[true]{\keyAtruetrue}
\define@key{MyFamily}{keyB}{
  \ifkeyAtrue
    \def\foo{\foo(#1)}
  \else
    \def\bar{\bar[#1]}
  \fi
}
\makeatother
\newcommand\macro[1]{
  \setkeys{MyFamily}{#1}
  \keyAtruefalse
}
\begin{document}
% This almost works but there is '\foo in \foo'-recursion.
\macro{keyA=true,keyB=first value}
\show\foo\show\bar
% This almost works but there is '\foo in \foo'-recursion and
% '\bar in \bar'-recursion
\macro{keyB=second value}
\show\foo\show\bar
% This dos not work at all. There is recursions and added to \foo, not to \bar.
\macro{keyA=false,keyB=third value}
\show\foo\show\bar
\end{document}

Thank you for help.

3

Some remarks not related to the problem which nonetheless may be useful:

  1. While line-wise and then character-wise reading/processing a .tex-input-file, (La)TeX usually will first remove all space characters (ASCII 32) at the right end of a line of input and then append a return character (ASCII 13) at the right end of that line of input. This is due to the integer parameter \endlinechar usually having the value 13. Then (La)TeX will "look" at that line of input character by character and hereby take that line for a set of instructions for appending tokens (character tokens and control sequence tokens) to the token stream. The return character usually has category code 5 (end of line). After tokenizing a curly brace ({ or }), (La)TeX's reading apparatus usually is in state M (middle of line). (Generally (La)TeX's reading apparatus will be in state M after tokenizing a control symbol token (other than control space; a control symbol token is a control sequence token whose name consists of a single character which does not have category code 11(letter)) or a character-token other than a space token.)

    When (La)TeX encounters a character of category code 5 (end of line) while in state M, (La)TeX will append a space token (character token of category code 10(space) and character code 32) to the token stream.
    In (restricted) horizontal mode such a space token may yield visible horizontal glue.

    Thus: Make sure that lines of input, where the last thing that during reading and tokenizing shall be appended to the token stream either is a control symbol token (control sequence token whose name consists o a single character which is not of category code 11(letter)) or is a character token, end with a comment character (%) in situations where such horizontal glue is undesired.
    As a rule of thumb make sure that lines of input, where the last thing that shall be appended to the token stream is some brace token ({-character-token of category code 1 (begin group) or }-character-token of category code 2 (end group)), end with a comment-character (%).

  2. \bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the names of the corresponding macros I use the all caps variants \FOO and \BAR.


Some remarks related to the problem which probably may be useful:

With the keyval package you cannot have the "function" underlying keyB act depending on the value of keyA/depending on the result of carrying out the "function" underlying keyA.

As you stated yourself:

The order in which these "functions" get carried out is not predictable.
Also it is not predictable whether these "functions" get carried out at all or get carried out more than once.

Instead you can define a family of keys with underlying "functions" for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.

Then you can define a macro which does \setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.

In the example below as flags I use switches created via \newif. I do so because with that example I don't wish LaTeX to load a large amount of packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.

Processing comma separated key-value-lists is about processing macro arguments.
Braces need to be balanced with macro arguments.
Therefore I assume that the "arbitrary values" for keyB are sequences of tokens which are brace balanced. ;-)

When storing such (almost) arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing. The reason is: You retrieve these token sequences by expanding these macros, while during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.

For achieving the doubling of hashes you can use the fact that when \the delivers the content of a token register during an \edef, the hashes therein will be doubled.
In the example below LaTeX 2e's reserved scratch token register \toks@ is used according to the pattern \toks@{⟨tokens⟩}...\edef\macro{\the\toks@}.

If e-TeX extensions are available, you can do \edef\macro{\unexpanded{⟨tokens⟩}}
as shown by Phelype Oleinik as \unexpanded also doubles hashes when carried out during \edef.


You also need forking depending on the phrase/depending on the token sequence provided as value to keyA:
In the example below you find \DetectYesNo which does the forking by means of delimited macro arguments.


You wish to have (almost) arbitrary things appended to the definition text of the macro \foo.

When doing this, you need to make sure that the new definition text of \foo also contains the tokens that form the old definition text of \foo by top-level-expanding \foo.

You could be tempted to do:

\expandafter\def\expandafter\foo\expandafter{\foo⟨tokens to append⟩}

But you need to be careful with such approaches as the amount of hashes will be halved during expansion of \foo. This may lead to problems.

If, e.g., \foo is defined via

\def\foo{%
  \def\bas##1{argument of bas: ##1.}%
}

and you do:

\expandafter\def\expandafter\foo\expandafter{%
  \foo
  \def\bat##1{argument of bat: ##1.}%
}

, then this will not yield

\def\foo{%
  \def\bas##1{argument of bas: ##1.}%
  \def\bat##1{argument of bat: ##1.}%
}

, but it will yield:

\def\foo{%
  \def\bas#1{argument of bas: #1.}%
  \def\bat##1{argument of bat: ##1.}%
}

This will be a problem and therefore will yield an error message as \foo does not process parameters while \foo's definition text does contain #1.

For achieving the hash-doubling you can again apply either \the-expansion on a token register (without e-TeX extensions) or \unexpanded (this requires e-TeX extensions) during \edef, but this time you need to combine that with \expandafter for obtaining the expansion of \foo:

Without e-TeX extensions, using LaTeX 2e's scratch token register \toks@, you can do something like:

\def\foo{%
  \def\bas##1{argument of bas: ##1.}%
}%
% Now let's apply \expandafter for obtaining the expansion of `\foo`:
\toks@\expandafter{%
  \foo
  \def\bat#1{argument of bat: #1.}%
}%
\edef\foo{\the\toks@}%
\show\foo

But the scratch token register \toks@ is reserved for the LaTeX 2e-kernel. This means that it should be reset before any token defined in the kernel will be carried out. This can be achieved by adding a bit of \exchange-trickery:

\long\def\exchange#1#2{#2#1}%
\def\foo{%
  \def\bas##1{argument of bas: ##1.}%
}%
\expandafter\exchange\expandafter{%
  \expandafter\toks@\expandafter{\the\toks@}%
}{%
  % Now let's apply \expandafter for obtaining the expansion of `\foo`:
  \toks@\expandafter{%
    \foo
    \def\bat#1{argument of bat: #1.}%
  }%
  \edef\foo{\the\toks@}%
}%
\show\foo

When e-TeX extensions are available, you can use a combination of \edef, \expandafter and \unexpanded as shown in the answer of Phelype Oleinik:

\def\foo{%
  \def\bas##1{argument of bas: ##1.}%
}%
\edef\foo{%
  \unexpanded\expandafter{%
    \foo
    \def\bat#1{argument of bat: #1.}%
  }%
}%
\show\foo


\documentclass{article}
\usepackage{keyval}

\makeatletter
%%----------------------------------------------------------------------
\newcommand\UD@firstofone[1]{#1}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{%
  \expandafter\UD@secondoftwo\string}\expandafter\expandafter
  \UD@firstoftwo{ }{}\UD@secondoftwo}{\expandafter\expandafter
  \UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%
%% A variant using e-TeX extensions could be: 
%%
%%\newcommand\UD@CheckWhetherNull[1]{%
%%  \romannumeral0\if\relax\detokenize{#1}\relax
%%  \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
%%  {\UD@firstoftwo{\expandafter}{} \UD@firstoftwo}%
%%  {\UD@firstoftwo{\expandafter}{} \UD@secondoftwo}%
%%}%
%%----------------------------------------------------------------------
%% \AppendTokensToMacro{tokens}{\macro}%
%% Appends tokens to definition text of \macro.%
%% (\toks@ is a reserved scratch-token-register in LaTeX 2e.
%% Thus when using it it must be ensured to reset it afterwards.)
%%----------------------------------------------------------------------
\newcommand\AppendTokensToMacro[2]{%
  \expandafter\UD@Exchange\expandafter{%
    \expandafter\toks@\expandafter{\the\toks@}%
  }{%
    \toks@\expandafter{#2#1}%
    \edef#2{\the\toks@}%
  }%
}%
%%
%% A variant using e-TeX extensions could be: 
%%
%%\newcommand\AppendTokensToMacro[2]{%
%%  \edef#2{\unexpanded\expandafter{#2#1}}%
%%}%   
%%----------------------------------------------------------------------
%% Macros \FOO and \BAR
%%----------------------------------------------------------------------
\newcommand*\FOO{}%
\newcommand*\BAR{}%
%%----------------------------------------------------------------------
%% Flags / \if-switches
%%----------------------------------------------------------------------
%%    Flag: Is there a need to print an error-message about keyA not 
%%          being set?
\newif\ifDeliverErrMsgKeyANotProvided
\DeliverErrMsgKeyANotProvidedtrue
%%    Flag: Is there a need to print an error-message about keyB not 
%%          being set?
\newif\ifDeliverErrMsgKeyBNotProvided
\DeliverErrMsgKeyBNotProvidedtrue
%%    Flag: Is there a need to print an error-message about keyA
%%          being provided multiple times?
\newif\ifDeliverErrMsgKeyAProvidedSeveralTimes
\DeliverErrMsgKeyAProvidedSeveralTimesfalse
%%    Flag: Is there a need to print an error-message about keyB
%%          being provided multiple times?
\newif\ifDeliverErrMsgKeyBProvidedSeveralTimes
\DeliverErrMsgKeyBProvidedSeveralTimesfalse
%%    Flag: Is there a need to print an error-message about keyA 
%%          having a value which is not in the true/false-spectrum?
\newif\ifDeliverErrMsgKeyANeitherTrueNorFalse
\DeliverErrMsgKeyANeitherTrueNorFalsefalse
%%    Flag: Has keyA the value true?
\newif\ifKeyAsValueIsTrue
\KeyAsValueIsTruetrue
%%----------------------------------------------------------------------
%% Place-holders for values that are to be used after flag-evaluation:
%%----------------------------------------------------------------------
\newcommand*\MyKeyBvalue{}%
%%----------------------------------------------------------------------
%% Error-messages:
%%----------------------------------------------------------------------
%% \PreambleMacroError
%%......................................................................
%% This macro takes three arguments:
%% A macro name. An error message. The help information.
%% It displays the error message, and sets the error help (the result of
%% typing h to the prompt).
%%----------------------------------------------------------------------
\newcommand*\PreambleMacroError[3]{%
  \GenericError{%
    \space\space\space\@spaces\@spaces\@spaces
  }{%
    LaTeX Error: Inapproriate usage of macro \string#1\on@line.\MessageBreak
    (\string#1 is defined in the document's preamble.)\MessageBreak
    Problem: #2%
  }{%
    Have a look at the comments in the preamble of this document.%
  }{#3}%
}%
%%----------------------------------------------------------------------
%%   Error-message in case a flag-key does not have a value of the 
%%   true/false-spectrum:
%%----------------------------------------------------------------------
\newcommand\DeliverErrMsgKeyNeitherTrueNorFalse[1]{%
  \PreambleMacroError{\macro}{Invalid value for #1}%
  %\PackageError{MyPackage}{\string\macro: Invalid value for #1\on@line}%
  %\@latex@error{\string\macro: Invalid value for #1\on@line}%
  {%
    #1 must have one of the following values:%
    \MessageBreak true/yes/on/1 or false/no/off/0.%
  }%
}%
%%----------------------------------------------------------------------
%%   Error-message in case a mandatory key is not set at all:
%%----------------------------------------------------------------------
\newcommand\DeliverErrMsgKeyNotProvided[1]{%
  \PreambleMacroError{\macro}{Setting for #1 is missing}%
  %\PackageError{MyPackage}{\string\macro: Setting for #1 is missing\on@line}%
  %\@latex@error{\string\macro: Setting for #1 is missing\on@line}%
  {%
    Setting #1 cannot be omitted.%
  }%
}%
%%----------------------------------------------------------------------
%%   Error-message in case a keys is set more times than once:
%%----------------------------------------------------------------------
\newcommand\DeliverErrMsgKeyProvidedSeveralTimes[1]{%
  \PreambleMacroError{\macro}{More than one value for #1}%
  %\PackageError{MyPackage}{\string\macro: More than one value for #1\on@line}%
  %\@latex@error{\string\macro: More than one value for #1\on@line}%
  {%
    For the sake of unambiguity provide a value for #1 exactly once.%
  }%
}%
%%----------------------------------------------------------------------
%%   \DetectYesNo detects whether value is either one of
%%   true/yes/on/1 or one of false/no/off/0
%%   \DetectYesNo{<value>}%
%%               {<tokens if value neither is "yes" nor is "no">}%
%%               {<tokens  if value is "yes">}%
%%               {<tokens if value is "no">}
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not
%% nested in braces:
%%......................................................................
%% \UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%%                         {<Tokens to be delivered in case that
%%                           argument contains no exclamation mark>}%
%%                         {<Tokens to be delivered in case that
%%                           argument contains exclamation mark>}%
%%
\newcommand\UD@GobbleToExclam{}\long\def\UD@GobbleToExclam#1!{}%
\newcommand\UD@CheckWhetherNoExclam[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}%
}%
\newcommand\TrueFalseFork{}
\long\def\TrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!{#2}%
\newcommand\DetectYesNo[1]{\lowercase{\InnerDetectYesNo{#1}}}%
\newcommand\InnerDetectYesNo[4]{%
  \romannumeral0\UD@CheckWhetherNoExclam{#1}{%
  \TrueFalseFork!#1!true!yes!on!1!false!no!off!0!{ #2}%<-case #1 is empty/has no tokens
                !!#1!yes!on!1!false!no!off!0!{ #3}%<-case #1 = true
                !!true!#1!on!1!false!no!off!0!{ #3}%<-case #1 = yes
                !!true!yes!#1!1!false!no!off!0!{ #3}%<-case #1 = on
                !!true!yes!on!#1!false!no!off!0!{ #3}%<-case #1 =  1
                !!true!yes!on!1!#1!no!off!0!{ #4}%<-case #1 = false
                !!true!yes!on!1!false!#1!off!0!{ #4}%<-case #1 = no
                !!true!yes!on!1!false!no!#1!0!{ #4}%<-case #1 = off
                !!true!yes!on!1!false!no!off!#1!{ #4}%<-case #1 = 0
                !!true!yes!on!1!false!no!off!0!{ #2}%<-case #1 something else without exclamation mark
                !!!!%
  }{ #2}%<-case #1 = something else with exclamation-mark.
}%
%%----------------------------------------------------------------------
%% Use the keys of the family MyFamily to change flags and to
%% save values that are to be used later
%%----------------------------------------------------------------------
\define@key{MyFamily}{keyA}[true]{%
   \DeliverErrMsgKeyANotProvidedfalse
   \define@key{MyFamily}{keyA}[true]{%
     \DeliverErrMsgKeyAProvidedSeveralTimestrue
   }%
   \DetectYesNo{#1}%
               {\DeliverErrMsgKeyANeitherTrueNorFalsetrue}%
               {\KeyAsValueIsTruetrue}%
               {\KeyAsValueIsTruefalse}%
}%
\define@key{MyFamily}{keyB}{%
   \DeliverErrMsgKeyBNotProvidedfalse
   \define@key{MyFamily}{keyB}{%
     \DeliverErrMsgKeyBProvidedSeveralTimestrue
   }%
   \AppendTokensToMacro{#1}{\MyKeyBvalue}%
}%
%%----------------------------------------------------------------------
%% Now the macro with evaluation and error-messages ad nauseam ;-) :  
%%----------------------------------------------------------------------
\newcommand\macro[1]{%
  \begingroup
  \setkeys{MyFamily}{#1}%
  %
  % Print Error-Messages if necessary:
  %
  \ifDeliverErrMsgKeyANeitherTrueNorFalse\DeliverErrMsgKeyNeitherTrueNorFalse{keyA}\fi
  \ifDeliverErrMsgKeyANotProvided\DeliverErrMsgKeyNotProvided{keyA}\fi
  \ifDeliverErrMsgKeyBNotProvided\DeliverErrMsgKeyNotProvided{keyB}\fi
  \ifDeliverErrMsgKeyAProvidedSeveralTimes\DeliverErrMsgKeyProvidedSeveralTimes{keyA}\fi
  \ifDeliverErrMsgKeyBProvidedSeveralTimes\DeliverErrMsgKeyProvidedSeveralTimes{keyB}\fi
  %
  % Perform the adding to \FOO or \BAR in case no error-messages were
  % printed:
  %
  \ifDeliverErrMsgKeyANotProvided\expandafter\UD@secondoftwo\else\expandafter\UD@firstofone\fi{%
    \ifDeliverErrMsgKeyBNotProvided\expandafter\UD@secondoftwo\else\expandafter\UD@firstofone\fi{%
      \ifDeliverErrMsgKeyAProvidedSeveralTimes\expandafter\UD@secondoftwo\else\expandafter\UD@firstofone\fi{%
        \ifDeliverErrMsgKeyBProvidedSeveralTimes\expandafter\UD@secondoftwo\else\expandafter\UD@firstofone\fi{%
          \ifDeliverErrMsgKeyANeitherTrueNorFalse\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi{%
            \ifKeyAsValueIsTrue\KeyAsValueIsTruetrue\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi{%
              \expandafter\endgroup\expandafter\AppendTokensToMacro\expandafter{\expandafter(\MyKeyBvalue)}{\FOO}%
            }{%
              \expandafter\endgroup\expandafter\AppendTokensToMacro\expandafter{\expandafter[\MyKeyBvalue]}{\BAR}%
            }%
          }%
        }%
      }%
    }%
  }%
  \endgroup
}%
\makeatother

\begin{document}
\noindent
\verb|\macro{keyA=true, keyB=Value in first call}| - now you have:\\
\macro{keyA=true, keyB=Value in first call}%
\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%
%
\null\hrulefill\null\\
\verb|\macro{keyA=yes, keyB=Value in second call}| - now you have:\\
\macro{keyA=yes, keyB=Value in second call}%
\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%
%
\null\hrulefill\null\\
\verb|\macro{keyA=0, keyB=Value in third call}| - now you have:\\
\macro{keyA=0, keyB=Value in third call}%
\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%
%
\null\hrulefill\null\\
\verb|\macro{keyA=Off, keyB=Value in fourth call}| - now you have:\\
\macro{keyA=Off, keyB=Value in fourth call}%
\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%
%
\null\hrulefill\null\\
\verb|\macro{keyA, keyB=Value in fifth call}| - now you have:\\
\macro{keyA, keyB=Value in fifth call}%
\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%
%
% Let's trigger some error-messages:
%
%\null\hrulefill\null\\
%\verb|\macro{keyA=woozle, }| - now you have:\\
%\macro{keyA=woozle, }%
%\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%
%
%\null\hrulefill\null\\
%\verb|\macro{keyA=1, keyA=false, keyB=Value in sixth call}| - now you have:\\
%\macro{keyA=1, keyA=false, keyB=Value in sixth call}%
%\texttt{\string\FOO: \meaning\FOO\\\string\BAR: \meaning\BAR\\}%

\end{document}

enter image description here

4

The \foo in \foo recursion you mentioned happens because when you do \def\foo{\foo(something else)}, the inner \foo is not expanded. If you were yo use that \foo TeX would soon explode: TeX capacity exceeded, sorry [input stack size=5000].

To have \foo contain whatever \foo was, plus the new contents, you can use \edef (expand def) and \unexpanded, like this:

\edef\foo{\unexpanded\expandafter{\foo(more stuff)}}

then \foo will be (something else)(more stuff).

Now, the keyA=false wasn't working because you defined keyA with:

\define@key{MyFamily}{keyA}[true]{\keyAtruetrue}

so it wouldn't matter which value you passed to keyA, at the end it would execute \keyAtruetrue, which is not what you wanted.

I defined a simple \teststring{<str a>}{<str b>}{<true>}{<false>} macro for you which compares the two strings <str a> and <str b> then returns <true> if they are equal or <false> if they are not. Then I changed the definition of keyA to cope with all the possibilities true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).

I also added a couple of % at the end of the lines to avoid spurious spaces.

And, as Ulrich Diez mentions in his answer (and I completely overlooked) you should be careful when using \def because you can override commands without knowing. I replaced \bar by \rab (I'm not creative :)

When running the code I got this in the terminal:

> \foo=macro:
->(first value).
l.51 \show\foo
              \show\rab
? 
> \rab=macro:
->.
l.51 \show\foo\show\rab

? 
> \foo=macro:
->(first value).
l.55 \show\foo
              \show\rab
? 
> \rab=macro:
->[second value].
l.55 \show\foo\show\rab

? 
> \foo=macro:
->(first value).
l.59 \show\foo
              \show\rab
? 
> \rab=macro:
->[second value][third value].
l.59 \show\foo\show\rab

?

Full code:

\documentclass{article}
\usepackage{keyval}
\def\foo{}
\def\rab{}
\newif\ifkeyAtrue\keyAtruefalse
\makeatletter
\def\teststring#1#2{%
  \edef\@tempa{\detokenize{#1}}%
  \edef\@tempb{\detokenize{#2}}%
  \ifx\@tempa\@tempb
    \@valid@keytrue
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\newif\if@valid@key
\define@key{MyFamily}{keyA}[true]{%
  \@valid@keyfalse
  \teststring{#1}{true}{\keyAtruetrue}
    {\teststring{#1}{yes}{\keyAtruetrue}
      {\teststring{#1}{on}{\keyAtruetrue}
        {\teststring{#1}{1}{\keyAtruetrue}
          {}}}}%
  \unless\if@valid@key
    \teststring{#1}{false}{\keyAtruefalse}
      {\teststring{#1}{no}{\keyAtruefalse}
        {\teststring{#1}{off}{\keyAtruefalse}
           {\teststring{#1}{0}{\keyAtruefalse}
            {}}}}%
  \fi
  \unless\if@valid@key
    \PackageError{}{Invalid option `#1' for keyA}{}
  \fi
}
\define@key{MyFamily}{keyB}{% <--
  \ifkeyAtrue
    \edef\foo{\unexpanded\expandafter{\foo(#1)}}% <--
  \else
    \edef\rab{\unexpanded\expandafter{\rab[#1]}}% <--
  \fi
}
\makeatother
\newcommand\macro[1]{% <--
  \setkeys{MyFamily}{#1}% <--
  \keyAtruefalse
}
\begin{document}
% This almost works but there is '\foo in \foo'-recursion.
\macro{keyA=true,keyB=first value}
\show\foo\show\rab
% This almost works but there is '\foo in \foo'-recursion and
% '\rab in \rab'-recursion
\macro{keyB=second value}
\show\foo\show\rab
% This dos not work at all. There is recursions and added to \foo, not to \rab.
% \tracingall
\macro{keyA=false,keyB=third value}
\show\foo\show\rab
\end{document}

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.