# Some misunderstanding about macro expansion

I have the following code which doesn't work:

\documentclass[12pt]{article}

\tracingcommands=2
\tracingmacros=2
\tracingall

\makeatletter

\let\sep\relax

\def\put@stack@#1#2{\edef#2{#1\sep#2}}

\def\put@queue@#1#2{\edef#2{#2\sep#1}}

\def\get@#1#2{\expandafter\get@@#2\endget@@#1#2}

\def\get@@#1\sep#2\endget@@#3#4{\edef#3{#1}\edef#4{#2}}

\def\empty@stack{\sep}

\newtoks\piecetoks

\def\leftrightbr{
\let\o@left\left
\let\o@right\right
\let\piece@nd\relax
\let\pieces@stack\empty@stack
\let\delims@stack\empty@stack
\let\on@end@line\empty@stack
\let\on@begin@line\empty@stack
\def\left##1{
\piece@nd
\let\piece@nd\endgroup
\def\on@first@right{
\put@stack@{\the\toks0}{\pieces@stack}
}
\put@stack@{##1}{\delims@stack}
\let\repl\relax
\let\vphantomer####1{}
\put@stack@{\the\piecetoks}{\pieces@stack}
\put@stack@{\right.}{\on@end@line}
\put@stack@{\left.}{\on@begin@line}
\piecetoks=\begingroup
}
\def\right##1{
\endgroup
\on@first@right
\let\on@first@right\relax
\get@\@dummy\on@end@line
\get@\@dummy\on@begin@line
\get@\@lpart\pieces@stack
\get@\@ldelim\delims@stack
\ifx\@lpart\empty
\repl
\else
\edef\repl{\o@left\@ldelim\@lpart\vphantomer{\the\piecetoks}\repl\the\piecetoks\vphantomer{\@lpart}\o@right##1}
\fi
}
\def\mathbr{
\piece@end
\put@stack@{\piece}{\pieces@stack}
\let\vphantomer\vphantom
\edef\repl{\on@end@line \\ \on@begin@line}
\def\piece\begingroup
}
}

\begin{document}

$$\leftrightbr \left(a\right)$$

\end{document}


So what's the catch?

EDIT: The error is

! Undefined control sequence.
<argument> \@dummy

l.85     \left(
a\right)


Looking into log gets us to conclusion that the problem is caused by call to \get@\@dummy\on@end@line. This seems to be strange since direct call to \get@ on \empty@stack results in no error.

EDIT: The code can be narrowed down:

\documentclass[12pt]{article}

\tracingcommands=2
\tracingmacros=2
\tracingall

\newtoks\piecetoks

\def\leftrightbr{
\def\left##1{
\piecetoks=\begingroup
}
\def\right##1{
\endgroup
}
}

\begin{document}

$$\leftrightbr \left(a\right)$$

\end{document}


This is shorter, isn't it? :-) It gives the other error, and the other question arises: how can we achieve behavior in which TeX captures tokens between two macros' calls?

• Welcome to TeX.SX! I get Undefined control sequence \@some with the second code. It would be beneficial if you explained what the macros are supposed to do. – egreg Sep 21 '13 at 19:59
• After fixing the typos \@some to \some both files run without errors. – Heiko Oberdiek Sep 21 '13 at 20:08
• Sorry, my mistake. I have much more code and while trying to make MWE i obviously mistyped. – Andrew Zabavnikov Sep 21 '13 at 20:11
• Maybe it's not so much code after all. I'll edit the question in a couple of minutes. – Andrew Zabavnikov Sep 21 '13 at 20:22
• @AndrewZabavnikov With a delimited macro argument. Your \left expects a \right, so you can gather all tokens between them. But, if you plan to use this with amsmath alignments, I can see many other problems. – egreg Sep 21 '13 at 22:10

You can't assign tokens to a token register with

\piecetoks=\begingroup...\endgroup


Nor you can start the assignment with a macro and end it with another one, because

<toks register>={...}


needs an explicit brace at the end and no expansion takes place during the evaluation of the <balanced text> to be stored.

In the TeXbook, page 276, you find what a <variable assignment> is; one of the alternatives is

<token variable> <equals> <general text>


and some lines above you see

<general text> → <filler> { <balanced text> <right brace>


In the syntax rules, { means an implicit or explicit character token of category code 1, while <right brace> means an explicit character token of category code 2.

Delimited arguments need to look for a fixed token, but you don't need to look ahead for \\-or-\right you can look ahead for \right and then look in the tokens you collected to see if there was a \\.

so

\documentclass{article}

\makeatletter
\newtoks\tA
\newtoks\tB

\def\left#1\right{%
\zz#1\\\right}

\def\zz#1\\#2\right#3{%
\ifx\relax#2\relax
\tA{\left#1\right#3}%
\typeout{^^J^^Jno \space\string\\:^^J\the\tA^^J}%
\expandafter\remove@to@nnil
\else
\tA{\left#1\right.\\}%
\expandafter\zzb
\fi
#2\right#3\@nnil}

\def\zzb#1\\\right#2\@nnil{%
\tB{\left.#1\right#2}%
\typeout{^^J^^Jhas \string\\:^^J\the\tA\space \the\tB^^J}}
\makeatother

\begin{document}

\left( a b\right)

\left( a \\ b\right)

\end{document}


produces

no  \\:
\left ( a b\right )

has \\:
\left ( a \right .\\ \left . b\right )


However I would never use this in production. redefining \left and \right to take macro arguments changes their scanning rules and will introduce all kinds of incompatibilities. Even if you avoid that by using new names rather than redefining the primitives, using \right.\\ \left. at the line break does not ensure that the paired delimiters are the same size so can not produce an acceptable result.

It is better really just to use \bigl( in one cell and \bigr) in a later cell.

• I thought that \vphantom will ensure that corresponding delimiters wil have the same size. Was i wrong? – Andrew Zabavnikov Sep 22 '13 at 9:19
• Another question - are there rules on how to select sizes for delimiters? – Andrew Zabavnikov Sep 22 '13 at 9:20
• @AndrewZabavnikov not really, just whatever looks best, it's a subjective judgement. In particular depending on the context sometimes you always want different sizes for nested pairs so the outer ones in ((a+b)+(c+d))` are bigger, and sometimes you don't. – David Carlisle Sep 22 '13 at 12:33
• @AndrewZabavnikov I mainly answered your argument scanning question, as indicated in comments on the question it was hard to guess what the original code was doing. If you grab everything and measure then it's possible to use vphantom to ensure equal size stretching, yes. – David Carlisle Sep 22 '13 at 12:36