[comp.text.tex] TeX bug: optional spaces are mandatory

nath@cernvax.cern.ch (Alfred Nathaniel) (02/08/91)

I discovered a bug in TeX3.0. It also shows up in TeX2.9. Going through the 
change records I couldn't find any reference to it. So I suppose that it has 
been there for much longer.

In sequences

     ...\counter=<constant>\ifnum\counter...
and

     ...\counter=<constant>\fi \ifnum\counter...

the \ifnum test fails if <constant> is not followed by a space.

The problem is in this part of scan_int:
-------------------------------------------------------------------------
@<Accumulate the constant...@>=
loop@+  begin if (cur_tok<zero_token+radix)and(cur_tok>=zero_token)and
    (cur_tok<=zero_token+9) then d:=cur_tok-zero_token
  else if radix=16 then
    if (cur_tok<=A_token+5)and(cur_tok>=A_token) then d:=cur_tok-A_token+10
    else if (cur_tok<=other_A_token+5)and(cur_tok>=other_A_token) then
      d:=cur_tok-other_A_token+10
    else goto done
  else goto done;
  vacuous:=false;
  if (cur_val>=m)and((cur_val>m)or(d>7)or(radix<>10)) then
    begin if OK_so_far then
      begin print_err("Number too big");
@.Number too big@>
      help2("I can only go up to 2147483647='17777777777=""7FFFFFFF,")@/
        ("so I'm using that number instead of yours.");
      error; cur_val:=infinity; OK_so_far:=false;
      end;
    end
  else cur_val:=cur_val*radix+d;
  get_x_token;
  end;
done:
-------------------------------------------------------------------------
To look for another digit belonging to <constant> get_x_token is called.
But if the next token is \ifnum the condition is evaluated before scan_int
did realize that <constant> is complete and has to be stored in \counter.

Here is an example where the bug occurs:

-------------------------------------------------------------------------
\tracingcommands=2
%
\count255=0
\count255=999\ifnum \count255=0 \message{[BOGUS: \the\count255<>0]} \fi
%
\count255=0
\ifnum 0=0 \count255=888\fi
\ifnum \count255=0 \message{[BOGUS: \the\count255<>0]} \fi
%
\end
-------------------------------------------------------------------------
This is TeX, C Version 3.0 (format=plain 91.1.28)  7 FEB 1991 16:23
**ifnumbug
(ifnumbug.tex
{vertical mode: \count}
{\count}
{\ifnum}
{true}
{\message}
 [BOGUS: 999<>0]
{blank space  }
{\fi}
{\count}
{\ifnum}
{true}
{\count}
{\fi}
{\ifnum}
{true}
{\message}
 [BOGUS: 888<>0]
{blank space  }
{\fi}
{\end}
 )
No pages of output.
-------------------------------------------------------------------------

Simply replacing get_x_token by get_token fixes this bug but the trip test
falls flat on its face. I would classify it as category S according to
"The Errors of TeX".

A nasty feature is that it is a Heisenbug. If you look at it it goes away.
Computing the absolute value of a macro argument:
      \counter=#1\ifnum\counter<0\counter=-\counter\fi
may fail depending on the initial value of \counter while:
      \counter=#1\showthe\counter\ifnum\counter<0\counter=-\counter\fi
always gives the correct result as does:
      \counter=#1 \ifnum\counter<0\counter=-\counter\fi

The lesson I learned from this that when writing TeX macros optional spaces
are sometimes mandatory.

Is this the right channel for bug reports? If not could somebody who knows
forward it or let me know where to send it to.

Best regards,
Alfred Nathaniel

Internet: nath@cernvax.cern.ch = 128.141.1.74
          nathanie@dxlmun.cern.ch = 128.141.1.121
  Bitnet: nathanie@cernvm

grabiner@math.harvard.edu (David Grabiner) (02/09/91)

In article <4117@cernvax.cern.ch>, Alfred Nathaniel writes:

> I discovered a bug in TeX3.0. It also shows up in TeX2.9. Going through the 
> change records I couldn't find any reference to it. So I suppose that it has 
> been there for much longer.

> In sequences

>      ...\counter=<constant>\ifnum\counter...
> and

>      ...\counter=<constant>\fi \ifnum\counter...

> the \ifnum test fails if <constant> is not followed by a space.

I don't think this is a bug; a similar situation is mentioned in the
TeXbook, p. 208.  TeX has to expand the \ifnum to see whether the
expansion becomes part of the <constant>.  For example:

\newif\ifcents % Express monetary amounts in cents, not dollars
\centstrue
\def\addcents{\ifcents 00\fi}
\newcount\money
\money=15\addcents

This sets \money to 15 or 1500, depending on the truth of \ifcents.  The
\addcents and \ifcents need to be expanded before TeX finishes reading
the number; otherwise, this would set \money to 15 and then make the 00
part of the paragraph.

--
David Grabiner, grabiner@zariski.harvard.edu
"We are sorry, but the number you have dialed is imaginary."
"Please rotate your phone 90 degrees and try again."
Disclaimer: I speak for no one and no one speaks for me...

eijkhout@s41.csrd.uiuc.edu (Victor Eijkhout) (02/09/91)

grabiner@math.harvard.edu (David Grabiner) writes:

>In article <4117@cernvax.cern.ch>, Alfred Nathaniel writes:

>> I discovered a bug in TeX3.0. 
>> In sequences
>>      ...\counter=<constant>\ifnum\counter...
>> and
>>      ...\counter=<constant>\fi \ifnum\counter...
>> the \ifnum test fails if <constant> is not followed by a space.

>I don't think this is a bug; a similar situation is mentioned in the
>TeXbook, p. 208.  TeX has to expand the \ifnum to see whether the
>expansion becomes part of the <constant>.  For example:

Correct. The second example is somewhat more surprising tho'...

The TeXbook is not very explicit about how conditionals are
handled, but what it boils down to is that \else and \fi
are also expandable control sequences, and with an
empty expansion. So \counter2=1\fi \ifnum1=1 2\fi
will assign \counter2=12 ! (not factorial, but as in '``"O"o"o!!''
``No, Aaaaargghh!!!'' '. Sorry. The Python in me coming out.)

And now for the people who think the understand the above:
remember that \pageno is just an abbreviation (by \countdef)
of \count0. So what's the difference between
    \count37=1\ifodd\pageno\fi2
and
    \count37=1\ifodd\count0\fi2
?

Victor.

monardo@cshl.org (Pat Monardo) (02/11/91)

>\afterassignment\showI
>\I=1\ifnum0=\count0\else\fi
>
>%	{\afterassignment}
>%	{\count37}
>%	{\ifnum}
>%	{\else}
>%	{true}
>%	
>%	\showI ->\showthe \I 
>%	{\showthe}
>%	> 1.
>%	\showI ->\showthe \I 
>%	                     
>%	<to be read again> 
>%	                   \relax 
>%	<to be read again> 
>%	                   \else 
>%	l.10 \I=1\ifnum0=\count0\else
>%	                             \fi
>%	? 
>%	
>%	{\relax}
>%	{\else}
>%	{\par}
>% Here, you'll note that {\else} is scanned and pushed back before the
>% {true} is determined. Also, note the surprise appearance of a {\relax}!

the else "relaxes" the scan of the count number. there is no scan
for the count number of \pageno because it is stored in the
symbol for \pageno.

pat