Use column-separator & (ampersand) inside \newenvironment

I'd like to define a grammar environment which basically wraps a tabular environment. Inside the grammar environment I'd like to use an environment called production to insert rows. Example:

\documentclass[a4paper,10pt]{article}

\newenvironment{production}[1]
{\noindent\ignorespaces#1 ::= &}
{\\}

\newenvironment{grammar}
{\begin{tabular}{p{3cm}l}}
{\end{tabular}}

\begin{document}
\begin{grammar}
\begin{production}{XmlStartTag}
...
\end{production}
\begin{production}{XmlEndTag}
...
\end{production}
\end{grammar}

\end{document}


The goal of this code is to put the name of the non-terminal that is being defined in the left column and the actual production into the right column of the tabular.

Unfortunately this fails due to the & and \\ within the production environment. I guess it has something to do with macro expansion. I guess I have to escape the & and \\ with something that is only expanded after the macro was used?

Thanks!

-

The problem is that the \begin{...} and \end{...} pair commands automatically create a "group" so that, in effect, the & and \\ are "out of scope" for the tabular, while inside the "production" environment they just show up at a place where the compiler is not expecting them.

A second problem with your definitions is that, even if they would work, they would be adding an extra \\ at the end of the tabular, adding an unwanted space at the end. Perhaps some more appropriate definitions would be

\documentclass[a4paper,10pt]{article}

\newcommand{\production}[1]{#1 ::= &}
\newenvironment{grammar}{\tabular{p{3cm}l}}{\endtabular}

\begin{document}
\begin{grammar}
\production{XmlStartTag} ... \\
\production{XmlOtherTag} ... \\
\production{XmlEndTag} ...
\end{grammar}
\end{document}


Note no \\ at the end of the last production. Also in the definition of the grammar you don't need to repeat the work of \begin/\end, and you can instead directly use \tabular and \endtabular.

-
Thanks for the clarification and the newline hint! It works as Ulrike Fischer and you propose (using a command). However, I still wonder if there would be an easy way to make it work with an environment. Using an environment I would have more ways to change the formatting of the entire production as one unit. –  desperate Sep 16 '10 at 7:24
No, not an easy way. At least not if you continue to use a tabular around. Can't you do without the tabular and use inside the production environment \parbox, \makebox, minipage, lrbox etc? You could also look at the eqparbox-package. –  Ulrike Fischer Sep 16 '10 at 8:16
Thanks for the pointers! –  desperate Sep 20 '10 at 15:33

EDIT: The answer was completely wrong; sorry to those who had upvoted.

I found a trick when trying to solve this question. You can use the environ package and define the production environment as follows:

\usepackage{environ}
\NewEnviron{production}[1]{%
\toks0={\noindent\ignorespaces#1 ::= &}%
\toks2=\expandafter{\BODY}%
\toks4={\\}%
\xdef\gtemp{\the\toks0 \the\toks2 \the\toks4 }%
\aftergroup\gtemp
}


\BODY is the content of the environment. The code works as follows: store \noindent\ignorespaces#1 ::= & into the token register \toks0, then store \BODY (expanded by \expandafter) into \toks2, and the trailing code \\ into \toks4. Put everything together into the global token list \gtemp. The \xdef expands \the\toks0, \the\toks2, and \the\toks4 (spaces after the numbers are important). Finally, \aftergroup\gtemp places the \gtemp after the end of the group formed by \begin{production}...\end{production}, so that this group does not interfere with groups produced by the tabular. See the answer to the question I mention above.

Two questions for more experienced TeXers: can I use \unexpanded to do this better? Is there a good way of executing a lot of stuff \aftergroup: I hate having to define \gtemp globally.

-
I get the error undefined control sequence (\end{production} ...). Everything works if I delete the ampersand. Do you have any idea how to solve that? –  Dominik Dec 31 '14 at 4:10
Thank you very much @Dominik. My code was simply wrong, now fixed. –  Bruno Le Floch Jan 2 at 9:11
@BrunoLeFloch Concerning your question: You could do \toks2=\expandafter{\BODY}\xdef\gtemp{\unexpanded{\noindent\ignorespaces#1 ::= &} \the\toks2 \unexpanded{\\}}, because you don't expand the contents of \toks0 and \toks4 anyway. For \ a simple \noexpand would also suffice because it is a single token. Regarding the \aftergroup and global dilemma: The problem is that \aftergroup only takes a single token. You could in principle put together all tokens and then iterate over them appending them to the \aftergroup register. This is just much more tedious than a global macro. –  Henri Menke Jan 2 at 11:56
This works. Thank you :-). –  Dominik Jan 2 at 20:51

It has nothing to do with macro expansion but with grouping: Cells in a tabular build a group. You are starting an environment in one group and close it in the next. Use better a command \production.

-
Thanks for the clarification! –  desperate Sep 16 '10 at 7:22

I've implemented Bruno Le Floch's answer with expl3. There it is easier to expand a token list only once.

\documentclass{article}
\pagestyle{empty}
\usepackage{environ,xparse}
\ExplSyntaxOn
\NewEnviron { production } [1]
{
\tl_gclear:N \g_tmpa_tl
\tl_gput_right:Nn \g_tmpa_tl { \noindent\ignorespaces#1 ~ ::= & }
\tl_gput_right:No \g_tmpa_tl { \BODY }
\tl_gput_right:Nn \g_tmpa_tl { \\ }
\group_insert_after:N \g_tmpa_tl
}
\ExplSyntaxOff
\newenvironment{grammar}
{\begin{tabular}{p{3cm}l}}
{\end{tabular}}
\begin{document}

\begin{grammar}
\begin{production}{XmlStartTag}
...
\end{production}
\begin{production}{XmlEndTag}
...
\end{production}
\end{grammar}

\end{document}

-

I suppose having separate prod1 and prodn production commands would allow \\ to be automatically inserted: make prodn prepend \\.

-