[Tex/LaTex] How does TeX handle \else and \fi in conditionals

conditionalstex-core

I have seen packages using

\def\afterelse#1\else#2\fi{\fi#1}

which TeX seems happy to accept. But on taking the first branch, isn't TeX expecting \else instead of \fi? That is, isn't the following the correct syntax for the first fork? This too is accepted by TeX.

\def\afterelse#1\else#2\fi{\else#1}

EDIT

Thanks to Egreg. Tracing \ifs shows what is happening.

\def\loggingallstuff{%
  \tracinggroups\@ne\tracingifs\@ne\loggingall\tracingassigns\@ne
}

\def\afterelse#1\else#2\fi{\fi#1}

{\loggingallstuff
  %\edef\x{\iftrue\afterelse T\else F\fi}
  \edef\x{\iftrue T\else F\fi}
%  \edef\x{\iffalse T\else F\fi}
}

% \iftrue
{\edef}
{\iftrue: (level 1) entered on line 23}
{true}
{\else: \iftrue (level 1) entered on line 23}
{\fi: \iftrue (level 1) entered on line 23}
{changing \x=undefined}
{into \x=macro:->T}

% \iffalse:
{\edef}
{\iffalse: (level 1) entered on line 24}
{false}
{\else: \iffalse (level 1) entered on line 24}
{\fi: \iffalse (level 1) entered on line 24}
{changing \x=undefined}
{into \x=macro:->F}

Here is one case I find interesting:

\documentclass{article}
\usepackage{pgfkeys}
\makeatletter

\def\pgfkeysafterfi#1\fi{\fi#1}
%\def\pgfkeysafterelsei#1\else#2\fi{\fi#1}
\def\pgfkeysafterelsei#1\else#2\fi{\else#1} % wrong but gives no error.

\def\pgfkeysifkeydef#1#2#3{%
  \ifcsname pgfk@#1/.@cmd\endcsname
    \pgfkeysafterelsei{#2}\else\pgfkeysafterfi{#3}\fi
}
\pgfkeys{%
  /handlers/.if definable/.code 2 args={
    \pgfkeysifkeydef{\pgfkeyscurrentpath}{%
      \@latexerr{Oops, key '\pgfkeyscurrentpath' already exists}
        {You aren't in trouble here!}%
    }{%
      \pgfkeys{\pgfkeyscurrentpath/.default=#1,\pgfkeyscurrentpath/.code=#2}%
    }%
  }%
}
\pgfkeys{
  /fam/.is family,/fam/.cd,
  keya/.if definable=\relax\@empty,
  keya/.if definable=\@empty{\def\x##1{##1*#1}},
}
\makeatother
\begin{document}
x
\end{document}

Best Answer

Let's use \iftrue and \iffalse for testing what happens, assuming the definition

\def\afterelse#1\else#2\fi{\fi#1}

\iftrue

\iftrue\afterelse T\else F\fi

becomes

\afterelse T\else F\fi

and the next expansion gives

\fi T

\iffalse

\iffalse\afterelse T\else F\fi

becomes

F\fi

and the expansion of \fi is empty.


The important thing is that when the "true" branch is taken, only the test is removed, while everything up to and including \else is removed when the "false" branch is taken. This can be seen in the following interactive session:

This is TeX, Version 3.1415926 (TeX Live 2011)
**\relax

*\toks0=\expandafter{\iftrue T\else F\fi}         

*\showthe\toks0
> T\else F\fi .
<*> \showthe\toks0

? 

*\toks0=\expandafter{\iffalse T\else F\fi}

*\showthe\toks0
> F\fi .
<*> \showthe\toks0

The code

\def\afterelse#1\else#2\fi{\else#1}

is not correct, as shown by the following interactive session

This is TeX, Version 3.1415926 (TeX Live 2011)
**\relax

*\def\afterelse#1\else#2\fi{\else#1}

*\iftrue\afterelse T\else F\fi

*
(Please type a command or say `\end')
*\bye
! Incomplete \iftrue; all text was ignored after line 0.
<inserted text> 
                \fi 
<to be read again> 
                   \bye 
<*> \bye