Macro for expanding macro arguments to gb4e's \gll

This is a followup question to Problem with gb4e's spacing when using datatool, where I was trying to loop over glosses stored in a CSV with datatool and print them with gb4e's \gll. The issue was that I was passing macro arguments to \gll command, when it expects strings with spaces.

Nicola provided a solution to my problem, which caused the macro arguments to \gll to be expanded before \gll saw them, so that it could handle the spacing appropriately.

Her solution was to create a macro, \doex, using \edef, inside the scope of the \DTLforeach command.

I now have reason to do this same thing inside multiple instances of \DTLforeach, so I'm wondering if there is a way to globally define the \doex command so that I don't have to redefine it each time I need to do this.

Ideally, it would be nice to be able to pass the column names to the \doex command, rather than have them hardcoded into the macro.

Here's a minimal non-working example that illustrates what I'm after. If possible, I'd also appreciate a little commentary explaining why this doesn't work and why your proposed solution does work. I'd like to better understand how macro expansion works with LaTeX.

\documentclass{article}

\begin{filecontents*}{glosses1.csv}
MyIpa,MyGloss,MyTranslation
sonj2n-i motun kuki-lul an-m2k-ess-ta,boy-NOM all cookie-ACC NEG-eat-PST-DEC,The boy didn't eat every cookie
\end{filecontents*}

\begin{filecontents*}{glosses2.csv}
IPA,GLOSS,TRANSLATION
\textipa{sonj2n-i} \textipa{motun} \textipa{kuki-lul} \textipa{an-m2k-ess-ta},boy-NOM all cookie-ACC NEG-eat-PST-DEC,The boy didn't eat every cookie
\end{filecontents*}

\usepackage{datatool}

\usepackage[T1]{tipa}
\usepackage{gb4e}

% This doesn't work
\newcommand*{\doex}[3]{
\noexpand\ex[]{\noexpand\gll
\expandonce#1 \noexpand\\
\expandonce#2 \noexpand\\
\noexpand\trans \expandonce#3'}}

\begin{document}

\DTLforeach{glosses1}{%
\MyIpa=MyIpa,
\MyGloss=MyGloss,
\MyTranslation=MyTranslation}{%
\begin{exe}
\doex{\MyIpa}{\MyGloss}{\MyTranslation}
\end{exe}
}

\DTLforeach{glosses2}{%
\IPA=IPA,
\GLOSS=GLOSS,
\TRANS=TRANSLATION}{%
\begin{exe}
\doex{\IPA}{\GLOSS}{\TRANS}
\end{exe}
}

\end{document}


You have to add an abstraction layer:

\documentclass{article}

\begin{filecontents*}{glosses1.csv}
MyIpa,MyGloss,MyTranslation
sonj2n-i motun kuki-lul an-m2k-ess-ta,boy-NOM all cookie-ACC NEG-eat-PST-DEC,The boy didn't eat every cookie
\end{filecontents*}

\begin{filecontents*}{glosses2.csv}
IPA,GLOSS,TRANSLATION
\textipa{sonj2n-i} \textipa{motun} \textipa{kuki-lul} \textipa{an-m2k-ess-ta},boy-NOM all cookie-ACC NEG-eat-PST-DEC,The boy didn't eat every cookie
\end{filecontents*}

\usepackage{datatool}

\usepackage[T1]{tipa}
\usepackage{gb4e}

\newcommand*{\doex}[3]{%
\begingroup\edef\x{\endgroup
\noexpand\ex[]{%
\noexpand\gll
\expandonce#1 \noexpand\\
\expandonce#2 \noexpand\\
\noexpand\trans\expandonce#3'%
}%
}\x
}

\begin{document}

\DTLforeach{glosses1}{%
\MyIpa=MyIpa,
\MyGloss=MyGloss,
\MyTranslation=MyTranslation}{%
\begin{exe}
\doex{\MyIpa}{\MyGloss}{\MyTranslation}
\end{exe}
}

\DTLforeach{glosses2}{%
\IPA=IPA,
\GLOSS=GLOSS,
\TRANS=TRANSLATION}{%
\begin{exe}
\doex{\IPA}{\GLOSS}{\TRANS}
\end{exe}
}

\end{document}


Easier with xparse, of course. 😉

\begin{filecontents*}{glosses1.csv}
MyIpa,MyGloss,MyTranslation
sonj2n-i motun kuki-lul an-m2k-ess-ta,boy-NOM all cookie-ACC NEG-eat-PST-DEC,The boy didn't eat every cookie
\end{filecontents*}

\begin{filecontents*}{glosses2.csv}
IPA,GLOSS,TRANSLATION
\textipa{sonj2n-i} \textipa{motun} \textipa{kuki-lul} \textipa{an-m2k-ess-ta},boy-NOM all cookie-ACC NEG-eat-PST-DEC,The boy didn't eat every cookie
\end{filecontents*}

\documentclass{article}
\usepackage[T1]{tipa}
\usepackage{datatool}
\usepackage{xparse}
\usepackage{gb4e}

\ExplSyntaxOn
\NewDocumentCommand{\doex}{mmm}
{
}

{
\ex[]{\gll #1 \\~ #2 \\~ \trans #3'}
}
\ExplSyntaxOff

\begin{document}

\DTLforeach{glosses1}{%
\MyIpa=MyIpa,
\MyGloss=MyGloss,
\MyTranslation=MyTranslation}{%
\begin{exe}
\doex{\MyIpa}{\MyGloss}{\MyTranslation}
\end{exe}
}

\DTLforeach{glosses2}{%
\IPA=IPA,
\GLOSS=GLOSS,
\TRANS=TRANSLATION}{%
\begin{exe}
\doex{\IPA}{\GLOSS}{\TRANS}
\end{exe}
}

\end{document}


The ~ (spaces) after \\ are essential for \gll` to work.