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'd like to create an environment that basically behaves like amsmath's (v. 2.13) align environment, i.e. line numbering, \label, \tag, \intertext ... are available.

Since I don't want to reinvent the wheel I naively thought I could just use a modified version of \align@preamble (lines 2075ff. of amsmath.sty). However, this won't work if I try to use the cell contents as argument of a command.

\documentclass{article}
\usepackage{amsmath}
\makeatletter
\def\mytest#1{#1}
% when defined without argument there's no problem:
% \def\mytest{\bfseries}

% this is my modified version of \align@preamble
\def\my@align@preamble{%
   &\hfil
    \strut@
    \setboxz@h{\@lign\mytest{##}}% this line causes trouble
    \ifmeasuring@\savefieldlength@\fi
    \set@field
    \tabskip\z@skip
   &\setboxz@h{\@lign{}##}%
    \ifmeasuring@\savefieldlength@\fi
    \set@field
    \hfil
    \tabskip\alignsep@
}
\newenvironment{test}{%
  \let\align@preamble\my@align@preamble
  \align}{%
  \endalign}
\makeatother
\begin{document}
\begin{test}
 a & b \\
 c & d
\end{test}
\end{document}

The code above throws the error

! Misplaced alignment tab character &.
\@tempa ->\endgroup &
                     &
l.31 \end{test}

This let's me guess -- but I might be terribly wrong -- that the command somehow closes the group opened by \halign\bgroup (in \align@ (lines 1546ff.) where \align@preamble is used) but I'm unable to figure out where and/or why that happens. It's not quite so easy to follow the code of amsmath.sty, unfortunately, at least not for me...

Can someone point me to the right direction? Or is there an alternative how I could apply a macro to each cell?

share|improve this question
add comment

1 Answer

up vote 4 down vote accepted

In an original version of this answer I wrote that

You can't easily do

\mytest{##}

in an halign preamble basically the closing brace isn't inserted until the & at the end of the cell is seen, which means that the macro argument isn't correctly parsed.

@egreg pointed out that this wasn't really true as you can put macro calls around the template in primitive halign syntax. (In fact your MWE already has examples of this). What is true but I'd only half remembered is that it is hard to do this with a LaTeX syntax table as (essentially) the \mytest{ is inserted at the start of the cell, but the } is only inserted when the end of the cell is seen. This works for cells not in the last column, where the primitive & ends the cell even when TeX is searching for the argument to \mytest but in the last column neither \\ nor \end{align} trigger the end of cell template as they need to be expanded to be recognised as end of cell constructs, and Tex doesn't expand while scanning for macro arguments.

So... the general approach is almost impossible to make work with normal LaTeX syntax (in blockarray years ago I mandated that the last row end with \\ and locally \let \\ to \cr so that it was a primitive cell end).

However your MWE only needed to access the first column, so things are easier. the problem there is that align has a hidden last row with internal code (actually two different last rows as the whole alignment is set twice for measuring reasons). Applying \mytest to that internal code upsets it somewhat.

So this takes a two stage approach, it checks if the next token is \global or \@empty which it assumes is the internal code from the two last rows, in which case it just re-inserts the token and does not surround it with \mytest. To make things more visual, \mytest is defined to use \fbox.

enter image description here

\documentclass{article}
\usepackage{amsmath}
\makeatletter

\def\xmytest#1{%
\def\mytest@a{#1}%
\ifx\mytest@a\mytest@aa
\expandafter\@firstofone
\else
\ifx\mytest@a\mytest@aaa
\expandafter\expandafter\expandafter\@firstofone
\else
\expandafter\expandafter\expandafter\mytest
\fi
\fi
{#1}}


\def\mytest#1\@empty{%
\fbox{#1}}

\def\mytest@aa{\global}
\def\mytest@aaa{\@empty}
% when defined without argument there's no problem:
%

% this is my modified version of \align@preamble
\def\my@align@preamble{%
   &\hfil
    \strut@
    \setboxz@h{\@lign
\xmytest##\@empty
}% this line causes trouble
    \ifmeasuring@\savefieldlength@\fi
    \set@field
    \tabskip\z@skip
   &\setboxz@h{\@lign{}##}%
    \ifmeasuring@\savefieldlength@\fi
    \set@field
    \hfil
    \tabskip\alignsep@
}
\newenvironment{test}{%
  \let\align@preamble\my@align@preamble
  \align}{\endalign}
\makeatother
\begin{document}

\begin{test}
 a & b \\
 c & d
\end{test}
\end{document}
share|improve this answer
    
I believe that the reason is completely different, as \halign{\hfil\whatever{#}\cr is perfectly accepted. See also the example at the bottom of page 248 of the TeXbook. Here the main problem is that \my@align@preamble is fed to \span for building the preamble. –  egreg Apr 6 '12 at 23:24
    
yes I was just about to delete my answer, sorry, not thinking straight tonight. –  David Carlisle Apr 6 '12 at 23:27
    
@egreg I sort of fixed the code, so undeleted the answer –  David Carlisle Apr 7 '12 at 16:02
    
Thanks in advance. Since it's Easter (and I have family visiting) I have little time to have a detailed look the next days... I'm almost sure I'll have a question about the “almost impossible” part when I get to it... Happy Holidays! –  cgnieder Apr 7 '12 at 21:23
    
@DavidCarlisle I'm sorry that I wasn't clear in my question. I actually meant to access every cell, i.e. the ones in the last column too. I left that out in the MWE because I wasn't aware that there would be a difference. (I should/could have thought of it, though, since it seems quite obvious that the last column ends differently than the other columns...). It is not worth the hassle for the application I had in mind and I will drop it, but I'm still curious: you said “the general approach is almost impossible”. So it is possible? Simply letting \\ to \cr cannot work here, I guess? –  cgnieder Apr 9 '12 at 12:22
show 1 more 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.