\newcommand for bmatrix of features using pgffor throws missing \endgroup error
Enrico Gregorio has already explained the reason in his comment:
\foreach
puts the body of its loop in a group. That conflicts with the cell grouping of the matrix environment. Another problem, if there is something after the line end that is not expandable (end of group, \relax
, ...), then a new (empty) line is created.
The following uses the loop to define \MatrixBody
that contains the lines of the matrix. The chain of \expandafter
expands \entry
once. Otherwise the macro \MatrixBody
would contain \entry
in each matrix line, undefined after the loop.
Global assignments are only used because of \g@addto@macro
from LaTeX's kernel that allows to add stuff to a macro. (Local variants are defined by packages, such as ltxcmds
).
\documentclass{article}
\usepackage{pgffor}
\usepackage{amsmath}
\makeatletter
\newcommand*{\Features}[1]{%
\global\let\MatrixBody\@empty
\foreach \entry in {#1} {%
\expandafter\g@addto@macro\expandafter\MatrixBody
\expandafter{%
\expandafter\text\expandafter{\entry}\\%
}%
}%
\(%
\begin{bmatrix}%
\MatrixBody
\end{bmatrix}%
\)%
}
\makeatother
\begin{document}
\Features{--ACC, +PL}
\end{document}
The problem is that \foreach
executes each cycle in a group. So in your case you're basically doing
\(
\begin{bmatrix}
\begingroup\text{--ACC}\\\endgroup
\begingroup\text{+PL}\\\endgroup
\end{bmatrix}
\)
which indeed stops with
! Missing \endgroup inserted.
<inserted text>
\endgroup
You have to build the contents of the matrix beforehand. Also using a cycle process that doesn't group is a lost battle anyway, because also table cells are evaluated in a group and \\
ends it.
Here's an expl3
based macro.
\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
\ExplSyntaxOn
% the user level command
\NewDocumentCommand{\Features}{ m }
{
\adam_features:n { #1 }
}
% a variable
\seq_new:N \l_adam_items_seq
% the programmer level function
\cs_new_protected:Npn \adam_features:n #1
{
% clear the sequence
\seq_clear:N \l_adam_items_seq
% do a mapping on the comma list separated input
% adding the item between \mathrm{ and }
\clist_map_inline:nn { #1 }
{
\seq_put_right:Nn \l_adam_items_seq { \mathrm { ##1 } }
}
\(
\begin{bmatrix}
% deliver the items separated by \\
\seq_use:Nn \l_adam_items_seq { \\ }
\end{bmatrix}
\)
}
\ExplSyntaxOff
\begin{document}
\Features{-ACC, +PL}
\end{document}
Note that I use \mathrm
rather than \text
; but it's personal choice.