% language=us runpath=texruns:manuals/lowlevel

\environment lowlevel-style

\startdocument
  [title=conditionals,
   color=middleblue]

\pushoverloadmode

\startsectionlevel[title=Preamble]

\startsectionlevel[title=Introduction]

% {\em There are more conditionals then discussed here but in due time these will
% also be explained.}
%
% + \if
% + \ifabsdim
% + \ifabsfloat
% + \ifabsnum
% + \ifarguments
% + \ifboolean
% + \ifcase
% + \ifcat
% + \ifchkdim
% + \ifchkdimension
% + \ifchkdimexpr
% + \ifchknum
% + \ifchknumber
% + \ifchknumexpr
% + \ifcmpdim
% + \ifcmpnum
% + \ifcondition
% + \ifcramped
% + \ifcsname
% + \ifcstok
% + \ifdefined
% + \ifdim
% + \ifdimexpression
% + \ifdimval
% + \ifempty
% + \iffalse
% + \ifflags
% + \iffloat
% + \iffontchar
% + \ifhaschar
% + \ifhastok
% + \ifhastoks
% + \ifhasxtoks
% + \ifhbox
% + \ifhmode
% + \ifinalignment
% + \ifincsname
% + \ifinner
% + \ifinsert
% + \ifintervaldim
% + \ifintervalfloat
% + \ifintervalnum
% + \iflastnamedcs
% + \iflist
% + \ifmathparameter
% + \ifmathstyle
% + \ifmmode
% + \ifnum
% + \ifnumexpression
% + \ifnumval
% + \ifodd
% + \ifparameter
% + \ifparameters
% + \ifrelax
% + \iftok
% + \iftrue
% + \ifvbox
% + \ifvmode
% + \ifvoid
% + \ifx
% + \ifzerodim
% + \ifzerofloat
% + \ifzeronum

You seldom need the low level conditionals because there are quite some so called
support macros available in \CONTEXT . For instance, when you want to compare two
values (or more accurate: sequences of tokens), you can do this:

\starttyping[option=TEX]
\doifelse {foo} {bar} {
    the same
} {
    different
}
\stoptyping

But if you look in the \CONTEXT\ code, you will see that often we use primitives
that start with \type {\if} in low level macros. There are good reasons for this.
First of all, it looks familiar when you also code in other languages. Another
reason is performance but that is only true in cases where the snippet of code is
expanded very often, because \TEX\ is already pretty fast. Using low level \TEX\
can also be more verbose, which is not always nice in a document source. But, the
most important reason (for me) is the layout of the code. I often let the look
and feel of code determine the kind of coding. This also relates to the syntax
highlighting that I am using, which is consistent for \TEX, \METAPOST, \LUA,
etc.\ and evolved over decades. If code looks bad, it probably is bad. Of course
this doesn't mean all my code looks good; you're warned. In general we can say
that I often use \type {\if...} when coding core macros, and \type {\doifelse...}
macros in (document) styles and modules.

In the sections below I will discuss the low level conditions in \TEX. For the
often more convenient \CONTEXT\ wrappers you can consult the source of the system
and support modules, the wiki and|/|or manuals.

Some of the primitives shown here are only available in \LUATEX, and some only in
\LUAMETATEX . We could do without them for decades but they were added to these
engines because of convenience and, more important, because then made for nicer
code. Of course there's also the fun aspect. This manual is not an invitation to
use these very low level primitives in your document source. The ones that
probably make most sense are \type {\ifnum}, \type {\ifdim} and \type {\ifcase}.
The others are often wrapped into support macros that are more convenient.

\stopsectionlevel

\startsectionlevel[title={Number and dimensions}]

Numbers and dimensions are basic data types in \TEX. When you enter one, a number
is just that but a dimension gets a unit. Compare:

\starttyping[option=TEX]
1234
1234pt
\stoptyping

If you also use \METAPOST, you need to be aware of the fact that in that language
there are not really dimensions. The \type {post} part of the name implies that
eventually a number becomes a \POSTSCRIPT\ unit which represents a base point (\type
{bp}) in \TEX. When in \METAPOST\ you entry \type {1234pt} you actually multiply
\type {1234} by the variable \type {pt}. In \TEX\ on the other hand, a unit like
\type {pt} is one of the keywords that gets parsed. Internally dimensions are
also numbers and the unit (keyword) tells the scanner what multiplier to use.
When that multiplier is one, we're talking of scaled points, with the unit \type
{sp}.

\startbuffer
\the\dimexpr 12.34pt \relax
\the\dimexpr 12.34sp \relax
\the\dimexpr 12.99sp \relax
\the\dimexpr 1234sp  \relax
\the\numexpr 1234    \relax
\stopbuffer

\typebuffer[option=TEX]

\startlines \getbuffer \stoplines

When we serialize a dimension it always shows the dimension in points, unless we
serialize it as number.

\startbuffer
\scratchdimen1234sp
\number\scratchdimen
\the\scratchdimen
\stopbuffer

\typebuffer[option=TEX]

\startlines \getbuffer \stoplines

When a number is scanned, the first thing that is taken care of is the sign. In many
cases, when \TEX\ scans for something specific it will ignore spaces. It will
happily accept multiple signs:

\startbuffer
\number +123
\number +++123
\number + + + 123
\number +-+-+123
\number --123
\number ---123
\stopbuffer

\typebuffer[option=TEX]

\startlines \getbuffer \stoplines

Watch how the negation accumulates. The scanner can handle decimal, hexadecimal
and octal numbers:

\startbuffer
\number -123
\number -"123
\number -'123
\stopbuffer

\typebuffer[option=TEX]

\startlines \getbuffer \stoplines

A dimension is scanned like a number but this time the scanner checks for upto
three parts: an either or not signed number, a period and a fraction. Here no
number means zero, so the next is valid:

\startbuffer
\the\dimexpr  . pt \relax
\the\dimexpr 1. pt \relax
\the\dimexpr  .1pt \relax
\the\dimexpr 1.1pt \relax
\stopbuffer

\typebuffer[option=TEX]

\startlines \getbuffer \stoplines

Again we can use hexadecimal and octal numbers but when these are entered, there
can be no fractional part.

\startbuffer
\the\dimexpr  16 pt \relax
\the\dimexpr "10 pt \relax
\the\dimexpr '20 pt \relax
\stopbuffer

\typebuffer[option=TEX]

\startlines \getbuffer \stoplines

The reason for discussing numbers and dimensions here is that there are cases where
when \TEX\ expects a number it will also accept a dimension. It is good to know that
for instance a macro defined with \type {\chardef} or \type {\mathchardef} also is
treated as a number. Even normal characters can be numbers, when prefixed by a \type
{`} (backtick).

The maximum number in \TEX\ is 2147483647 so we can do this:

\starttyping[option=TEX]
\scratchcounter2147483647
\stoptyping

but not this

\starttyping[option=TEX]
\scratchcounter2147483648
\stoptyping

as it will trigger an error. A dimension can be positive and negative so there we
can do at most:

\starttyping[option=TEX]
\scratchdimen  1073741823sp
\stoptyping

\startbuffer
\scratchdimen1073741823sp
\number\scratchdimen
\the\scratchdimen
\scratchdimen16383.99998pt
\number\scratchdimen
\the\scratchdimen
\stopbuffer

\typebuffer[option=TEX]

\startlines
\getbuffer
\stoplines

We can also do this:

\startbuffer
\scratchdimen16383.99999pt
\number\scratchdimen
\the\scratchdimen
\stopbuffer

\typebuffer[option=TEX]

\startlines
\getbuffer
\stoplines

but the next one will fail:

\starttyping[option=TEX]
\scratchdimen16383.9999999pt
\stoptyping

Just keep in mind that \TEX\ scans both parts as number so the error comes from
checking if those numbers combine well.

\startbuffer
\ifdim 16383.99999  pt = 16383.99998  pt the same \else different \fi
\ifdim 16383.999979 pt = 16383.999980 pt the same \else different \fi
\ifdim 16383.999987 pt = 16383.999991 pt the same \else different \fi
\stopbuffer

\typebuffer[option=TEX]

Watch the difference in dividing, the \type {/} rounds, while the \type {:}
truncates.

\startlines
\getbuffer
\stoplines

You need to be aware of border cases, although in practice they never really
are a problem:

\startbuffer
\ifdim \dimexpr16383.99997 pt/2\relax = \dimexpr 16383.99998 pt/2\relax
    the same \else different
\fi
\ifdim \dimexpr16383.99997 pt:2\relax = \dimexpr 16383.99998 pt:2\relax
    the same \else different
\fi
\stopbuffer

\typebuffer[option=TEX]

\startlines
\getbuffer
\stoplines

\startbuffer
\ifdim \dimexpr1.99997 pt/2\relax = \dimexpr 1.99998 pt/2\relax
    the same \else different
\fi
\ifdim \dimexpr1.99997 pt:2\relax = \dimexpr 1.99998 pt:2\relax
    the same \else different
\fi
\stopbuffer

\typebuffer[option=TEX]

\startlines
\getbuffer
\stoplines

\startbuffer
\ifdim \dimexpr1.999999 pt/2\relax = \dimexpr 1.9999995 pt/2\relax
    the same \else different
\fi
\ifdim \dimexpr1.999999 pt:2\relax = \dimexpr 1.9999995 pt:2\relax
    the same \else different
\fi
\stopbuffer

\typebuffer[option=TEX]

\startlines
\getbuffer
\stoplines

This last case demonstrates that at some point the digits get dropped (still
assuming that the fraction is within the maximum permitted) so these numbers then
are the same. Anyway, this is not different in other programming languages and
just something you need to be aware of.

\stopsectionlevel

\stopsectionlevel

\startsectionlevel[title={\TEX\ primitives}]

\startsectionlevel[title={\tex{if}}]

I seldom use this one. Internally \TEX\ stores (and thinks) in terms of tokens.
If you see for instance \type {\def} or \type {\dimen} or \type {\hbox} these all
become tokens. But characters like \type {A} or {@} also become tokens. In this
test primitive all non|-|characters are considered to be the same. In the next
examples this is demonstrated.

\startbuffer
[\if AB yes\else nop\fi]
[\if AA yes\else nop\fi]
[\if CDyes\else nop\fi]
[\if CCyes\else nop\fi]
[\if\dimen\font yes\else nop\fi]
[\if\dimen\font yes\else nop\fi]
\stopbuffer

\typebuffer[option=TEX]

Watch how spaces after the two characters are kept: \inlinebuffer . This primitive looks
at the next two tokens but when doing so it expands. Just look at the following:

\startbuffer
\def\AA{AA}%
\def\AB{AB}%
[\if\AA yes\else nop\fi]
[\if\AB yes\else nop\fi]
\stopbuffer

\typebuffer[option=TEX]

We get: \inlinebuffer .

% protected macros

\stopsectionlevel

\startsectionlevel[title={\tex{ifcat}}]

In \TEX\ characters (in the input) get interpreted according to their so called
catcodes. The most common are letters (alphabetic) and and other (symbols) but
for instance the backslash has the property that it starts a command, the dollar
signs trigger math mode, while the curly braced deal with grouping. If for
instance either or not the ampersand is special (for instance as column separator
in tables) depends on the macro package.

\startbuffer
[\ifcat AB yes\else nop\fi]
[\ifcat AA yes\else nop\fi]
[\ifcat CDyes\else nop\fi]
[\ifcat CCyes\else nop\fi]
[\ifcat C1yes\else nop\fi]
[\ifcat\dimen\font yes\else nop\fi]
[\ifcat\dimen\font yes\else nop\fi]
\stopbuffer

\typebuffer[option=TEX]

This time we also compare a letter with a number: \inlinebuffer . In that case
the category codes differ (letter vs other) but in this test comparing the
letters result in a match. This is a test that is used only once in \CONTEXT\ and
even that occasion is dubious and will go away.

You can use \type {\noexpand} to prevent expansion:

\startbuffer
\def\A{A}%
\let\B B%
\def\C{D}%
\let\D D%
[\ifcat\noexpand\A Ayes\else nop\fi]
[\ifcat\noexpand\B Byes\else nop\fi]
[\ifcat\noexpand\C Cyes\else nop\fi]
[\ifcat\noexpand\C Dyes\else nop\fi]
[\ifcat\noexpand\D Dyes\else nop\fi]
\stopbuffer

\typebuffer[option=TEX]

We get: \inlinebuffer, so who still thinks that \TEX\ is easy to understand for a
novice user?

\stopsectionlevel

\startsectionlevel[title={\tex{ifnum}}]

This condition compares its argument with another one, separated by an \type {<},
\type {=} or \type {>} character.

\starttyping[option=TEX]
\ifnum\scratchcounter<0
    less than
\else\ifnum\scratchcounter>0
    more than
\else
    equal to
\fi zero
\stoptyping

This is one of these situations where a dimension can be used instead. In that
case the dimension is in scaled points.

\starttyping[option=TEX]
\ifnum\scratchdimen<0
    less than
\else\ifnum\scratchdimen>0
    more than
\else
    equal to
\fi zero
\stoptyping

Of course this equal treatment of a dimension and number is only true when the
dimension is a register or box property.

\stopsectionlevel

\startsectionlevel[title={\tex{ifdim}}]

This condition compares one dimension with another one, separated by an \type {<},
\type {=} or \type {>} sign.

\starttyping[option=TEX]
\ifdim\scratchdimen<0pt
    less than
\else\ifdim\scratchdimen>0pt
    more than
\else
    equal to
\fi zero
\stoptyping

While when comparing numbers a dimension is a valid quantity but here you cannot
mix them: something with a unit is expected.

\stopsectionlevel

\startsectionlevel[title={\tex{ifodd}}]

This one can come in handy, although in \CONTEXT\ it is only used in checking for
an odd of even page number.

\startbuffer
\scratchdimen  3sp
\scratchcounter4

\ifodd\scratchdimen   very \else not so \fi odd
\ifodd\scratchcounter very \else not so \fi odd
\stopbuffer

\typebuffer[option=TEX]

As with the previously discussed \type {\ifnum} you can use a dimension variable
too, which is then interpreted as representing scaled points. Here we get:

\startlines
\getbuffer
\stoplines

\stopsectionlevel

\startsectionlevel[title={\tex{ifvmode}}]

This is a rather trivial check. It takes no arguments and just is true when we're
in vertical mode. Here is an example:

\startbuffer
\hbox{\ifvmode\else\par\fi\ifvmode v\else h\fi mode}
\stopbuffer

\typebuffer[option=TEX]

We're always in horizontal mode and issuing a \type {\par} inside a horizontal
box doesn't change that, so we get: \ruledhbox{\inlinebuffer}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifhmode}}]

As with \type {\ifvmode} this one has no argument and just tells if we're in
vertical mode.

\startbuffer
\vbox {
    \noindent \ifhmode h\else v\fi mode
    \par
    \ifhmode h\else \noindent v\fi mode
}
\stopbuffer

\typebuffer[option=TEX]

You can use it for instance to trigger injection of code, or prevent that some
content (or command) is done more than once:

\startlinecorrection
\ruledhbox{\inlinebuffer}
\stoplinecorrection

\stopsectionlevel

\startsectionlevel[title={\tex{ifmmode}}]

Math is something very \TEX\ so naturally you can check if you're in math mode.
here is an example of using this test:

\starttyping[option=TEX]
\def\enforcemath#1{\ifmmode#1\else$ #1 $\fi}
\stoptyping

Of course in reality macros that do such things are more advanced than this one.

\stopsectionlevel

\startsectionlevel[title={\tex{ifinner}}]

\startbuffer
\def\ShowMode
  {\ifhmode      \ifinner inner \fi hmode
   \else\ifvmode \ifinner inner \fi vmode
   \else\ifmmode \ifinner inner \fi mmode
   \else         \ifinner inner \fi unset
   \fi\fi\fi}
\stopbuffer

\typebuffer[option=TEX] \getbuffer

\startbuffer
\ShowMode \ShowMode

\vbox{\ShowMode}

\hbox{\ShowMode}

$\ShowMode$

$$\ShowMode$$
\stopbuffer

\typebuffer[option=TEX]

The first line has two tests, where the first one changes the mode to horizontal
simply because a text has been typeset. Watch how display math is not inner.

\startpacked
\startlines
\getbuffer
\stoplines
\stoppacked

By the way, moving the \type {\ifinner} test outside the branches (to the top of
the macro) won't work because once the word \type {inner} is typeset we're no
longer in vertical mode, if we were at all.

\stopsectionlevel

\startsectionlevel[title={\tex{ifvoid}}]

A box is one of the basic concepts in \TEX. In order to understand this primitive
we present four cases:

\startbuffer
\setbox0\hbox{}         \ifvoid0 void \else content \fi
\setbox0\hbox{123}      \ifvoid0 void \else content \fi
\setbox0\hbox{} \box0   \ifvoid0 void \else content \fi
\setbox0\hbox to 10pt{} \ifvoid0 void \else content \fi
\stopbuffer

\typebuffer[option=TEX]

In the first case, we have a box which is empty but it's not void. It helps to
know that internally an hbox is actually an object with a pointer to a linked
list of nodes. So, the first two can be seen as:

\starttyping
hlist -> [nothing]
hlist -> 1 -> 2 -> 3 -> [nothing]
\stoptyping

but in any case there is a hlist. The third case puts something in a hlist but
then flushes it. Now we have not even the hlist any more; the box register has
become void. The last case is a variant on the first. It is an empty box with a
given width. The outcome of the four lines (with a box flushed in between) is:

\startlines
\getbuffer
\stoplines

So, when you want to test if a box is really empty, you need to test also its
dimensions, which can be up to three tests, depending on your needs.

\startbuffer
\setbox0\emptybox                  \ifvoid0 void\else content\fi
\setbox0\emptybox        \wd0=10pt \ifvoid0 void\else content\fi
\setbox0\hbox to 10pt {}           \ifvoid0 void\else content\fi
\setbox0\hbox         {} \wd0=10pt \ifvoid0 void\else content\fi
\stopbuffer

\typebuffer[option=TEX]

Setting a dimension of a void (empty) box doesn't make it less void:

\startlines
\getbuffer
\stoplines

\stopsectionlevel

\startsectionlevel[title={\tex{ifhbox}}]

This test takes a box number and gives true when it is an hbox.

\stopsectionlevel

\startsectionlevel[title={\tex{ifvbox}}]

This test takes a box number and gives true when it is an vbox. Both a \type
{\vbox} and \type {\vtop} are vboxes, the difference is in the height and depth
and the baseline. In a \type {\vbox} the last line determines the baseline

\startlinecorrection
\ruledvbox{vbox or vtop\par vtop or vbox}
\stoplinecorrection

And in  a \type {\vtop} the first line takes control:

\startlinecorrection
\ruledvtop{vbox or vtop\par vtop or vbox}
\stoplinecorrection

but, once wrapped, both internally are just vlists.

\stopsectionlevel

\startsectionlevel[title={\tex{ifx}}]

This test is actually used a lot in \CONTEXT: it compares two token(list)s:

\startbuffer
                   \ifx a b  Y\else N\fi
                   \ifx ab   Y\else N\fi
\def\A {a}\def\B{b}\ifx \A\B Y\else N\fi
\def\A{aa}\def\B{a}\ifx \A\B Y\else N\fi
\def\A {a}\def\B{a}\ifx \A\B Y\else N\fi
\stopbuffer

\typebuffer[option=TEX]

Here the result is: \quotation{\inlinebuffer}. It does not expand the content, if
you want that you need to use an \type {\edef} to create two (temporary) macros
that get compared, like in:

\starttyping[option=TEX]
\edef\TempA{...}\edef\TempB{...}\ifx\TempA\TempB ...\else ...\fi
\stoptyping

\stopsectionlevel

\startsectionlevel[title={\tex{ifeof}}]

This test checks if a the pointer in a given input channel has reached its end.
It is also true when the file is not present. The argument is a number which
relates to the \type {\openin} primitive that is used to open files for reading.

\stopsectionlevel

\startsectionlevel[title={\tex{iftrue}}]

It does what it says: always true.

\stopsectionlevel

\startsectionlevel[title={\tex{iffalse}}]

It does what it says: always false.

\stopsectionlevel

\startsectionlevel[title={\tex{ifcase}}]

The general layout of an \type {\ifcase} tests is as follows:

\starttyping[option=TEX]
\ifcase<number>
    when zero
\or
    when one
\or
    when two
\or
    ...
\else
    when something else
\fi
\stoptyping

As in other places a number is a sequence of signs followed by one of more digits

\stopsectionlevel

\stopsectionlevel

\startsectionlevel[title={\ETEX\ primitives}]

\startsectionlevel[title={\tex{ifdefined}}]

This primitive was introduced for checking the existence of a macro (or primitive)
and with good reason. Say that you want to know if \type {\MyMacro} is defined? One
way to do that is:

\startbuffer
\ifx\MyMacro\undefined
    {\bf undefined indeed}
\fi
\stopbuffer

\typebuffer[option=TEX]

This results in: \inlinebuffer , but is this macro really undefined? When \TEX\
scans your source and sees a the escape character (the forward slash) it will
grab the next characters and construct a control sequence from it. Then it finds
out that there is nothing with that name and it will create a hash entry for a
macro with that name but with no meaning. Because \type {\undefined} is also not
defined, these two macros have the same meaning and therefore the \type {\ifx} is
true. Imagine that you do this many times, with different macro names, then your
hash can fill up. Also, when a user defined \type {\undefined} you're suddenly
get a different outcome.

In order to catch the last problem there is the option to test directly:

\startbuffer
\ifdefined\MyOtherMacro \else
    {\bf also undefined}
\fi
\stopbuffer

\typebuffer[option=TEX]

This (or course) results in: \inlinebuffer, but the macro is still sort of
defined (with no meaning). The next section shows how to get around this.

\stopsectionlevel

\startsectionlevel[title={\tex{ifcsname}}]

A macro is often defined using a ready made name, as in:

\starttyping[option=TEX]
\def\OhYes{yes}
\stoptyping

The name is made from characters with catcode letter which means that you cannot
use for instance digits or underscores unless you also give these characters that
catcode, which is not that handy in a document. You can however use \type
{\csname} to define a control sequence with any character in the name, like:

\starttyping[option=TEX]
\expandafter\def\csname Oh Yes : 1\endcsname{yes}
\stoptyping

Later on you can get this one with \type {\csname}:

\starttyping[option=TEX]
\csname Oh Yes : 1\endcsname
\stoptyping

However, if you say:

\starttyping[option=TEX]
\csname Oh Yes : 2\endcsname
\stoptyping

you won't get some result, nor a message about an undefined control sequence, but
the name triggers a define anyway, this time not with no meaning (undefined) but
as equivalent to \type {\relax}, which is why

\starttyping[option=TEX]
\expandafter\ifx\csname Oh Yes : 2\endcsname\relax
    {\bf relaxed indeed}
\fi
\stoptyping

is the way to test its existence. As with the test in the previous section,
this can deplete the hash when you do lots of such tests. The way out of this
is:

\starttyping[option=TEX]
\ifcsname Oh Yes : 2\endcsname \else
    {\bf unknown indeed}
\fi
\stoptyping

This time there is no hash entry created and therefore there is not even an
undefined control sequence.

In \LUATEX\ there is an option to return false in case of a messy expansion
during this test, and in \LUAMETATEX\ that is default. This means that tests can
be made quite robust as it is pretty safe to assume that names that make sense
are constructed from regular characters and not boxes, font switches, etc.

\stopsectionlevel

\startsectionlevel[title={\tex{iffontchar}}]

This test was also part of the \ETEX\ extensions and it can be used to see if
a font has a character.

\startbuffer
\iffontchar\font`A
    {\em This font has an A!}
\fi
\stopbuffer

\typebuffer[option=TEX]

And, as expected, the outcome is: \quotation {\inlinebuffer}. The test takes two
arguments, the first being a font identifier and the second a character number,
so the next checks are all valid:

\starttyping[option=TEX]
\iffontchar\font     `A yes\else nop\fi\par
\iffontchar\nullfont `A yes\else nop\fi\par
\iffontchar\textfont0`A yes\else nop\fi\par
\stoptyping

In the perspective of \LUAMETATEX\ I considered also supporting \type {\fontid}
but it got a bit messy due to the fact that this primitive expands in a different
way so this extension was rejected.

\stopsectionlevel

\startsectionlevel[title={\tex{unless}}]

You can negate the results of a test by using the \type {\unless} prefix, so for
instance you can replace:

\starttyping[option=TEX]
\ifdim\scratchdimen=10pt
    \dosomething
\else\ifdim\scratchdimen<10pt
    \dosomething
\fi\fi
\stoptyping

by:

\starttyping[option=TEX]
\unless\ifdim\scratchdimen>10pt
    \dosomething
\fi
\stoptyping

An \type {\unless} makes little sense when used with \type {\ifcase} but contrary
to the other engines we don't error or it; we just give a warning. Some
conditionals internally use a case so there we can actually provide a variant:

\startbuffer
\ifcase 1 \relax zero \or one \or two \else else \fi = one \par
\ifcase 2 \relax zero \or one \or two \else else \fi = two \par

\unless\ifcase 1 \relax zero \or one \or two \else else \fi % warning
\unless\ifcase 2 \relax zero \or one \or two \else else \fi % warning

\ifchkdim1pt\or yes \else nop \fi = yes \par
\ifchkdim2  \or nop \else yes \fi = yes \par

\unless\ifchkdim1pt\or nop \else yes \fi = yes \par
\unless\ifchkdim2  \or yes \else nop \fi = yes \par
\stopbuffer

\typebuffer[option=TEX]

The \typ {\ifchkdim}, \typ {\ifchkdimension}, \typ {\ifchknum}, \typ
{\ifchknumber} and \typ {\ifparameter} are supported.

\startpacked \getbuffer \stoppacked

\stopsectionlevel

\stopsectionlevel

\startsectionlevel[title={\LUATEX\ primitives}]

\startsectionlevel[title={\tex{ifincsname}}]

As it had no real practical usage uit might get dropped in \LUAMETATEX, so it
will not be discussed here.

\stopsectionlevel

\startsectionlevel[title={\tex{ifprimitive}}]

As it had no real practical usage due to limitations, this one is not available
in \LUAMETATEX\ so it will not be discussed here. If really needed you can use \type
{\ifflags}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifabsnum}}]

This test is inherited from \PDFTEX\ and behaves like \type {\ifnum} but first
turns a negative number into a positive one.

\stopsectionlevel

\startsectionlevel[title={\tex{ifabsdim}}]

This test is inherited from \PDFTEX\ and behaves like \type {\ifdim} but first
turns a negative dimension into a positive one.

\stopsectionlevel

\startsectionlevel[title={\tex{ifcondition}}]

This is not really a test but in order to unstand that you need to know how
\TEX\ internally deals with tests.

\starttyping[option=TEX]
\ifdimen\scratchdimen>10pt
    \ifdim\scratchdimen<20pt
        result a
    \else
        result b
    \fi
\else
    result c
\fi
\stoptyping

When we end up in the branch of \quotation {result a} we need to skip two \type
{\else} branches after we're done. The \type {\if..} commands increment a level
while the \type {\fi} decrements a level. The \type {\else} needs to be skipped
here. In other cases the true branch needs to be skipped till we end up a the
right \type {\else}. When doing this skipping, \TEX\ is not interested in what it
encounters beyond these tokens and this skipping (therefore) goes real fast but
it does see nested conditions and doesn't interpret grouping related tokens.

A side effect of this is that the next is not working as expected:

\starttyping[option=TEX]
\def\ifmorethan{\ifdim\scratchdimen>}
\def\iflessthan{\ifdim\scratchdimen<}

\ifmorethan10pt
    \iflessthan20pt
        result a
    \else
        result b
    \fi
\else
    result c
\fi
\stoptyping

The \type{\iflessthan} macro is not seen as an \type {\if...} so the nesting gets
messed up. The solution is to fool the scanner in thinking that it is. Say we have:

\startbuffer
\scratchdimen=25pt

\def\ifmorethan{\ifdim\scratchdimen>}
\def\iflessthan{\ifdim\scratchdimen<}
\stopbuffer

\typebuffer[option=TEX] \getbuffer

and:

\startbuffer
\ifcondition\ifmorethan10pt
    \ifcondition\iflessthan20pt
        result a
    \else
        result b
    \fi
\else
    result c
\fi
\stopbuffer

\typebuffer[option=TEX]

When we expand this snippet we get: \quotation {\inlinebuffer} and no error
concerning a failure in locating the right \type {\fi's}. So, when scanning the
\type {\ifcondition} is seen as a valid \type {\if...} but when the condition is
really expanded it gets ignored and the \type {\ifmorethan} has better come up
with a match or not.

In this perspective it is also worth mentioning that nesting problems can be
avoided this way:

\starttyping[option=TEX]
\def\WhenTrue {something \iftrue  ...}
\def\WhenFalse{something \iffalse ...}

\ifnum\scratchcounter>123
    \let\next\WhenTrue
\else
    \let\next\WhenFalse
\fi
\next
\stoptyping

This trick is mentioned in The \TeX book and can also be found in the plain \TEX\
format. A variant is this:

\starttyping[option=TEX]
\ifnum\scratchcounter>123
    \expandafter\WhenTrue
\else
    \expandafter\WhenFalse
\fi
\stoptyping

but using \type {\expandafter} can be quite intimidating especially when there
are multiple in a row. It can also be confusing. Take this: an \type
{\ifcondition} expects the code that follows to produce a test. So:

\starttyping[option=TEX]
\def\ifwhatever#1%
  {\ifdim#1>10pt
      \expandafter\iftrue
   \else
      \expandafter\iffalse
   \fi}

\ifcondition\ifwhatever{10pt}
    result a
\else
    result b
\fi
\stoptyping

This will not work! The reason is in the already mentioned fact that when we end
up in the greater than \type {10pt} case, the scanner will happily push the \type
{\iftrue} after the \type {\fi}, which is okay, but when skipping over the \type
{\else} it sees a nested condition without matching \type {\fi}, which makes ity
fail. I will spare you a solution with lots of nasty tricks, so here is the clean
solution using \type {\ifcondition}:

\starttyping[option=TEX]
\def\truecondition {\iftrue}
\def\falsecondition{\iffalse}

\def\ifwhatever#1%
  {\ifdim#1>10pt
      \expandafter\truecondition
   \else
      \expandafter\falsecondition
   \fi}

\ifcondition\ifwhatever{10pt}
    result a
\else
    result b
\fi
\stoptyping

It will be no surprise that the two macros at the top are predefined in \CONTEXT.
It might be more of a surprise that at the time of this writing the usage in
\CONTEXT\ of this \type {\ifcondition} primitive is rather minimal. But that
might change.

As a further teaser I'll show another simple one,

\startbuffer
\def\HowOdd#1{\unless\ifnum\numexpr ((#1):2)*2\relax=\numexpr#1\relax}

\ifcondition\HowOdd{1}very \else not so \fi odd
\ifcondition\HowOdd{2}very \else not so \fi odd
\ifcondition\HowOdd{3}very \else not so \fi odd
\stopbuffer

\typebuffer[option=TEX]

This renders:

\startlines
\getbuffer
\stoplines

The code demonstrates several tricks. First of all we use \type {\numexpr} which
permits more complex arguments, like:

\starttyping[option=TEX]
\ifcondition\HowOdd{4+1}very \else not so \fi odd
\ifcondition\HowOdd{2\scratchcounter+9}very \else not so \fi odd
\stoptyping

Another trick is that we use an integer division (the \type {:}) which is an
operator supported by \LUAMETATEX .

\stopsectionlevel

\stopsectionlevel

\startsectionlevel[title={\LUAMETATEX\ primitives}]

\startsectionlevel[title={\tex{ifnum} and \type {ifdim}}]

These have been extended with a few more operators. For instance, we can use a
negation:

\startbuffer
\ifnum 10  > 5 Y\else N\fi
\ifnum 10 !> 5 Y\else N\fi
\stopbuffer

\typebuffer[option=TEX]

Results in: \inlinebuffer. A bitwise comparison is possible too:

\startbuffer
\ifnum "02  & 2 Y\else N\fi
\ifnum "02  & 4 Y\else N\fi
\ifnum "02 !& 8 Y\else N\fi
\stopbuffer

\typebuffer[option=TEX]

yields: \inlinebuffer. You can also use the \UNICODE\ variants \type {∈}, \type
{∉}, \type {≠}, \type {≤}, \type {≥}, \type {≰}, and \type {≱}.

\stopsectionlevel

\startsectionlevel[title={\tex{iffloat}}]

This is a test for a float, much like a test for a dimen without unit.

\stopsectionlevel

\startsectionlevel[title={\tex{ifabsfloat}}]

This is a test for a float, much like a test for a dimen without unit.

\stopsectionlevel

\startsectionlevel[title={\tex{ifintervalnum}}]

This is a test for equality of two numbers within an interval, as in:

\startbuffer
\ifintervalnum   1   2 1 Y\else N\fi
\ifintervalnum   1   3 1 Y\else N\fi
\ifintervalnum 100 102 1 Y\else N\fi
\ifintervalnum 100 102 3 Y\else N\fi
\stopbuffer

\typebuffer[option=TEX]

which results in: \inlinebuffer.

\stopsectionlevel

\startsectionlevel[title={\tex{ifintervaldim}}]

This is a test for equality of two dimensions within an interval, as in:

\startbuffer
\ifintervaldim   1pt   2pt 1pt Y\else N\fi
\ifintervaldim   1pt   3pt 1pt Y\else N\fi
\ifintervaldim 100pt 102pt 1pt Y\else N\fi
\ifintervaldim 100pt 102pt 3pt Y\else N\fi
\stopbuffer

\typebuffer[option=TEX]

We get: \inlinebuffer.

\stopsectionlevel

\startsectionlevel[title={\tex{ifintervalfloat}}]

This is a test for a float, much like a test for a dimen without unit.

\stopsectionlevel

\startsectionlevel[title={\tex{ifdimexpression}}]

This is a boolean checker so the comparison is done as part of the expression, as in:

\starttyping[option=TEX]
\ifdimexpression{10pt > (4pt + 8pt)}Y\else N\fi
\stoptyping

\stopsectionlevel

\startsectionlevel[title={\tex{ifnumexpression}}]

This is a boolean checker so the comparison is done as part fo the expression, as in:

\starttyping[option=TEX]
\ifnumexpression{10 > (4 + 8)}Y\else N\fi
\stoptyping

\stopsectionlevel

\startsectionlevel[title={\tex{ifcmpnum}}]

This one is part of s set of three tests that all are a variant of a \type
{\ifcase} test. A simple example of the first test is this:

\starttyping[option=TEX]
\ifcmpnum 123 345 less \or equal \else more \fi
\stoptyping

The test scans for two numbers, which of course can be registers or expressions,
and sets the case value to 0, 1 or 2, which means that you then use the normal
\type {\or} and \type {\else} primitives for follow up on the test.

\stopsectionlevel

\startsectionlevel[title={\tex{ifchknum}}]

This test scans a number and when it's okay sets the case value to 1, and otherwise
to 2. So you can do the next:

\starttyping[option=TEX]
\ifchknum 123\or good \else bad \fi
\ifchknum bad\or good \else bad \fi
\stoptyping

An error message is suppressed and the first \type {\or} can be seen as a sort of
recovery token, although in fact we just use the fast scanner mode that comes
with the \type {\ifcase}: because the result is 1 or 2, we never see invalid
tokens.

In order to avoid another scan the a valid result it is made available in \type
{\lastchknumber}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifchknumber}}]

This one is a more rigorous variant of \type {\ifchknum} and doesn't like
trailing non numeric crap.

\stopsectionlevel

\startsectionlevel[title={\tex{ifchknumexpr}}]

This test goes a bit further and accepts an expression.

\starttyping[option=TEX]
\ifchknumexpr 123 + 45\or good \else bad \fi
\stoptyping

As with the other checkers, if there is a valid result it is available in \type
{\lastchknumber}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifnumval}}]

A sort of combination of the previous two is \type {\ifnumval} which checks a
number but also if it's less, equal or more than zero:

\starttyping[option=TEX]
\ifnumval 123\or less \or equal \or more \else error \fi
\ifnumval bad\or less \or equal \or more \else error \fi
\stoptyping

You can decide to ignore the bad number or do something that makes more sense.
Often the to be checked value will be the content of a macro or an argument like
\type {#1}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifcmpdim}}]

This test is like \type {\ifcmpnum} but for dimensions.

\stopsectionlevel

\startsectionlevel[title={\tex{ifchkdim}}]

This test is like \type {\ifchknum} but for dimensions. The last checked value is
available as \type {\lastchknumber}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifchkdimension}}]

This one is a more rigorous variant of \type {\ifchkdim} and doesn't like
trailing rubish.

\stopsectionlevel

\startsectionlevel[title={\tex{ifchkdimexpr}}]

This test is like \type {\ifchknumexpr} but for dimensions. The last checked value is
available as \type {\lastchkdimension}.

\stopsectionlevel

\startsectionlevel[title={\tex{ifdimval}}]

This test is like \type {\ifnumval} but for dimensions. The last checked value is
available as \type {\lastchkdim}

\stopsectionlevel

\startsectionlevel[title={\tex{iftok}}]

Although this test is still experimental it can be used. What happens is that
two to be compared \quote {things} get scanned for. For each we first gobble
spaces and \type {\relax} tokens. Then we can have several cases:

\startitemize[n,packed]
    \startitem
        When we see a left brace, a list of tokens is scanned upto the
        matching right brace.
    \stopitem
    \startitem
        When a reference to a token register is seen, that register is taken as
        value.
    \stopitem
    \startitem
        When a reference to an internal token register is seen, that register is
        taken as value.
    \stopitem
    \startitem
        When a macro is seen, its definition becomes the to be compared value.
    \stopitem
    \startitem
        When a number is seen, the value of the corresponding register is taken
    \stopitem
\stopitemize

An example of the first case is:

\starttyping[option=TEX]
\iftok {abc} {def}%
  ...
\else
  ...
\fi
\stoptyping

The second case goes like this:

\starttyping[option=TEX]
\iftok\scratchtoksone\scratchtokstwo
  ...
\else
  ...
\fi
\stoptyping

Case one and four mixed:

\starttyping[option=TEX]
\iftok{123}\TempX
  ...
\else
  ...
\fi
\stoptyping

The last case is more a catch: it will issue an error when no number is given.
Eventually that might become a bit more clever (depending on our needs.)

\stopsectionlevel

\startsectionlevel[title={\tex{ifzeronum}, \tex{ifzerodim}, \tex{ifzerofloat}}]

The names of these three tells what they do: checking for a zero value.

\startbuffer
(\ifzerodim   10pt\norelax A\orelse\ifzerodim   0pt\norelax B\else C\fi)
(\ifzeronum   10  \norelax A\orelse\ifzeronum   0  \norelax B\else C\fi)
(\ifzerofloat 10.0\norelax A\orelse\ifzerofloat 0.0\norelax B\else C\fi)
\stopbuffer

\typebuffer[option=TEX]

Here we use the \type {\norelax} to get rid of trailing spaces: \inlinebuffer.

\stopsectionlevel

\startsectionlevel[title={\tex{ifhaschar}, \tex{ifhastok}, \tex{ifhastoks},\tex{ifhasxtoks}}]

These checkers can be used to identify a (sequence) of token(s) in a given token
list. Their working can best be shown with a few examples:

\startbuffer
\ifhaschar   c {abcd}Y\else N\fi
\ifhastok    c {abcd}Y\else N\fi
\ifhastoks  {c}{abcd}Y\else N\fi
\ifhasxtoks {c}{abcd}Y\else N\fi

\def\abcd{abcd}

\ifhaschar   c {\abcd}Y\else N\fi
\ifhastok    c {\abcd}Y\else N\fi
\ifhastoks  {c}{\abcd}Y\else N\fi
\ifhasxtoks {c}{\abcd}Y\else N\fi

\ifhaschar   c {a{bc}d}Y\else N\fi
\ifhastok    c {a{bc}d}Y\else N\fi
\ifhastoks  {c}{a{bc}d}Y\else N\fi
\ifhasxtoks {c}{a{bc}d}Y\else N\fi

\def\abcd{a{bc}d}

\ifhaschar   c {\abcd}Y\else N\fi
\ifhastok    c {\abcd}Y\else N\fi
\ifhastoks  {c}{\abcd}Y\else N\fi
\ifhasxtoks {c}{\abcd}Y\else N\fi
\stopbuffer

\typebuffer[option=TEX] \getbuffer

The \type {\ifhaschar} test will not descend into a braced sublist. The \type {x}
variants expand the list before comparison.

\stopsectionlevel

\startsectionlevel[title={\tex{ifcstok}}]

There is a subtle difference between this one and \type {\iftok}: spaces
and \type {\relax} tokens are skipped but nothing gets expanded. So, when
we arrive at the to be compared \quote {things} we look at what is there,
as|-|is.

\stopsectionlevel

\startsectionlevel[title={\tex{iffrozen}}]

{\em This is an experimental test.} Commands can be defined with the \type
{\frozen} prefix and this test can be used to check if that has been the case.

\stopsectionlevel

\startsectionlevel[title={\tex{ifprotected}}]

Commands can be defined with the \type {\protected} prefix (or in \CONTEXT, for
historic reasons, with \type {\unexpanded}) and this test can be used to check if
that has been the case.

\stopsectionlevel

\startsectionlevel[title={\tex{ifarguments}}]

This conditional can be used to check how many arguments were matched. It only
makes sense when used with macros defined with the \type {\tolerant} prefix
and|/|or when the sentinel \type {\ignorearguments} after the arguments is used.
More details can be found in the lowlevel macros manual.

\stopsectionlevel

\startsectionlevel[title={\tex{ifrelax}}]

\startbuffer
            \ifrelax\relax                  Y\else N\fi
            \ifrelax\norelax                Y\else N\fi
\expandafter\ifrelax\csname ReLaX\endcsname Y\else N\fi
\stopbuffer

The following tests all return the same: \inlinebuffer; it is a shortcut for
\type {\ifx ... \relax} that looks nicer in code.

\typebuffer[option=TEX]

\stopsectionlevel

\startsectionlevel[title={\tex{ifempty}}]

\startbuffer
\ifempty{}     Y\else N\fi
\ifempty{!}    Y\else N\fi
\ifempty\empty Y\else N\fi
\stopbuffer

This is again a shortcut, this time for \type {\ifx ...\empty} assuming that
\type {\empty} is defined as being nothing. Instead of a token you can also pass
a list, so here we get \inlinebuffer.

\typebuffer[option=TEX]

\stopsectionlevel

\startsectionlevel[title={\tex{iflastnamedcs}}]

This test is part of the \type {\csname} repertoire and uses the last valid
result from such a command.

\startbuffer
\def\Hello{upper}
\def\hello{lower}
\ifcsname Hello\endcsname
    \iflastnamedcs\hello
      world
    \orelse\iflastnamedcs\Hello
      World
    \fi
\fi
\stopbuffer

\typebuffer[option=TEX]

Here the \quote {Hello} test result in \quote {\nospacing\inlinebuffer}. It is an
example of a follow up test, most likely used in user interfacing.

\stopsectionlevel

\startsectionlevel[title={\tex{ifboolean}}]

Another new one is the following: it tests a number for being zero or not. As
with any primitive that scans for a number, it accepts a braced expression too.

\startbuffer
(\ifboolean 0 T\else F\fi)
(\ifboolean 1 T\else F\fi)
(\ifboolean {(2 * 4) < 5} T\else F\fi)
(\ifboolean \dimexpression{(1em > 20pt) or  (1ex > 15pt)} T\else F\fi)
(\ifboolean \dimexpression{(1em > 3pt)  and (1ex <  3pt)} T\else F\fi)
\stopbuffer

\typebuffer[option=TEX]

We get: \inlinebuffer.

\stopsectionlevel

\startsectionlevel[title={\tex{iflist}}]

The \type {\ifvoid} test doesn't really test for a box being empty, which is
why we have an additional primitive. Compare the following:

\startbuffer
\setbox0\hbox{}
\setbox2\hbox{!}
\setbox4\emptybox % \box\voidbox
\setbox8\box6

\wd0 10pt \wd2 10pt \wd4 10pt \wd6 10pt

[\ifvoid0 Y\else N\fi \iflist0 Y\else N\fi \the\wd0] % empty  hbox
[\ifvoid2 Y\else N\fi \iflist2 Y\else N\fi \the\wd2] % hbox with content
[\ifvoid4 Y\else N\fi \iflist4 Y\else N\fi \the\wd4] % no box
[\ifvoid6 Y\else N\fi \iflist6 Y\else N\fi \the\wd6] % no box
\stopbuffer

\typebuffer[option=TEX]

The result demonstrates that we check if there is any content at all, independent
of dimensions or the presence of a wrapping list node.

\startlines
\getbuffer
\stoplines

\stopsectionlevel

\startsectionlevel[title={\tex{ifcramped}}]

This test relates to math and in particular to four of the eight states:

\startbuffer
\im {
    \sqrt
        {\ifcramped\mathstyle y\else n\fi}
      ^ {\ifcramped\mathstyle y\else n\fi}
      _ {\ifcramped\mathstyle y\else n\fi}
}
\stopbuffer

\typebuffer[option=TEX]

Because a math formula is first read and then processed in several passes you
need to be aware of this state not always being easily predictable because there
can be a delay between that read and successive treatments.

\startlinecorrection
\scale[s=2]{\showglyphs\getbuffer}
\stoplinecorrection

\stopsectionlevel

\startsectionlevel[title={\tex{ifmathparameter}}]

The next example demonstrates what this test provides:

\startbuffer
[\ifmathparameter\Umathextrasubspace   \displaystyle zero\or set\else unset\fi]
[\ifmathparameter\Umathaccentbaseheight\displaystyle zero\or set\else unset\fi]
[\ifmathparameter\Umathaccentbasedepth \displaystyle zero\or set\else unset\fi]
\stopbuffer

\typebuffer[option=TEX]

There are three possible outcomes; here we get: \inlinebuffer. In \LUAMETATEX\ we
have more math parameters than in \LUATEX, and some are set in font specific so
called \quote {goodie} files.

\stopsectionlevel

\startsectionlevel[title={\tex{ifmathstyle}}]

Here you need to keep in mind that you test the style that is set when \TEX\ scans for
formula. Processing happens afterwards and then styles can change.

\startbuffer
   {\ifmathstyle D\or D'\or T\or T'\or S\or S'\or SS\or SS'\else ?\fi}
\im{\ifmathstyle D\or D'\or T\or T'\or S\or S'\or SS\or SS'\else ?\fi}
\dm{\ifmathstyle D\or D'\or T\or T'\or S\or S'\or SS\or SS'\else ?\fi}
\stopbuffer

\typebuffer[option=TEX]

We get: \inlinebuffer. The odd values are cramped.


\stopsectionlevel

\startsectionlevel[title={\tex{ifinalignment}}]

This test is an experimental one:

\startbuffer
\halign \bgroup
    \aligncontent
    \aligntab
    \aligncontent
    \cr
    one \aligntab \ifinalignment two\else three\fi \cr
    \noalign{\ifinalignment yes\else no\fi}
    one \aligntab \hbox{\ifinalignment two\else three\fi} \cr
\egroup

\hbox{\ifinalignment two\else three\fi}
\stopbuffer

\typebuffer[option=TEX]

We get:

\startpacked\getbuffer \stoppacked

\stopsectionlevel

\startsectionlevel[title={\tex{ifinsert}}]

This primitive checks if an insert box has content. Usage depends on the macro
package so for instance in \CONTEXT, after \type {\footnote {A note.}} you can
actually check it with:

\starttyping[option=TEX]
\setupheadertexts[\ifinsert\namedinsertionnumber{footnote} Y\else N\fi]
\stoptyping

You pass the number of a insert class and in this example the content, set by the
page builder, hasn't yet been flushed.

\stopsectionlevel

\startsectionlevel[title={\tex{ifflags}}]

This one related to interfacing. When a macro is defined, one can apply several
prefixes to that macro. Some of these prefixes result in a specific kind of
macro, for instance a protected, tolerant, tolerant protected, or regular macro.
When a macro is defined global, its (internal) level value indicates that. In
addition macros, or actually any control sequence, also the built-in ones, can
have a set of flags. Some, have consequences in the engine, so for instance an
untraced macro will present itself as a primitive, without details that clutter a
log. Other flags get meaning when the overload protection mechanisms are enabled.

Testing flags can give some insight but in \CONTEXT\ there is little reason to
use this test other than for illustrative purposes. Take this definition

\startbuffer
\global\protected\def\Foo{Foo}
\stopbuffer

\typebuffer[option=TEX]

This macro is internally represented as follows; here we used \type
{\meaningasis}:

\start
\getbuffer \tt \meaningasis\Foo
\stop

When we use \type {\meaning} we get:

\start
\getbuffer \tt \meaning\Foo
\stop

With \type {\meaningfull} we get:

\start
\getbuffer \tt \meaningfull\Foo
\stop

Here is how you can test what properties and flags are set.

\startbuffer[test]
\ifflags\Foo\global    global    \fi
\ifflags\Foo\protected protected \fi
\ifflags\Foo\tolerant  tolerant  \fi
\stopbuffer

\typebuffer[test][option=TEX]

We only show a few tests here:

\start \getbuffer \tt \getbuffer[test] \stop

Instead of a prefix you can also pass a number:

\startbuffer[test]
\ifflags\relax\primitiveflagcode primitive \fi
\ifflags\relax\permanentflagcode permanent \fi
\stopbuffer

\typebuffer[test][option=TEX]

\start \tt \getbuffer[test] \stop

In \CONTEXT\ many macros are defined as permanent which in terms of overload
protection has the same impact. Relevant flag values are available in \typ
{tex.getflagvalues()} but in \CONTEXT\ we prefer predefined constants:

\startalign[flushleft]
\startluacode
local done = false
for k, v in table.sortedhash(tex.flagcodes) do
    if type(k) == "string" then
        if done then
            context(", ")
        else
            done = true
        end
        context.tex(k .. "flagcode")
    end
end
\stopluacode
\stopalign

\stopsectionlevel

\startsectionlevel[title={\tex{ifparameters}}]

This is an \type {\ifcase} where the number is the number of parameters passed to
the current macro. Of course, when used in a macro one should be aware of the
fact that another macro call will change this number.

\stopsectionlevel

\startsectionlevel[title={\tex{ifparameter}}]

This test checks if a parameter has been set, and it's used as follows:

\starttyping
\ifparameter#4\or set\else unset\fi
\stoptyping

because \type {#4} is actually a reference it refers to the parameter in the
current macro and is not influences by nested macro calls which makes if more
reliable than a \type {\ifparameters} test.

\stopsectionlevel

\startsectionlevel[title={\tex{orelse}}]

This it not really a test primitive but it does act that way. Say that we have this:

\starttyping[option=TEX]
\ifdim\scratchdimen>10pt
    case 1
\else\ifdim\scratchdimen<20pt
    case 2
\else\ifcount\scratchcounter>10
    case 3
\else\ifcount\scratchcounter<20
    case 4
\fi\fi\fi\fi
\stoptyping

A bit nicer looks this:

\starttyping[option=TEX]
\ifdim\scratchdimen>10pt
    case 1
\orelse\ifdim\scratchdimen<20pt
    case 2
\orelse\ifcount\scratchcounter>10
    case 3
\orelse\ifcount\scratchcounter<20
    case 4
\fi
\stoptyping

% We stay at the same level and the only test that cannot be used this way is \type
% {\ifcondition} but that is no real problem. Sometimes a more flat test tree had

We stay at the same level. Sometimes a more flat test tree had advantages but if
you think that it gives better performance then you will be disappointed. The
fact that we stay at the same level is compensated by a bit more parsing, so
unless you have millions such cases (or expansions) it might make a bit of a
difference. As mentioned, I'm a bit sensitive for how code looks so that was the
main motivation for introducing it. There is a companion \type {\orunless}
continuation primitive.

A rather neat trick is the definition of \type {\quitcondition}:

\starttyping[option=TEX]
\def\quitcondition{\orelse\iffalse}
\stoptyping

This permits:

\starttyping[option=TEX]
\ifdim\scratchdimen>10pt
    case 1a
    \quitcondition
    case 4b
\fi
\stoptyping

where, of course, the quitting normally is the result of some intermediate extra
test. But let me play safe here: beware of side effects.

\stopsectionlevel

\startsectionlevel[title={\tex{orunless}}]

This is the negated variant of \type {\orelse}.

\stopsectionlevel

\stopsectionlevel

\startsectionlevel[title={For the brave}]

\startsectionlevel[title={Full expansion}]

If you don't understand the following code, don't worry. There is seldom much
reason to go this complex but obscure \TEX\ code attracts some users so \unknown

When you have a macro that has for instance assignments, and when you expand that
macro inside an \type {\edef}, these assignments are not actually expanded but
tokenized. In \LUAMETATEX\ there is a way to apply these assignments without side
effects and that feature can be used to write a fully expandable user test. For
instance:

\startbuffer
\def\truecondition {\iftrue}
\def\falsecondition{\iffalse}

\def\fontwithidhaschar#1#2%
  {\beginlocalcontrol
   \scratchcounter\numexpr\fontid\font\relax
   \setfontid\numexpr#1\relax
   \endlocalcontrol
   \iffontchar\font\numexpr#2\relax
      \beginlocalcontrol
      \setfontid\scratchcounter
      \endlocalcontrol
      \expandafter\truecondition
   \else
      \expandafter\falsecondition
   \fi}
\stopbuffer

\typebuffer[option=TEX] \getbuffer

The \type {\iffontchar} test doesn't handle numeric font id, simply because
at the time it was added to \ETEX, there was no access to these id's. Now we
can do:

\startbuffer
\edef\foo{\fontwithidhaschar{1} {75}yes\else nop\fi} \meaning\foo
\edef\foo{\fontwithidhaschar{1}{999}yes\else nop\fi} \meaning\foo

[\ifcondition\fontwithidhaschar{1} {75}yes\else nop\fi]
[\ifcondition\fontwithidhaschar{1}{999}yes\else nop\fi]
\stopbuffer

\typebuffer[option=TEX]

These result in:

\startlines
\getbuffer
\stoplines

If you remove the \type {\immediateassignment} in the definition above then the
typeset results are still the same but the meanings of \type {\foo} look
different: they contain the assignments and the test for the character is
actually done when constructing the content of the \type {\edef}, but for the
current font. So, basically that test is now useless.

\stopsectionlevel

\startsectionlevel[title={User defined if's}]

There is a \type {\newif} macro that defines three other macros:

\starttyping[option=TEX]
\newif\ifOnMyOwnTerms
\stoptyping

After this, not only \type {\ifOnMyOwnTerms} is defined, but also:

\starttyping[option=TEX]
\OnMyOwnTermstrue
\OnMyOwnTermsfalse
\stoptyping

These two actually are macros that redefine \type {\ifOnMyOwnTerms} to be either
equivalent to \type {\iftrue} and \type {\iffalse}. The (often derived from plain
\TEX) definition of \type {\newif} is a bit if a challenge as it has to deal with
removing the \type {if} in order to create the two extra macros and also make
sure that it doesn't get mixed up in a catcode jungle.

In \CONTEXT\ we have a variant:

\starttyping[option=TEX]
\newconditional\MyConditional
\stoptyping

that can be used with:

\starttyping[option=TEX]
\settrue\MyConditional
\setfalse\MyConditional
\stoptyping

and tested like:

\starttyping[option=TEX]
\ifconditional\MyConditional
    ...
\else
    ...
\fi
\stoptyping

This one is cheaper on the hash and doesn't need the two extra macros per test.
The price is the use of \type {\ifconditional}, which is {\em not} to confused
with \type {\ifcondition} (it has bitten me already a few times).

\stopsectionlevel

\stopsectionlevel

\startsectionlevel[title=Relaxing]

When \TEX\ scans for a number or dimension it has to check tokens one by one. On
the case of a number, the scanning stops when there is no digit, in the case of a
dimension the unit determine the end of scanning. In the case of a number, when a
token is not a digit that token gets pushed back. When digits are scanned a
trailing space or \type {\relax} is pushed back. Instead of a number of dimension
made from digits, periods and units, the scanner also accepts registers, both the
direct accessors like \type {\count} and \type {\dimen} and those represented by
one token. Take these definitions:

\startbuffer
\newdimen\MyDimenA \MyDimenA=1pt  \dimen0=\MyDimenA
\newdimen\MyDimenB \MyDimenB=2pt  \dimen2=\MyDimenB
\stopbuffer

\typebuffer[option=TEX] \getbuffer

I will use these to illustrate the side effects of scanning. Watch the spaces
in the result.

% \startbuffer[a]
% \testfeatureonce{1000000}{
%     \whatever{1pt}{2pt}%
%     \whatever{1pt}{1pt}%
%     \whatever{\dimen0}{\dimen2}%
%     \whatever{\dimen0}{\dimen0}%
%     \whatever\MyDimenA\MyDimenB
%     \whatever\MyDimenA\MyDimenB
% } \elapsedtime
% \stopbuffer

\startbuffer[b]
\starttabulate[|T|T|]
\NC \type {\whatever{1pt}{2pt}}         \NC \edef\temp{\whatever        {1pt}{2pt}}[\meaning\temp] \NC \NR
\NC \type {\whatever{1pt}{1pt}}         \NC \edef\temp{\whatever        {1pt}{1pt}}[\meaning\temp] \NC \NR
\NC \type {\whatever{\dimen0}{\dimen2}} \NC \edef\temp{\whatever{\dimen0}{\dimen2}}[\meaning\temp] \NC \NR
\NC \type {\whatever{\dimen0}{\dimen0}} \NC \edef\temp{\whatever{\dimen0}{\dimen0}}[\meaning\temp] \NC \NR
\NC \type {\whatever\MyDimenA\MyDimenB} \NC \edef\temp{\whatever\MyDimenA\MyDimenB}[\meaning\temp] \NC \NR
\NC \type {\whatever\MyDimenA\MyDimenB} \NC \edef\temp{\whatever\MyDimenA\MyDimenB}[\meaning\temp] \NC \NR
\stoptabulate
\stopbuffer

First I show what effect we want to avoid. When second argument contains a number
(digits) the zero will become part of it so we actually check \type {\dimen00}
here.

\startbuffer[c][option=TEX]
\def\whatever#1#2%
  {\ifdim#1=#20\else1\fi}
\stopbuffer

\typebuffer[c][option=TEX] \getbuffer[c,b]

The solution is to add a space but watch how that one can end up in the result:

\startbuffer[c][option=TEX]
\def\whatever#1#2%
  {\ifdim#1=#2 0\else1\fi}
\stopbuffer

\typebuffer[c][option=TEX] \getbuffer[c,b]

A variant is using \type {\relax} and this time we get this token retained in
the output.

\startbuffer[c][option=TEX]
\def\whatever#1#2%
  {\ifdim#1=#2\relax0\else1\fi}
\stopbuffer

\typebuffer[c][option=TEX] \getbuffer[c,b]

A solution that doesn't have side effects of forcing the end of a number (using a
space or \type {\relax} is one where we use expressions. The added overhead of
scanning expressions is taken for granted because the effect is what we like:

\startbuffer[c][option=TEX]
\def\whatever#1#2%
  {\ifdim\dimexpr#1\relax=\dimexpr#2\relax0\else1\fi}
\stopbuffer

\typebuffer[c][option=TEX] \getbuffer[c,b]

Just for completeness we show a more obscure trick: this one hides assignments to
temporary variables. Although performance is okay, it is the least efficient
one so far.

\ifdefined\beginlocalcontrol

\startbuffer[c][option=TEX]
\def\whatever#1#2%
  {\beginlocalcontrol
   \MyDimenA#1\relax
   \MyDimenB#2\relax
   \endlocalcontrol
   \ifdim\MyDimenA=\MyDimenB0\else1\fi}
\stopbuffer

\typebuffer[c][option=TEX] \getbuffer[c,b]

\fi

It is kind of a game to come up with alternatives but for sure those involve
dirty tricks and more tokens (and runtime). The next can be considered a dirty
trick too: we use a special variant of \type {\relax}. When a number is scanned
it acts as relax, but otherwise it just is ignored and disappears.

\ifdefined\norelax\else\let\norelax\relax\fi

\startbuffer[c][option=TEX]
\def\whatever#1#2%
  {\ifdim#1=#2\norelax0\else1\fi}
\stopbuffer

\typebuffer[c][option=TEX] \getbuffer[c,b]

\stopsectionlevel

\popoverloadmode

\stopdocument

% \def\foo{foo=bar}
% \def\oof{foo!bar}
% \scratchtoks{=}

% \ifhasxtoks\scratchtoks{foo!bar} YES\else NOP\fi\par
% \ifhasxtoks\scratchtoks{foo=bar} YES\else NOP\fi\par


%   \ifhaschar
%   \ifhastok
%   \ifhastoks
%   \ifhasxtoks

% \showluatokens\foo

% \ifhastoks\scratchtoks\oof YES\else NOP\fi\par
% \ifhastoks\scratchtoks\foo YES\else NOP\fi\par
