Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I would like to be able to create a command (using pgfkeys) that can take inputs that are of a limited set of forms, and for which certain pieces will come from a limited set of values. More specifcally, any input will have some piece A (which will come from a known list), and for some values of A, additional information #1, #2, etc. (not from a known list) can also be specified, using a limited set of forms, but need not be.

For a made-up example, let's say that my command is \meal, and A will be an element of the list

chicken, beef, tofu

When A is either chicken or beef, the following forms of additional information can be specified like this:

chicken with #1
chicken on a bed of #1
beef with #1
beef with #1 or #2

I would like to have the greatest possible versatility in what happens once the correct form is determined, and the input is understood. For example, suppose I want

\meal{chicken}                                ->   Chicken
\meal{chicken with #1}                 ->   CHICKEN (with #1? uggh)
\meal{chicken on a bed of #1}    ->   #1, underneath chicken
\meal{beef}                                      ->   Beef
\meal{beef with #1}                       ->   Beef and a side of #1
\meal{beef with #1 or #2}           ->   Beef with your choice of #1 or #2 as a side
\meal{tofu}                                      ->   Tofu

I realize this is already a pretty tall order (ha ha), but for some modicum of simplicity, assume that we will never need to write

\meal{chicken on a bed of beef with chicken}

or other things that would be confusing to the computer.

Now, I was doing some preliminary testing (before trying to make something that did all of the things I wanted), and I ran into a problem. The code I wrote, after being adapted to this made-up example, would be

\documentclass{article}
\usepackage{pgfkeys}
\pgfkeys{%
    mealname/.initial = ,%
    overform/.style args = {#1 over #2}{mealname = #1 over #2},%
    andform/.style args  = {#1 and #2}{mealname = #1 and #2}%
    noform/.style        = {mealname = #1}%
}
\newcommand{\meal}[1]{%
\pgfkeys{%
    overform/.try  = #1,%
    andform/.retry = #1,%
    noform/.retry  = #1,%
    mealname/.get  = \mealname}%
\mealname%
}

\begin{document}

\meal{A over B}

\end{document}

This runs fine; however, when I replace A over B with A and B, I get the error

File ended while scanning use of \pgfkeys@code

What I assume is happening is that pgfkeys is attempting to find an occurrence of the word over in the input A and B, and is in an infinite loop or in one way or another breaks because it cannot find it. I had hoped that after failing to find over anywhere, an error would be thrown that told /.try to stop, but this does not appear to be the case.

Now, I realize that what I am asking for may be a little silly, given that pgfkeys is for "key-value arguments", because a solution that is much easier to code (but slightly less intuitive to use) would be to have each of the different forms (chicken, chicken with, etc.) be its own key, and accept inputs like this:

\meal{chicken}
\meal{chicken with = #1}
...

or alternatively, have each value of A be a family, with the forms as subkeys; e.g., calling the key chicken would /.cd us into the /chicken/ family, where there are keys /chicken/with and /chicken/on a bed of, so that one could write

\meal{chicken}
\meal{chicken, with = #1}
...

Perhaps I am just being unreasonably picky in wanting to avoid the equals signs; but to the greatest extent possible, I would want the code of my document to model natural English expressions (in addition to just wanting to figure out how to do it out for the challenge / out of curiosity).

So, my question is: is there any way of achieving my desired command with pgfkeys? Is there some other package or approach that can do it? Assuming my approach was at least somewhat on the right track: is there any way I can use the /.style args key handler (perhaps together with the /.try handler?) to determine which form the input is, and do the correct operations for that form, without running into an infinite loop trying to test whether the input is of a given form?

share|improve this question
    
What do you think about \meal{meal=chicken, with=<1> or <2>, on a bed of=<3>} (or \meal{chicken}{with=…, on a bed of=…}) where meal is a .is choice key (so you can pre-define which values are allowed), and or <2> is optional (as well as the use of with and on a bed of, of course). With the filtering facilities of PGF keys it is probably also doable to deactivate any use of with and on a bed of when meal=Tofu. –  Qrrbrbirlbel Apr 28 '13 at 3:01
add comment

1 Answer

You can do the following.

This doesn’t check much, though. So you could say

\meal{Tofu with something}

without raising an error.

Code

\documentclass{article}
\usepackage{pgf,pgfkeys}
\makeatletter
\newcommand*{\kitchenset}{\pgfqkeys{/kitchen}}
\newcommand*{\kitchenalso}{\pgfqkeysalso{/kitchen}}
\newcommand*{\qrr@pgfutil@in@}[2]{%
    \pgfutil@in@{#1}{#2}%
    \ifpgfutil@in@
        \expandafter\pgfutil@firstoftwo
    \else
        \expandafter\pgfutil@secondoftwo
    \fi
}
\kitchenset{
    mealname/.initial=Nothing,
    mealwith/.initial=nothing,
    mealwithor/.initial=nothing,
    mealbed/.initial=a plate,
    @meal/.code={%
        \qrr@pgfutil@in@
            { with }{#1}
            {\kitchenalso{@meal with={#1}}}
            {%
                \qrr@pgfutil@in@
                    { on a bed of }{#1}
                    {\kitchenalso{@meal bed={#1}}}
                    {\kitchenalso{mealname={#1}}}%
            }%
    },
    @meal with/.code args={#1 with #2}{%
        \kitchenalso{mealname={#1}}
        \qrr@pgfutil@in@
            { or }{#2}
            {\kitchenalso{@meal with or={#2}}}
            {\kitchenalso{mealwith={#2}}}%
    },
    @meal with or/.style args={#1 or #2}{%
        mealwith={#1},
        mealwithor={#2},
    },
    @meal bed/.style args={#1 on a bed of #2}{%
        mealname={#1},
        mealbed={#2},
    }
}
\makeatother

\newcommand*{\kitchenvalues}{%
    Meal: \pgfkeysvalueof{/kitchen/mealname} with \pgfkeysvalueof{/kitchen/mealwith} (or \pgfkeysvalueof{/kitchen/mealwithor}) on a bed of \pgfkeysvalueof{/kitchen/mealbed}.}

\newcommand*{\meal}[1]{%
    \begingroup
        \kitchenset{@meal={#1}}%
        \kitchenvalues
    \endgroup}
\begin{document}
\meal{xxx}

\meal{Beef}

\meal{Beef with Salad}

\meal{Beef with Salad or French Fries}
\end{document}

Output

enter image description here

share|improve this answer
    
Thanks, I appreciate your answer - could you explain the principle behind this extraction of different pieces of the input? It definitely seems like what I'm after, but I can't seem to find the relevant section in the TikZ/PGF 2.10 manual that explains the operation of pgfutil. Also, as currently written, I'm afraid it doesn't really meet my goal of versatility in the output, though I'll try to modify that aspect of your code on my own for a bit. –  Zev Chonoles Apr 28 '13 at 3:57
    
The \pgfutil@in@ isn’t explained in the manual, but we have a good explanation on TeX.SX: TikZ: pgfutil-common: pgfutil@in@ - how does it work? — Have you seen my comment on the question? The syntax provided by my answer is not very versatile and is hard to maintain. Parsing all possible input streams may become very painful. Let the user do some of the parsing by adding , at the right places. The macro \kitchenvalues (and thus the output) can be adjusted, of course. –  Qrrbrbirlbel Apr 28 '13 at 14:44
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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