# Concatenating/appending to macro in loop

I have looked through answers on this forum that did bring me closer to the solution of the problem stated above, but both the approach via \xdef and via \g@addto@macro give errors or wrong results.

What I want to do is to define a macro with an optional argument that is parsed in a loop: the input \gttest[Mg=x,Fe=y,Ca=z] is supposed to create a chemical formula of the form Mg_x Fe_y Ca_(1-x-y); spaces and parens are given here for clarity only and don't appear in the actual output. Here is the code (part of a .sty file):

\RequirePackage{xspace}
\RequirePackage{xstring}
\RequirePackage{ifthen}
\RequirePackage{fp-eval}
\RequirePackage{twoopt}
% preliminaries for the components
\newcommand{\@sublow}{\vphantom{A}}
\newcommand{\@chem}[2]{%
\ifthenelse{\equal{#2}{1}}{#1}{%
\ensuremath{\mathrm{#1}\@sublow_{#2}}}}
\newcommand{\@GCresb}{}
\newcommand{\@GCresc}{}
\newcommand{\@GCtmpa}{}
\newcommand{\@GCtmpb}{}
\newcommand{\@GCtmpc}{}
\newcommand{\@GCtmpd}{}
\newcounter{isscmp}
\newcounter{jsscmp}
\newcounter{lsscmp}
\newcounter{nsscmp}
% the actual macro
\newcommand{\gttest}[1][]{%
\StrCount{#1}{=}[\nsscmp]%
\setcounter{lsscmp}{\nsscmp}%
\setcounter{isscmp}{0}%
\setcounter{nsscmp}{1}%
\renewcommand{\@GCtmpa}{}%
\renewcommand{\@GCresc}{}%
\whiledo{\value{nsscmp}<\value{lsscmp}\OR\value{nsscmp}=\value{lsscmp}}{%
\ifthenelse{\value{nsscmp}<\value{lsscmp}}{%
\StrPosition[\thensscmp]{#1}{,}[\jsscmp]%
\StrMid{#1}{\value{isscmp}+1}{\numexpr\jsscmp-1}[\@GCtmpb]}{%
\StrGobbleLeft{#1}{\value{isscmp}}[\@GCtmpb]}%
\StrBefore{\@GCtmpb}{=}[\@GCtmpc]%
\StrBehind{\@GCtmpb}{=}[\@GCtmpd]%
\ifthenelse{\value{nsscmp}<\value{lsscmp}}{%
% all but last comp.: use given subscript and accumulate
\xdef\@GCresc{\@GCresc-\@GCtmpd}%
\renewcommand{\@GCresb}{\@chem{\@GCtmpc}{\@GCtmpd}}}{%
% last comp.: apply closure condition \sum x_i=1
\renewcommand{\@GCresb}{\@chem{\@GCtmpc}{1\@GCresc}}}%
\xdef\@GCtmpa{\@GCtmpa\@GCresb}% <-- causes Incomplete iffalse
\setcounter{isscmp}{\jsscmp}%
\stepcounter{nsscmp}}%
\@GCtmpa}

In this form, the code throws an error:

! Incomplete \iffalse; all text was ignored after line 5.

\fi

<*> mytest

If I comment the xdef line signaled with the <-- and uncomment the following line with the g@addto... instead, it runs smoothly but the result is Ca_(1-x-y) Ca_(1-x-y) Ca_(1-x-y)

So - why doesn't this work? I have no idea where an incomplete if clause would be in the first variant nor how the last appendage gets to be repeated three times in the second variant, although the argument is correctly parsed.

-
Could you please convert your code snippet into a full-fledge minimal working example (MWE)? Also, why add z when it's converted to 1-x-y? – Werner Oct 7 '12 at 2:21
– Peter Grill Oct 7 '12 at 2:41
I think you are going to be much better off using a dedicated keyval parsing routine to do this. However, I'm not 100% clear on the interface. Presumably, you know the general formula, which must contain calcium. Is the element list fixed? – Joseph Wright Oct 7 '12 at 8:56
Werner, I just tried to paste the code from my larger test file into a separate mwe file, but it turns out that this changes the error in the case of xdef line. As for adding the z subscript: this just signals that the subscript is not a number (this macro is actually a subset of a macro that is also meant to handle numbers). – TomR Oct 7 '12 at 13:09
Joseph, I have indeed considered using keyval, but if I understand the docs correctly, that would require a set of predefined keys, in other words, I couldn't give the command arbitrary arguments. So, no, the element list is not really fixed. The goal is to be able to give any number of elements >2 as an argument (in practice that would be 2-4 in most cases). The last entry must fulfill the closure condition that the sum of the indices should be 1, as you will have noticed. – TomR Oct 7 '12 at 13:17

I think the coding's a lot more complicated than it need be, which makes it harder to keep track of the expansion. I think the tests can be a lot simpler, something like:

\documentclass{article}

\makeatletter

\newcommand{\@chem}[2]{%
\def\xtest{#1}%
\def\xtest{1}%
\mathrm{#1}%
\ifx\xtest\ytest\else\@sublow_{#2}\fi}

\newcommand{\@sublow}{\vphantom{A}}

\newcommand\gttest[1][]{\ensuremath{%
\gdef\chem@last{1}%
\@for\tmp:=#1\@empty\do{%
\expandafter\x@chem\tmp\relax
}}}

\def\last@test#1\@empty#2#3\@nil{#2}

\def\x@chem#1=#2\relax{%
\expandafter\ifx\expandafter!\last@test#2!\@empty?\@nil
\@chem{#1}\chem@last
\else