5

In a document I'm writing I have a nested tabular structure that I want to capture as a macro for reuse. I'd like to define the nesting using a new environment but it doesn't seem to work. I have tried three approaches: using \newenvironment, using \NewEnviron and using a normal macro. The macro works but the environments do not.

Here's a small example showing a simplified version of what I'm doing and the issues I'm running into:

\documentclass{article}

\usepackage{environ}

\newenvironment{testTabA}{%
    \multicolumn{3}{l}\begingroup
        \begin{tabular}{ll}
}{%
        \end{tabular}
    \endgroup \\
}

\NewEnviron{testTabB}{%
    \multicolumn{3}{l}{%
        \begin{tabular}{ll}
            \BODY
        \end{tabular}
    } \\
}

\newcommand\testTabC[1]{%
    \multicolumn{3}{l}{%
        \begin{tabular}{ll}
            #1
        \end{tabular}
    } \\
}

\begin{document}
    % No macros: This works
    \begin{tabular}{lll}
        1 & 2 & 3 \\
        \multicolumn{3}{l}{
            \begin{tabular}{ll}
                a & b \\
            \end{tabular}
        } \\
    \end{tabular}

    % newenvironment: This fails with "Misplaced \omit"
    \begin{tabular}{lll}
        1 & 2 & 3 \\
        \begin{testTabA}
            a & b \\
        \end{testTabA}
    \end{tabular}

    % NewEnviron: This fails with "Argument of \testTabB has an extra }"
    \begin{tabular}{lll}
        1 & 2 & 3 \\
        \begin{testTabB}
            a & b \\
        \end{testTabB}
    \end{tabular}

    % Macro: This works
    \begin{tabular}{lll}
        1 & 2 & 3 \\
        \testTabC{
            a & b \\
        }
    \end{tabular}
\end{document}

As a fairly new user of TeX, I have no idea what's happening. Can anyone explain why the environment examples are behaving as they are?

6
  • The most evident quirk is the fact that \multicolumn should come first in a cell (after expansion); using it hidden in an environment will never work, because \begin does assignments and this closes the “window” where \multicolumn can be used.
    – egreg
    Jan 6, 2014 at 11:42
  • 1
    If you go ` \multicolumn{3}{l}\begingroup` then the 3rd argument to multicolumn is (just) \begingroup and anything that happens after that is accidental artefact of the expansion. Jan 6, 2014 at 11:46
  • @DavidCarlisle Thanks for educating me. Given egreg's answer, I think I need to use a different approach anyway. But I'm curious to know how you would cause the expansion to work the way I intended? I've seen {\ifnum0=`}\fi somewhere, would that work?
    – David Roe
    Jan 6, 2014 at 12:25
  • 1
    the {\ifnum0=}\fi is used to "hide" the inner &` but as egreg says the definition of \begin is non-expandable so you are doomed, unless you locally redefine \begin to be expandable. the blkarray pakage allows for block "environments" that essentially hide \multicolumn as you wish but it is massively fragile code that I wouldn't really suggest using, you could always have a look at its source for ideas though:-) Jan 6, 2014 at 12:41

1 Answer 1

6

When TeX is starting a cell in an alignment built over the primitive \halign (this is the case with tabular), it wants to check whether a special token called \omit comes first.

Wait a moment! What's \omit? It's not in the manual!

No it isn't in the manual: it's another TeX primitive and LaTeX users are not supposed to use it or even know about it. To put it simple, \multicolumn is defined in such a way that, after some macro expansions, it starts with \omit. Actually, its expansion starts with \multispan and it's this macro that eventually produces \omit:

% latex.ltx, line 5053:
\long\def\multicolumn#1#2#3{\multispan{#1}\begingroup
  \@mkpream{#2}%
  \def\@sharp{#3}\set@typeset@protect
  \let\@startpbox\@@startpbox\let\@endpbox\@@endpbox
  \@arstrut \@preamble\hbox{}\endgroup\ignorespaces}
% latex.ltx, line 5197:
\def\multispan{\omit\@multispan}

In order to see whether \omit comes along, at the beginning of an alignment cell it expands macros. The process stops as soon as an unexpandable token is found. The most common case is finding a character, which is of course unexpandable; but also \relax will stop this search.

If \omit is found after this unexpandable token, an error of Misplaced \omit is raised. A minimal example is

\begin{tabular}{ll}
\relax\multicolumn{2}{c}{a}
\end{tabular}

In your case, you have \begin{testTabA} at the start of the cell. Now \begin is defined as

% latex.ltx, line 3944:
\def\begin#1{%
  \@ifundefined{#1}%
    {\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}%
    {\def\reserved@a{\def\@currenvir{#1}%
     \edef\@currenvline{\on@line}%
     \csname #1\endcsname}}%
  \@ignorefalse
  \begingroup\@endpefalse\reserved@a}

The \@ifundefined{...}{...}{...} by itself would give no problem, because it's expandable:

% latex.ltx, line 788:
\def\@ifundefined#1{%
  \expandafter\ifx\csname#1\endcsname\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}

But now the problem happens: since the environment testTabA is defined, the “false” branch is followed, so TeX sees

\def\reserved@a{...}

and this ends the “time frame” where \omit is legal. The same happens if \NewEnviron is used instead of \newenvironment, because the problem is in \begin.

So, \multicolumn can never be hidden in an environment, for the purpose you have in mind. With the macro approach there is no problem, but the macro should not have optional arguments (unless it's defined with \DeclareExpandableDocumentCommand from xparse, see Custom macro for multicolumn with & without xparse).

1
  • Thank you. That was a great explanation of what's going on.
    – David Roe
    Jan 6, 2014 at 12:22

You must log in to answer this question.

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