TeX's own lexical analysis doesn't offer the macro programmer terribly much support: while category codes will distinguish letters (or what TeX currently thinks of as letters) from everything else, there's no support for analysing numbers.
The simple-minded solution is to compare numeric characters with the characters of the argument, one by one, by a sequence of direct tests, and to declare the argument "not a number" if any character fails all comparisons:
which one would then use in a tail-recursing macro to gobble an argument. One could do slightly better by assuming (pretty safely) that the digits' character codes are consecutive:\ifx1#1 \else\ifx2#1 ... \else\ifx9#1 \else\isanumfalse \fi\fi...\fi
again used in tail-recursion. However, these forms aren't very satisfactory: getting the recursion "right" is troublesome (it has a tendency to gobble spaces in the argument), and in any case TeX itself has mechanisms for reading numbers, and it would be nice to use them.\ifnum`#1<`0 \isanumfalse \else\ifnum`#1>`9 \isanumfalse \fi \fi
Donald Arseneau's cite package offers the following test for an argument being a strictly positive integer:
which can be adapted to a test for a non-negative integer thus:\def\IsPositive#1{% TT\fi \ifcat_\ifnum0<0#1 _\else A\fi }
or a test for any integer:\def\IsNonNegative{% \ifcat_\ifnum9<1#1 _\else A\fi }
but this surely stretches the technique further than is reasonable.\def\gobbleminus#1{\ifx-#1\else#1\fi} \def\IsInteger#1{% TT\fi \ifcat_\ifnum9<1\gobbleminus#1 _\else A\fi }
If we don't care about the sign, we can use TeX to remove the entire number (sign and all) from the input stream, and then look at what's left:
(which technique is due to David Kastrup); this can provoke errors. In a later thread on the same topic, Michael Downes offered:\def\testnum#1{\afterassignment\testresult\count255=#1 \end} \def\testresult#1\end{\ifx\end#1\end\isanumtrue\else\isanumfalse\fi}
which relies on\def\IsInteger#1{% TT\fi \begingroup \lccode`\-=`\0 \lccode`+=`\0 \lccode`\1=`\0 \lccode`\2=`\0 \lccode`\3=`\0 \lccode`\4=`\0 \lccode`\5=`\0 \lccode`\6=`\0 \lccode`\7=`\0 \lccode`\8=`\0 \lccode`\9=`\0 \lowercase{\endgroup \expandafter\ifx\expandafter\delimiter \romannumeral0\string#1}\delimiter }
\
romannumeral
producing an empty result if its
argument is zero. Sadly, this technique has the unfortunate property
that it accepts simple expressions such as '1+2-3
'; this
could be solved by an initial \
gobbleminus
-like construction.
All the complete functions above are designed to be used in TeX conditionals written "naturally" - for example:
The LaTeX memoir class has an internal command of its own,\if\IsInteger{<subject of test>}% <deal with integer>% \else <deal with non-integer>% \fi
\
checkifinteger{
num}
, that sets the conditional command
\
ifinteger
according to whether the argument was an integer.
Of course, all this kerfuffle would be (essentially) void if there was
a simple means of "catching" TeX errors. Imagining an
error-catching primitive \
ifnoerror
, one might write:
thus using TeX's own integer-parsing code to do the check. It's a pity that such a mechanism was never defined (it could be that it's impossible to program within TeX!).\def\IsInteger#1{% TT% \ifnoerror \tempcount=#1\relax % carries on if no error \expandafter\iftrue \else % here if there was an error \expandafter\iffalse \fi }
This question on the Web: http://www.tex.ac.uk/cgi-bin/texfaq2html?label=isitanum