I am trying to better understand brace hacks. The issue was brought to my attention in egreg's answer, Conflict between eqnarray and tabstackengine, that tabstackengine
's failure to protect inner alignment groups could be fixed using brace hacks. David Carlisle pointed me to the TeXbook, and I find them described on p.385.
Now the one used by egreg was similar to but different from the ones on p.385. Below I summarize the 4 given by Knuth, and add 2 more based on egreg's answer, with regards to how they affect the master and balance counter, when expanded and prior to expansion:
TeX Brace Hacks (p.385, TeXbook)
master balance
ex nox ex nox
{ 1 1 1 1
\bgroup 0 0 0 0
\iffalse{\fi 1 1 0 1
\ifnum0=`{\fi 0 1 0 1
{\iffalse}\fi 0 0 1 0
{\ifnum0=`}\fi 1 0 1 0
I realize that in addition to alignment groups, brace types influence how math binary/unary categories communicate to adjacent atoms, they can limit the scope of defined data, and some can or cannot be used in macro/environment definitions without the corresponding [balanced] brace.
To this end, I set up an MWE to test the 6 brace types shown above, as well as \begingroup...\endgroup
, to see how they behaved and scored each of the results:
% BRACE HACK TESTING/LEARNING
%
\documentclass{article}
\usepackage{amsmath}
\usepackage[margin=3cm]{geometry}
\newenvironment{QQQ}{}{}
\def\blech#1{\blechaux#1\relax}
\def\blechaux#1\relax{#1/#2}
\begin{document}
GRADING ELEMENTS:\\
0/1 does not/does protect inner alignment group\\
0/1 does not/does communicate binary nature across boundary\\
0/1 does not/does preserve defined data across boundary\\
0/1 does not/does work within begining/end of environment definition
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{...}|\hfill Grade 1000
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ {\blech{A&OK}}
\end{aligned}$\hfill
$A{=}B$\hfill${\def\Q{XYZ}}\meaning\Q$
%\renewenvironment{QQQ}{{\catcode`&=12 }{}}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\bgroup...\egroup|\hfill Grade 0001
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ \textrm{BAD ALIGNMENT}%\bgroup\blech{A&OK}\egroup
\end{aligned}$\hfill
$A\bgroup=\egroup B$\hfill$\bgroup\def\Q{XYZ}\egroup\meaning\Q$
\renewenvironment{QQQ}{\bgroup\catcode`&=12 }{\egroup}
\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\iffalse{\fi...\iffalse}\fi|\hfill Grade 1110
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \iffalse{\fi\blech{A&OK}\iffalse}\fi
\end{aligned}$\hfill
$A\iffalse{\fi=\iffalse}\fi B$\hfill$\iffalse{\fi\def\Q{XYZ}\iffalse}\fi\meaning\Q$
%\renewenvironment{QQQ}{\iffalse{\fi\catcode`&=12 }{\iffalse}\fi}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\ifnum0=`{\fi...\ifnum0=`}\fi|\hfill Grade 0110
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \textrm{BAD ALIGNMENT}%\ifnum0=`{\fi\blech{A&OK}\ifnum0=`}\fi
\end{aligned}$\hfill
$A\ifnum0=`{\fi=\ifnum0=`}\fi B$\hfill$\ifnum0=`{\fi\def\Q{XYZ}\ifnum0=`}\fi\meaning\Q$
%\renewenvironment{QQQ}{\ifnum0=`{\fi\catcode`&=12 }{\ifnum0=`}\fi}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{\iffalse}\fi...\iffalse{\fi}|\hfill Grade 0001
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \textrm{BAD ALIGNMENT}% {\iffalse}\fi\blech{A&OK}\iffalse{\fi}
\end{aligned}$\hfill
$A{\iffalse}\fi=\iffalse{\fi} B$\hfill${\iffalse}\fi\def\Q{XYZ}\iffalse{\fi}\meaning\Q$
\renewenvironment{QQQ}{{\iffalse}\fi\catcode`&=12 }{\iffalse{\fi}}
\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{\ifnum0=`}\fi...\ifnum0=`{\fi}|\hfill Grade 1001
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ {\ifnum0=`}\fi\blech{A&OK}\ifnum0=`{\fi}
\end{aligned}$\hfill
$A{\ifnum0=`}\fi=\ifnum0=`{\fi}B$\hfill${\ifnum0=`}\fi\def\Q{XYZ}\ifnum0=`{\fi}\meaning\Q$
\renewenvironment{QQQ}{{\ifnum0=`}\fi\catcode`&=12 }{\ifnum0=`{\fi}}
\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\begingroup...\endgroup|\hfill Grade 0101
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ \textrm{BAD ALIGNMENT}%\begingroup\blech{A&OK}\endgroup}\meaning \Q
\end{aligned}$\hfill
$A\begingroup=\endgroup B$\hfill$\begingroup\def\Q{XYZ}\endgroup\meaning\Q$
\renewenvironment{QQQ}{\begingroup\catcode`&=12 }{\endgroup}
\begin{QQQ}&&&\end{QQQ}
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{document}
The grade is a 4-digit number, each successive digit describing a "do or not do" to 4 questions:
gets a 1 if the brace method protects inner alignment groups (
A/OK
vs.BAD ALIGNMENT
)gets a 1 if surrounding math atoms can communicate their presence through the boundary (
A = B
vs.A=B
)gets a 1 if an inner macro definition is preserved outside of the grouping (
macro:-> XYZ
vs.undefined
)gets a 1 if the unbalanced version of the brace type can be used in an environment (
&&&
vs.BAD ENV. DEFINITION
).
For completeness, I guess I should note that the absence of any bracing (not performed in my MWE) would get a score of 0111
.
This was all very enlightening to me, but I had originally been thinking that there must be more differences (tests to perform) that can differentiate these methods that I am missing. However, that thinking was based on a typo in my code that Marcel caught and pointed out. Nonetheless, there may be more tests to further differentiate the results. Also, my results (the "grade") describes things differently than Knuth, who instead describes the differences in terms of effects on master and balance counters, for both expanded and unexpanded states of input.
I also note that my limited set of tests would indicate that \bgroup...\egroup
behaves in the exact same manner as {\iffalse}\fi...\iffalse{\fi}
. Is this actually the case?
So the question is simply, are there other tests can be run to further differentiate the behavior of the seven different bracing techniques shown in my MWE? Are there additional brace hacks that bring further nuance to the question?
p.s. I have submitted a revision of tabstackengine
to ctan.org
today, which fixes the inner alignment group issue and provides some new features.
RESULTS, THANKS TO MARCEL'S ANSWER AND GuM's SUGGESTION
Thanks to Marcel's answer, two important things were learned:
I had a typo in my original question that was causing one of my misunderstandings (without taking anything away from Marcel's answer, I edited the original question to fix it since typos generally make for really bad questions)
A 5th test was added to the mix, to further differentiate the grade: whether the brace hack could be used to delimit an
\edef
.
In addition, GuM suggested two possible brace hacks, one of which proved uniquely successful: \iffalse{\fi\ifnum0=‘}\fi...\ifnum0=‘{\fi\iffalse}\fi
. I have added it to my list.
I show those updated results below, and include the control case of "no delimiters whatsoever". With this 5th test, the results are fully clarified, showing that no two brace hacks perform identically for the 5 tests, and it further emerges that some are complementary (opposite) bracing conditions:
The case of no delimiters and brace
{...}
delimiters were complementary, with grades of01110
and10001
, respectively.the case of
\iffalse{\fi...\iffalse}\fi
and{\iffalse}\fi...\iffalse{\fi}
were complementary, with grades of11100
and00011
, respectively.The case of
\ifnum0=‘{\fi...\ifnum0=‘}\fi
and{\ifnum0=‘}\fi...\ifnum0=‘{\fi}
were complementary, with grades of01100
and10011
, respectively.The remaining two cases,
\bgroup...\egroup
and\begingroup...\endgroup
were similar, except for the well known distinction that the latter will allow math classes to see across the boundary to adjacent math atoms. Their respective scores were00010
and01010
.Gum's suggested brace hack has the most comprehensive score of
11110
, which means it protects/isolates inner alignment groups, provides knowledge of math atoms across boundaries, preserves inner defined data across the outer boundary, and works as an open/close pair in begin/end environment definitions. It only fails in that it cannot delimit an\edef
.
Perhaps Marcel's coolest discernment in his answer was that the {\iffalse}\fi...\iffalse{\fi}
brace hack could be used across an environment definition in order to capture the fully expanded contents of the environment; A very unique insight.
The updated MWE, incorporating all that I've learned here:
% BRACE HACK TESTING/LEARNING
%
\documentclass{article}
\usepackage{amsmath}
\usepackage[margin=3cm,top=2cm,bottom=2cm]{geometry}
\newenvironment{QQQ}{}{}
\def\blech#1{\blechaux#1\relax}
\def\blechaux#1\relax{#1/#2}
\begin{document}
GRADING ELEMENTS:\\
0/1 does not/does protect/isolate inner alignment groups.\\
0/1 does not/does provide knowledge of math atoms across boundary.\\
0/1 does not/does preserve inner-defined data across outer boundary.\\
0/1 does not/does work as open/close pair in the begining/end of environment definition.\\
0/1 does not/does work as delimiters to an \textbackslash edef.
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|...| (absence of delimiters)\hfill Grade 01110
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{...}|\hfill Grade 10001
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ {\blech{A&OK}}
\end{aligned}$\hfill
$A{=}B$\hfill${\def\Q{XYZ}}\meaning\Q$
%\renewenvironment{QQQ}{{\catcode`&=12 }{}}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\hfill\edef\QQ{\today}[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\bgroup...\egroup|\hfill Grade 00010
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ \textrm{BAD ALIGNMENT}%\bgroup\blech{A&OK}\egroup
\end{aligned}$\hfill
$A\bgroup=\egroup B$\hfill$\bgroup\def\Q{XYZ}\egroup\meaning\Q$
\renewenvironment{QQQ}{\bgroup\catcode`&=12 }{\egroup}
\begin{QQQ}&&&\end{QQQ}
\hfill CAN'T EDEF%\edef\QQ\bgroup\today\egroup[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\iffalse{\fi...\iffalse}\fi|\hfill Grade 11100
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \iffalse{\fi\blech{A&OK}\iffalse}\fi
\end{aligned}$\hfill
$A\iffalse{\fi=\iffalse}\fi B$\hfill$\iffalse{\fi\def\Q{XYZ}\iffalse}\fi\meaning\Q$
%\renewenvironment{QQQ}{\iffalse{\fi\catcode`&=12 }{\iffalse}\fi}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\hfill CAN'T EDEF%\edef\QQ\iffalse{\fi\today\iffalse}\fi[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\ifnum0=`{\fi...\ifnum0=`}\fi|\hfill Grade 01100
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \textrm{BAD ALIGNMENT}%\ifnum0=`{\fi\blech{A&OK}\ifnum0=`}\fi
\end{aligned}$\hfill
$A\ifnum0=`{\fi=\ifnum0=`}\fi B$\hfill$\ifnum0=`{\fi\def\Q{XYZ}\ifnum0=`}\fi\meaning\Q$
%\renewenvironment{QQQ}{\ifnum0=`{\fi\catcode`&=12 }{\ifnum0=`}\fi}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\hfill CAN'T EDEF%\edef\QQ\ifnum0=`{\fi\today\ifnum0=`}\fi[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{\iffalse}\fi...\iffalse{\fi}|\hfill Grade 00011
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \textrm{BAD ALIGNMENT}% {\iffalse}\fi\blech{A&OK}\iffalse{\fi}
\end{aligned}$\hfill
$A{\iffalse}\fi=\iffalse{\fi} B$\hfill${\iffalse}\fi\def\Q{XYZ}\iffalse{\fi}\meaning\Q$
\renewenvironment{QQQ}{{\iffalse}\fi\catcode`&=12 }{\iffalse{\fi}}
\begin{QQQ}&&&\end{QQQ}
\hfill\edef\QQ{\iffalse}\fi\today\iffalse{\fi}[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{\ifnum0=`}\fi...\ifnum0=`{\fi}|\hfill Grade 10011
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ {\ifnum0=`}\fi\blech{A&OK}\ifnum0=`{\fi}
\end{aligned}$\hfill
$A{\ifnum0=`}\fi=\ifnum0=`{\fi}B$\hfill${\ifnum0=`}\fi\def\Q{XYZ}\ifnum0=`{\fi}\meaning\Q$
\renewenvironment{QQQ}{{\ifnum0=`}\fi\catcode`&=12 }{\ifnum0=`{\fi}}
\begin{QQQ}&&&\end{QQQ}
\hfill\edef\QQ{\ifnum0=`}\fi\today\ifnum0=`{\fi}[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\begingroup...\endgroup|\hfill Grade 01010
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ \textrm{BAD ALIGNMENT}%\begingroup\blech{A&OK}\endgroup}
\end{aligned}$\hfill
$A\begingroup=\endgroup B$\hfill$\begingroup\def\Q{XYZ}\endgroup\meaning\Q$
\renewenvironment{QQQ}{\begingroup\catcode`&=12 }{\endgroup}
\begin{QQQ}&&&\end{QQQ}
\hfill CAN'T EDEF%\edef\QQ\begingroup\today\endgroup[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\iffalse{\fi\ifnum0=`}\fi...\ifnum0=`{\fi\iffalse}\fi|\hfill
Grade 11110
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ \iffalse{\fi\ifnum0=`}\fi\blech{A&OK}\ifnum0=`{\fi\iffalse}\fi
\end{aligned}$\hfill
$A\iffalse{\fi\ifnum0=`}\fi=\ifnum0=`{\fi\iffalse}\fi B$
\hfill$\iffalse{\fi\ifnum0=`}\fi\def\Q{XYZ}\ifnum0=`{\fi\iffalse}\fi\meaning\Q$
\renewenvironment{QQQ}{\iffalse{\fi\ifnum0=`}\fi\catcode`&=12 }{\ifnum0=`{\fi\iffalse}\fi}
\begin{QQQ}&&&\end{QQQ}
\hfill CAN'T EDEF%\edef\QQ\iffalse{\fi\ifnum0=`}\fi\today\ifnum0=`{\fi\iffalse}\fi[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\clearpage
{\LARGE\bfseries FAILED CASES}
The following case has a score of \verb|01110|, which is identical
to the case of ``absent delimiters.''
Thus, it is currently excluded as a ``brace hack'' as it provides no added value.
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|\ifnum0=`{\fi\iffalse}\fi...\iffalse{\fi\ifnum0=`}\fi|\hfill
Grade 01110
$\begin{aligned}
y&=mx + b\\
E&=mc^2
% DOES NOT PROTECT INNER ALIGNMENT TAB
+ \textrm{BAD ALIGNMENT}%\ifnum0=`{\fi\iffalse}\fi\blech{A&OK}\iffalse{\fi\ifnum0=`}\fi
\end{aligned}$\hfill
$A\ifnum0=`{\fi\iffalse}\fi=\iffalse{\fi\ifnum0=`}\fi B$
\hfill$\ifnum0=`{\fi\iffalse}\fi\def\Q{XYZ}\iffalse{\fi\ifnum0=`}\fi\meaning\Q$
\renewenvironment{QQQ}{\ifnum0=`{\fi\iffalse}\fi\catcode`&=12 }{\iffalse{\fi\ifnum0=`}\fi}
\begin{QQQ}&&&\end{QQQ}
\hfill CAN'T EDEF%\edef\QQ\ifnum0=`{\fi\iffalse}\fi\today\iffalse{\fi\ifnum0=`}\fi[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
These two cases below produce the same grades as the same as the simpler
\verb|\iffalse| and \verb|\ifnum| cases.
Thus, they provide no added value to the list of ``brace hacks.''
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{\iffalse{\fi}...{\iffalse}\fi}|\hfill Grade 11100
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ {\iffalse{\fi}\blech{A&OK}{\iffalse}\fi}
\end{aligned}$\hfill
$A{\iffalse{\fi}={\iffalse}\fi}B$%
\hfill${\iffalse{\fi}\def\Q{XYZ}{\iffalse}\fi}\meaning\Q$
%\renewenvironment{QQQ}{{\iffalse{\fi}\catcode`&=12 }{{\iffalse}\fi}}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\hfill\edef\QQ{\iffalse{\fi}executed, not \verb|\edef|ed{\iffalse}\fi}[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
$\bullet$Case \verb|{\ifnum0=`{\fi}...{\ifnum0=`}\fi}|\hfill Grade 01100
$\begin{aligned}
y&=mx + b\\
E&=mc^2
+ BAD ALIGNMENT%{\ifnum0=`{\fi}\blech{A&OK}{\ifnum0=`}\fi}
\end{aligned}$\hfill
$A{\ifnum0=`{\fi}={\ifnum0=`}\fi}B$%
\hfill${\ifnum0=`{\fi}\def\Q{XYZ}{\ifnum0=`}\fi}\meaning\Q$
%\renewenvironment{QQQ}{{\ifnum0=`{\fi}\catcode`&=12 }{{\ifnum0=`}\fi}}
BAD ENV. DEFINITION%\begin{QQQ}&&&\end{QQQ}
\hfill\edef\QQ{\ifnum0=`{\fi}executed, not \verb|\edef|ed{\ifnum0=`}\fi}[\QQ]
\noindent\hrulefill%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{document}
The updated counter-change table is
TeX Brace Hacks (p.385, TeXbook)
master balance
ex nox ex nox
{ 1 1 1 1 (Knuth)
\bgroup 0 0 0 0 (Knuth)
\iffalse{\fi 1 1 0 1 (Knuth)
\ifnum0=`{\fi 0 1 0 1 (Knuth)
{\iffalse}\fi 0 0 1 0 (egreg inspired)
{\ifnum0=`}\fi 1 0 1 0 (egreg used)
\iffalse{\fi\ifnum0=`}\fi 1 0 0 0 (GuM suggestion)
Some correlations between my 5-digit grades and this counter table can be established:
In order to get a grade of 1 in the 1st digit (the ability to protect/isolate inner alignment groups), the expanded master counter must increase by 1.
In order to get a grade of 1 in the 4th digit (be able to use as open/close pair in the begin/end of an environment), the unexpanded balance (or master) counter must not increase (has a value of 0).
In order to provide delimiters for an
\edef
, the expanded balance counter must increase by 1.
Unrelated to the counter table, but observed from the graded results (excluding \begingroup...\endgroup
which is in a category by itself), grades of 1 in digits 2 and 3 (knowledge of math atoms across boundary and preservation of inner-defined data) can only occur if there are no unconditional occurrences of {...}
or \bgroup...\egroup
.
Thanks for putting up with this extended question!
\iffalse{\fi\ifnum`}=\z@\fi
and\ifnum`{=\z@\fi\iffalse}\fi
.{\iffalse{\fi}...{\iffalse}\fi}
and{\ifnum0=‘{\fi}...{\ifnum0=‘}\fi}
. These cases turned out identical to the simpler\iffalse{\fi...\iffalse}\fi
and\ifnum0=‘{\fi...\ifnum0=‘}\fi
cases, respectively.\iffalse{\fi\ifnum0=‘}\fi...\ifnum0=‘{\fi\iffalse}\fi
actually produces a score of11110
(awesome!!), whereas the case of\ifnum0=‘{\fi\iffalse}\fi...\iffalse{\fi\ifnum0=‘}\fi
seems get a score of 01110, which is the same as the "absent delimiters" case.