# macros, category codes, how to pass stuff with cat codes inside?

The original question is below (it was a bad simplification of the problem I had and also before being edited, it also contained major syntax errors). The problem that originated the question is why \test and \ttest work but \tttest gives ! Package pgfbasematrix Error: Single ampersand used with wrong catcode.

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{matrix}
\def\test #1.{#1}
\def\ttest #1.{\node{#1}}
\def\tttest #1, #2.{\node{#1}; & \node{#2};}
\begin{document}
\begin{tikzpicture}
\matrix [draw,row sep=1cm,nodes=draw]
{\node  {10}; & \node {5};  \\
\node  {5}; & \node  {\test 10.}; \\};
\end{tikzpicture}
\newpage
\begin{tikzpicture}
\matrix [draw,row sep=1cm,nodes=draw]
{\node  {10}; & \ttest 5. ;  \\
\node  {5}; & \node  {\test 10.}; \\};
\end{tikzpicture}
\newpage
\begin{tikzpicture}
\matrix [draw,row sep=1cm,nodes=draw]
{\tttest 10, 5. \\
\node  {5}; & \node  {\test 10.}; \\};
\end{tikzpicture}
\end{document}


Original question:

Say Bob (our fictitious character) has to type long systems of equations using AMS-TeX all day long. Poor Bob is very tired but one day he has a revelation: The TeX Book! Bob reads chapters 7 and 20 and thinks he solved his problem.

 \begin{align*}
& 2x + & 10y =& 20 \\
& x  - & y   =& 10 \\
\end{align*}


Bob creates a macro and thinks the problem has been solved:

\def\bobmacro #1, #2, #3{ & #1 & #2 & #3 \\}
\begin{align*}
\bobmacro{ 2x+, 10y=, 20}
\bobmacro{ x-, y, 10}
\end{align*}


But alas! The macro does not compile, Bob suspects the problem is with the cat code & and perhaps also //. He knows he can neutralize them by using \string and doing that the code compiles but the outcome is again not the one desired.

Bob goes to TeX.SE and reads questions about \csname and \scantokens. The last one looks promising but he can't find a simple example on Google.

Bob seems more confused then when he started. He wonders if: Is there a TeX/LaTeX or e-TeX way of implementing the idea behind his macro?

-
I have retagged, because Plain-TeX has nothing to do with this, not category codes. If you feel that those tag are appropriate, then your question should be different. – egreg May 13 '14 at 6:57
As Frank Mittelbach points out, the basic problem is just that \bobmacro is not called according to its definition. There is nothing problematic about catcodes (which usually only cause problems if you want to change them somewhere), and & comes out of the macro exactly as if you typed it explicitly. Note however that using align one should have &= rather than =& to get the correct alignment (again independent of defining macros), so the = should be in the third argument, not the second. – Marc van Leeuwen May 13 '14 at 13:54
@MarcvanLeeuwen: Thanks but please see the new edit. – Sergio Parreiras May 13 '14 at 14:08

Sergio, sorry, Bob is using the wrong tool, because the alignment provided by align is surely not the one you need for linear systems.

Category codes have nothing to do with this problem and \scantokens wouldn't help; \string& would be completely wrong. The problem is that you can't have a macro that straddles two cells in an alignment unless it is defined in a less simplistic way.

However Sergio, sorry, Bob, has a better method available, with the systeme package. The documentation is in French, but it has plenty of examples.

\documentclass{article}
\usepackage{systeme}

\begin{document}
$\systeme{ 2x + 10y = 20, x - y = 10 }$
\end{document}


If you don't want the brace, add

\sysdelim..


in your document preamble or before the \systeme command where you don't want the brace.

# After big change in the question

It is a known limitation of TikZ and the matrix library. Ampersands are quite special and should never be inside arguments or macros called within the tikzpicture environment. The workaround is to use ampersand replacement; I've used it throughout, although in the first two examples it's not necessary.

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{matrix}
\def\test #1.{#1}
\def\ttest #1.{\node{#1}}
\def\tttest #1, #2.{\node{#1}; \& \node{#2};}

\begin{document}

\begin{tikzpicture}
\matrix [draw,row sep=1cm,nodes=draw,ampersand replacement=\&]
{\node  {10}; \& \node {5};  \\
\node  {5}; \& \node  {\test 10.}; \\};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}
\matrix [draw,row sep=1cm,nodes=draw,ampersand replacement=\&]
{\node  {10}; \& \ttest 5. ;  \\
\node  {5}; \& \node  {\test 10.}; \\};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}
\matrix [draw,row sep=1cm,nodes=draw,ampersand replacement=\&]
{\tttest 10, 5. \\
\node  {5}; \& \node  {\test 10.}; \\};
\end{tikzpicture}
\end{document}


-
grazzie mille and sorry: I learned my lesson from now on, I will make questions closely related to what I want to do. – Sergio Parreiras May 13 '14 at 14:32

Besides all the comments that have been made about whether or not the approach is appropriate, the above simply doesn't work because \bobmacro is defined a as a macro with 2 delimited arguments (by a comma) followed by one normal argument, but it is then called as a macro with a single argument, so it would need to be called as

\begin{align*}
\bobmacro 2x+, 10y=, {20}
\bobmacro  x-, y, {10}
\end{align*}


in which case it produces exactly the same output as the original align* albeit with a very strange input syntax.

I suggest that Bob (our fictitious character) should be rereading chapter 20 (the part on how arguments are determined) to understand what he defined and how to possibly improve on the syntax. The answer given by @fjbu shows, for example how to implement his input syntax.

-
Thanks and sorry for the big blunder, the question was intended to simplify my original problem (see the new edit) but obviously I did a terrible job. – Sergio Parreiras May 13 '14 at 14:05
@SergioParreiras well better in that case make a new question next time. The way it is now nobody else will have much benefit from reading all the answers. – Frank Mittelbach May 13 '14 at 15:16

It makes no real sense to define such a macro:

\documentclass{article}
\usepackage{amsmath}
\makeatletter
\def\bobmacro#1{\expandafter\bobmacro@i#1\@nil}
\def\bobmacro@i#1,#2,#3\@nil{ & #1 & #2 & #3 \\}
\makeatother

\begin{document}
\begin{align}
\bobmacro{ 2x+, 10y=, 20}
\bobmacro{ x-, y, 10}
\end{align}

\begin{align}
2x+ 10y &= 20\\
x-   y &= 10
\end{align}

\end{document}


You need a different macro for the last line, align do not like a \\ at the end:

-

Your problem has nothing to do with catcodes.

Besides, as has been pointed in other answers, this is not a good approach to typeset linear systems; nevertheless, I stick close to your initial code, hence obey the interface where the user must insert a space after each comma and do not go into the direction of a single \bobmacro with a comma separated argument.

The update addresses an aspect pointed out by Herbert (ams does not like \\ on the last line), and removes the space-after-comma-is-mandatory thing.

\documentclass{amsart}

\def\bobmacroaux #1, #2, #3\\{ #1 & #2 & #3 \\}
\def\bobmacro    #1{\bobmacroaux #1\\}

\begin{document}
Compare
\begin{align*}
\bobmacro{ 2x+{}, 10y=, 20}
\bobmacro{ x-{}, y=, 10}
\end{align*}
with
\begin{align*}
2x+{}&10y=&20\\
x-{}&y=&10\\
\end{align*}
\end{document}


Update.

\documentclass{article}
\usepackage{amsmath}

\chardef\bobmacroindicator 0
\def\bobmacroaux #1,#2,#3\bobmacroaux {#1&#2&#3}
\def\bobmacro    #1{\ifnum\bobmacroindicator=1 \expandafter\\\fi
\bobmacroaux #1\bobmacroaux
\chardef\bobmacroindicator 1
}

\begin{document}
Compare
\begin{align}
\bobmacro{ 2x+{}, 10y=, 20}
\bobmacro{ x-{}, y=, 10}
\end{align}
with
\begin{align}
2x+{}&10y=&20\\
x-{}&y=&10
\end{align}
\end{document}


(added missing = in last update)

-
I obey Sergio's interface which requires the input to have spaces after the commas. Else, \def\bobmacroaux #1, #2, #3\\  should be advantageously replaced by \def\bobmacroaux #1,#2,#3\\ , then the input is not constrained to have spaces after commas. Besides, and as has been pointed out, this is not the good way to typeset linear systems... – jfbu May 13 '14 at 6:58
the code using \bobmacroindicator would have problems if a nested align environment using \bobmacro is itself inside the argument to a top level \bobmacro... – jfbu May 13 '14 at 13:44
Thanks and sorry for the bad example I used to post the question, I edited the question adding an example the closer reflects the problem I'm facing (the error message refers to catcodes) – Sergio Parreiras May 13 '14 at 14:24

Here is a LaTeX way of approaching this. I use both pgffor and mathtools. It's not entirely necessary to use these packages, but it would be a bit more work to create the loop to iterate over rows and control expansion inside \xdef.

\documentclass{article}

\usepackage{amsmath,amssymb}
\usepackage{etoolbox}
\usepackage{pgffor}

\makeatletter

\let\bob@align\relax
\let\bob@row\relax

\def\bob@build@row#1{%%
\let\bob@row\relax
\foreach \myi in {#1}
{%%
\ifx\bob@row\relax
\xdef\bob@row{\myi}%%
\else
\xdef\bob@row{\bob@row & \myi}%%
\fi
}%%
\xdef\bob@row{\bob@row \noexpand\\}%%
}

\def\bobmacro#1{%%
\let\bob@align\relax
\foreach \myn in {#1}
{%%
\ifx\bob@align\relax
\expandafter\bob@build@row\expandafter{\myn}%%
\xdef\bob@align{\expandonce\bob@row}%%
\else
\expandafter\bob@build@row\expandafter{\myn}%%
\xdef\bob@align{\expandonce\bob@align \expandonce\bob@row}%%
\fi
}
\begin{align*}
\bob@align
\end{align*}
}

\makeatother

\begin{document}

\bobmacro{%%
{2x+, 10y=, 20},
{x-,y=,10}%%
}

compare with:

\begin{align*}
2x + & 10y = & 20 \\
x  - &   y=  & 10
\end{align*}

\end{document}


The idea is to use two macros:

• One to build the individual rows
• The other to build the completed align* environment.

You do need to take care with errant whitespace cropping in and breaking things:

 \bobmacro{%%
{2x+, 10y=, 20},
{x-,y=,10}
}


Is rather innocuous looking, but \pgffor sees whitespace at the end of {x-,y=,10} and then doesn't strip off the parentheses.

-
Just a minor improvement: \bobmacro{ {2x+ {}, 10y=, 20}, {x-{},y=,10} } compare with: \begin{align*} 2x + {} & 10y = & 20 \\ x - {} & y= & 10 \end{align*} to get proper spacing around the plus and the minus signs. – Gonzalo Medina May 13 '14 at 2:46
@GonzaloMedina Good point. – A.Ellett May 13 '14 at 2:48