1
2
3\environment lowlevelstyle
4
5\startdocument
6 [title=conditionals,
7 color=middleblue]
8
9\pushoverloadmode
10
11\startsectionlevel[title=Preamble]
12
13\startsectionlevel[title=Introduction]
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82You seldom need the low level conditionals because there are quite some so called
83support macros available in \CONTEXT . For instance, when you want to compare two
84values (or more accurate: sequences of tokens), you can do this:
85
86\starttyping[option=TEX]
87\doifelse {foo} {bar} {
88 the same
89} {
90 different
91}
92\stoptyping
93
94But if you look in the \CONTEXT\ code, you will see that often we use primitives
95that start with \type {\if} in low level macros. There are good reasons for this.
96First of all, it looks familiar when you also code in other languages. Another
97reason is performance but that is only true in cases where the snippet of code is
98expanded very often, because \TEX\ is already pretty fast. Using low level \TEX\
99can also be more verbose, which is not always nice in a document source. But, the
100most important reason (for me) is the layout of the code. I often let the look
101and feel of code determine the kind of coding. This also relates to the syntax
102highlighting that I am using, which is consistent for \TEX, \METAPOST, \LUA,
103etc.\ and evolved over decades. If code looks bad, it probably is bad. Of course
104this doesnt mean all my code looks good; youre warned. In general we can say
105that I often use \type {\if...} when coding core macros, and \type {\doifelse...}
106macros in (document) styles and modules.
107
108In the sections below I will discuss the low level conditions in \TEX. For the
109often more convenient \CONTEXT\ wrappers you can consult the source of the system
110and support modules, the wiki andor manuals.
111
112Some of the primitives shown here are only available in \LUATEX, and some only in
113\LUAMETATEX . We could do without them for decades but they were added to these
114engines because of convenience and, more important, because then made for nicer
115code. Of course theres also the fun aspect. This manual is not an invitation to
116use these very low level primitives in your document source. The ones that
117probably make most sense are \type {\ifnum}, \type {\ifdim} and \type {\ifcase}.
118The others are often wrapped into support macros that are more convenient.
119
120\stopsectionlevel
121
122\startsectionlevel[title={Number and dimensions}]
123
124Numbers and dimensions are basic data types in \TEX. When you enter one, a number
125is just that but a dimension gets a unit. Compare:
126
127\starttyping[option=TEX]
1281234
1291234pt
130\stoptyping
131
132If you also use \METAPOST, you need to be aware of the fact that in that language
133there are not really dimensions. The \type {post} part of the name implies that
134eventually a number becomes a \POSTSCRIPT\ unit which represents a base point (\type
135{bp}) in \TEX. When in \METAPOST\ you entry \type {1234pt} you actually multiply
136\type {1234} by the variable \type {pt}. In \TEX\ on the other hand, a unit like
137\type {pt} is one of the keywords that gets parsed. Internally dimensions are
138also numbers and the unit (keyword) tells the scanner what multiplier to use.
139When that multiplier is one, were talking of scaled points, with the unit \type
140{sp}.
141
142\startbuffer
143\the\dimexpr 12.34pt \relax
144\the\dimexpr 12.34sp \relax
145\the\dimexpr 12.99sp \relax
146\the\dimexpr 1234sp \relax
147\the\numexpr 1234 \relax
148\stopbuffer
149
150\typebuffer[option=TEX]
151
152\startlines \getbuffer \stoplines
153
154When we serialize a dimension it always shows the dimension in points, unless we
155serialize it as number.
156
157\startbuffer
158\scratchdimen1234sp
159\number\scratchdimen
160\the\scratchdimen
161\stopbuffer
162
163\typebuffer[option=TEX]
164
165\startlines \getbuffer \stoplines
166
167When a number is scanned, the first thing that is taken care of is the sign. In many
168cases, when \TEX\ scans for something specific it will ignore spaces. It will
169happily accept multiple signs:
170
171\startbuffer
172\number 123
173\number 123
174\number 123
175\number 123
176\number 123
177\number 123
178\stopbuffer
179
180\typebuffer[option=TEX]
181
182\startlines \getbuffer \stoplines
183
184Watch how the negation accumulates. The scanner can handle decimal, hexadecimal
185and octal numbers:
186
187\startbuffer
188\number 123
189\number "123
190\number 123
191\stopbuffer
192
193\typebuffer[option=TEX]
194
195\startlines \getbuffer \stoplines
196
197A dimension is scanned like a number but this time the scanner checks for upto
198three parts: an either or not signed number, a period and a fraction. Here no
199number means zero, so the next is valid:
200
201\startbuffer
202\the\dimexpr . pt \relax
203\the\dimexpr 1. pt \relax
204\the\dimexpr .1pt \relax
205\the\dimexpr 1.1pt \relax
206\stopbuffer
207
208\typebuffer[option=TEX]
209
210\startlines \getbuffer \stoplines
211
212Again we can use hexadecimal and octal numbers but when these are entered, there
213can be no fractional part.
214
215\startbuffer
216\the\dimexpr 16 pt \relax
217\the\dimexpr "10 pt \relax
218\the\dimexpr 20 pt \relax
219\stopbuffer
220
221\typebuffer[option=TEX]
222
223\startlines \getbuffer \stoplines
224
225The reason for discussing numbers and dimensions here is that there are cases where
226when \TEX\ expects a number it will also accept a dimension. It is good to know that
227for instance a macro defined with \type {\chardef} or \type {\mathchardef} also is
228treated as a number. Even normal characters can be numbers, when prefixed by a \type
229{} (backtick).
230
231The maximum number in \TEX\ is 2147483647 so we can do this:
232
233\starttyping[option=TEX]
234\scratchcounter2147483647
235\stoptyping
236
237but not this
238
239\starttyping[option=TEX]
240\scratchcounter2147483648
241\stoptyping
242
243as it will trigger an error. A dimension can be positive and negative so there we
244can do at most:
245
246\starttyping[option=TEX]
247\scratchdimen 1073741823sp
248\stoptyping
249
250\startbuffer
251\scratchdimen1073741823sp
252\number\scratchdimen
253\the\scratchdimen
254\scratchdimen16383.99998pt
255\number\scratchdimen
256\the\scratchdimen
257\stopbuffer
258
259\typebuffer[option=TEX]
260
261\startlines
262\getbuffer
263\stoplines
264
265We can also do this:
266
267\startbuffer
268\scratchdimen16383.99999pt
269\number\scratchdimen
270\the\scratchdimen
271\stopbuffer
272
273\typebuffer[option=TEX]
274
275\startlines
276\getbuffer
277\stoplines
278
279but the next one will fail:
280
281\starttyping[option=TEX]
282\scratchdimen16383.9999999pt
283\stoptyping
284
285Just keep in mind that \TEX\ scans both parts as number so the error comes from
286checking if those numbers combine well.
287
288\startbuffer
289\ifdim 16383.99999 pt = 16383.99998 pt the same \else different \fi
290\ifdim 16383.999979 pt = 16383.999980 pt the same \else different \fi
291\ifdim 16383.999987 pt = 16383.999991 pt the same \else different \fi
292\stopbuffer
293
294\typebuffer[option=TEX]
295
296Watch the difference in dividing, the \type {} rounds, while the \type {:}
297truncates.
298
299\startlines
300\getbuffer
301\stoplines
302
303You need to be aware of border cases, although in practice they never really
304are a problem:
305
306\startbuffer
307\ifdim \dimexpr16383.99997 pt2\relax = \dimexpr 16383.99998 pt2\relax
308 the same \else different
309\fi
310\ifdim \dimexpr16383.99997 pt:2\relax = \dimexpr 16383.99998 pt:2\relax
311 the same \else different
312\fi
313\stopbuffer
314
315\typebuffer[option=TEX]
316
317\startlines
318\getbuffer
319\stoplines
320
321\startbuffer
322\ifdim \dimexpr1.99997 pt2\relax = \dimexpr 1.99998 pt2\relax
323 the same \else different
324\fi
325\ifdim \dimexpr1.99997 pt:2\relax = \dimexpr 1.99998 pt:2\relax
326 the same \else different
327\fi
328\stopbuffer
329
330\typebuffer[option=TEX]
331
332\startlines
333\getbuffer
334\stoplines
335
336\startbuffer
337\ifdim \dimexpr1.999999 pt2\relax = \dimexpr 1.9999995 pt2\relax
338 the same \else different
339\fi
340\ifdim \dimexpr1.999999 pt:2\relax = \dimexpr 1.9999995 pt:2\relax
341 the same \else different
342\fi
343\stopbuffer
344
345\typebuffer[option=TEX]
346
347\startlines
348\getbuffer
349\stoplines
350
351This last case demonstrates that at some point the digits get dropped (still
352assuming that the fraction is within the maximum permitted) so these numbers then
353are the same. Anyway, this is not different in other programming languages and
354just something you need to be aware of.
355
356\stopsectionlevel
357
358\stopsectionlevel
359
360\startsectionlevel[title={\TEX\ primitives}]
361
362\startsectionlevel[title={\tex{if}}]
363
364I seldom use this one. Internally \TEX\ stores (and thinks) in terms of tokens.
365If you see for instance \type {\def} or \type {\dimen} or \type {\hbox} these all
366become tokens. But characters like \type {A} or {@} also become tokens. In this
367test primitive all noncharacters are considered to be the same. In the next
368examples this is demonstrated.
369
370\startbuffer
371[\if AB yes\else nop\fi]
372[\if AA yes\else nop\fi]
373[\if CDyes\else nop\fi]
374[\if CCyes\else nop\fi]
375[\if\dimen\font yes\else nop\fi]
376[\if\dimen\font yes\else nop\fi]
377\stopbuffer
378
379\typebuffer[option=TEX]
380
381Watch how spaces after the two characters are kept: \inlinebuffer . This primitive looks
382at the next two tokens but when doing so it expands. Just look at the following:
383
384\startbuffer
385\def\AA{AA}
386\def\AB{AB}
387[\if\AA yes\else nop\fi]
388[\if\AB yes\else nop\fi]
389\stopbuffer
390
391\typebuffer[option=TEX]
392
393We get: \inlinebuffer .
394
395
396
397\stopsectionlevel
398
399\startsectionlevel[title={\tex{ifcat}}]
400
401In \TEX\ characters (in the input) get interpreted according to their so called
402catcodes. The most common are letters (alphabetic) and and other (symbols) but
403for instance the backslash has the property that it starts a command, the dollar
404signs trigger math mode, while the curly braced deal with grouping. If for
405instance either or not the ampersand is special (for instance as column separator
406in tables) depends on the macro package.
407
408\startbuffer
409[\ifcat AB yes\else nop\fi]
410[\ifcat AA yes\else nop\fi]
411[\ifcat CDyes\else nop\fi]
412[\ifcat CCyes\else nop\fi]
413[\ifcat C1yes\else nop\fi]
414[\ifcat\dimen\font yes\else nop\fi]
415[\ifcat\dimen\font yes\else nop\fi]
416\stopbuffer
417
418\typebuffer[option=TEX]
419
420This time we also compare a letter with a number: \inlinebuffer . In that case
421the category codes differ (letter vs other) but in this test comparing the
422letters result in a match. This is a test that is used only once in \CONTEXT\ and
423even that occasion is dubious and will go away.
424
425You can use \type {\noexpand} to prevent expansion:
426
427\startbuffer
428\def\A{A}
429\let\B B
430\def\C{D}
431\let\D D
432[\ifcat\noexpand\A Ayes\else nop\fi]
433[\ifcat\noexpand\B Byes\else nop\fi]
434[\ifcat\noexpand\C Cyes\else nop\fi]
435[\ifcat\noexpand\C Dyes\else nop\fi]
436[\ifcat\noexpand\D Dyes\else nop\fi]
437\stopbuffer
438
439\typebuffer[option=TEX]
440
441We get: \inlinebuffer, so who still thinks that \TEX\ is easy to understand for a
442novice user?
443
444\stopsectionlevel
445
446\startsectionlevel[title={\tex{ifnum}}]
447
448This condition compares its argument with another one, separated by an \type {<},
449\type {=} or \type {>} character.
450
451\starttyping[option=TEX]
452\ifnum\scratchcounter<0
453 less than
454\else\ifnum\scratchcounter>0
455 more than
456\else
457 equal to
458\fi zero
459\stoptyping
460
461This is one of these situations where a dimension can be used instead. In that
462case the dimension is in scaled points.
463
464\starttyping[option=TEX]
465\ifnum\scratchdimen<0
466 less than
467\else\ifnum\scratchdimen>0
468 more than
469\else
470 equal to
471\fi zero
472\stoptyping
473
474Of course this equal treatment of a dimension and number is only true when the
475dimension is a register or box property.
476
477\stopsectionlevel
478
479\startsectionlevel[title={\tex{ifdim}}]
480
481This condition compares one dimension with another one, separated by an \type {<},
482\type {=} or \type {>} sign.
483
484\starttyping[option=TEX]
485\ifdim\scratchdimen<0pt
486 less than
487\else\ifdim\scratchdimen>0pt
488 more than
489\else
490 equal to
491\fi zero
492\stoptyping
493
494While when comparing numbers a dimension is a valid quantity but here you cannot
495mix them: something with a unit is expected.
496
497\stopsectionlevel
498
499\startsectionlevel[title={\tex{ifodd}}]
500
501This one can come in handy, although in \CONTEXT\ it is only used in checking for
502an odd of even page number.
503
504\startbuffer
505\scratchdimen 3sp
506\scratchcounter4
507
508\ifodd\scratchdimen very \else not so \fi odd
509\ifodd\scratchcounter very \else not so \fi odd
510\stopbuffer
511
512\typebuffer[option=TEX]
513
514As with the previously discussed \type {\ifnum} you can use a dimension variable
515too, which is then interpreted as representing scaled points. Here we get:
516
517\startlines
518\getbuffer
519\stoplines
520
521\stopsectionlevel
522
523\startsectionlevel[title={\tex{ifvmode}}]
524
525This is a rather trivial check. It takes no arguments and just is true when were
526in vertical mode. Here is an example:
527
528\startbuffer
529\hbox{\ifvmode\else\par\fi\ifvmode v\else h\fi mode}
530\stopbuffer
531
532\typebuffer[option=TEX]
533
534Were always in horizontal mode and issuing a \type {\par} inside a horizontal
535box doesnt change that, so we get: \ruledhbox{\inlinebuffer}.
536
537\stopsectionlevel
538
539\startsectionlevel[title={\tex{ifhmode}}]
540
541As with \type {\ifvmode} this one has no argument and just tells if were in
542vertical mode.
543
544\startbuffer
545\vbox {
546 \noindent \ifhmode h\else v\fi mode
547 \par
548 \ifhmode h\else \noindent v\fi mode
549}
550\stopbuffer
551
552\typebuffer[option=TEX]
553
554You can use it for instance to trigger injection of code, or prevent that some
555content (or command) is done more than once:
556
557\startlinecorrection
558\ruledhbox{\inlinebuffer}
559\stoplinecorrection
560
561\stopsectionlevel
562
563\startsectionlevel[title={\tex{ifmmode}}]
564
565Math is something very \TEX\ so naturally you can check if youre in math mode.
566here is an example of using this test:
567
568\starttyping[option=TEX]
569\def\enforcemath#1{\ifmmode#1\else$ #1 $\fi}
570\stoptyping
571
572Of course in reality macros that do such things are more advanced than this one.
573
574\stopsectionlevel
575
576\startsectionlevel[title={\tex{ifinner}}]
577
578\startbuffer
579\def\ShowMode
580 {\ifhmode \ifinner inner \fi hmode
581 \else\ifvmode \ifinner inner \fi vmode
582 \else\ifmmode \ifinner inner \fi mmode
583 \else \ifinner inner \fi unset
584 \fi\fi\fi}
585\stopbuffer
586
587\typebuffer[option=TEX] \getbuffer
588
589\startbuffer
590\ShowMode \ShowMode
591
592\vbox{\ShowMode}
593
594\hbox{\ShowMode}
595
596$\ShowMode$
597
598$$\ShowMode$$
599\stopbuffer
600
601\typebuffer[option=TEX]
602
603The first line has two tests, where the first one changes the mode to horizontal
604simply because a text has been typeset. Watch how display math is not inner.
605
606\startpacked
607\startlines
608\getbuffer
609\stoplines
610\stoppacked
611
612By the way, moving the \type {\ifinner} test outside the branches (to the top of
613the macro) wont work because once the word \type {inner} is typeset were no
614longer in vertical mode, if we were at all.
615
616\stopsectionlevel
617
618\startsectionlevel[title={\tex{ifvoid}}]
619
620A box is one of the basic concepts in \TEX. In order to understand this primitive
621we present four cases:
622
623\startbuffer
624\setbox0\hbox{} \ifvoid0 void \else content \fi
625\setbox0\hbox{123} \ifvoid0 void \else content \fi
626\setbox0\hbox{} \box0 \ifvoid0 void \else content \fi
627\setbox0\hbox to 10pt{} \ifvoid0 void \else content \fi
628\stopbuffer
629
630\typebuffer[option=TEX]
631
632In the first case, we have a box which is empty but its not void. It helps to
633know that internally an hbox is actually an object with a pointer to a linked
634list of nodes. So, the first two can be seen as:
635
636\starttyping
637hlist > [nothing]
638hlist > 1 > 2 > 3 > [nothing]
639\stoptyping
640
641but in any case there is a hlist. The third case puts something in a hlist but
642then flushes it. Now we have not even the hlist any more; the box register has
643become void. The last case is a variant on the first. It is an empty box with a
644given width. The outcome of the four lines (with a box flushed in between) is:
645
646\startlines
647\getbuffer
648\stoplines
649
650So, when you want to test if a box is really empty, you need to test also its
651dimensions, which can be up to three tests, depending on your needs.
652
653\startbuffer
654\setbox0\emptybox \ifvoid0 void\else content\fi
655\setbox0\emptybox \wd0=10pt \ifvoid0 void\else content\fi
656\setbox0\hbox to 10pt {} \ifvoid0 void\else content\fi
657\setbox0\hbox {} \wd0=10pt \ifvoid0 void\else content\fi
658\stopbuffer
659
660\typebuffer[option=TEX]
661
662Setting a dimension of a void (empty) box doesnt make it less void:
663
664\startlines
665\getbuffer
666\stoplines
667
668\stopsectionlevel
669
670\startsectionlevel[title={\tex{ifhbox}}]
671
672This test takes a box number and gives true when it is an hbox.
673
674\stopsectionlevel
675
676\startsectionlevel[title={\tex{ifvbox}}]
677
678This test takes a box number and gives true when it is an vbox. Both a \type
679{\vbox} and \type {\vtop} are vboxes, the difference is in the height and depth
680and the baseline. In a \type {\vbox} the last line determines the baseline
681
682\startlinecorrection
683\ruledvbox{vbox or vtop\par vtop or vbox}
684\stoplinecorrection
685
686And in a \type {\vtop} the first line takes control:
687
688\startlinecorrection
689\ruledvtop{vbox or vtop\par vtop or vbox}
690\stoplinecorrection
691
692but, once wrapped, both internally are just vlists.
693
694\stopsectionlevel
695
696\startsectionlevel[title={\tex{ifx}}]
697
698This test is actually used a lot in \CONTEXT: it compares two token(list)s:
699
700\startbuffer
701 \ifx a b Y\else N\fi
702 \ifx ab Y\else N\fi
703\def\A {a}\def\B{b}\ifx \A\B Y\else N\fi
704\def\A{aa}\def\B{a}\ifx \A\B Y\else N\fi
705\def\A {a}\def\B{a}\ifx \A\B Y\else N\fi
706\stopbuffer
707
708\typebuffer[option=TEX]
709
710Here the result is: \quotation{\inlinebuffer}. It does not expand the content, if
711you want that you need to use an \type {\edef} to create two (temporary) macros
712that get compared, like in:
713
714\starttyping[option=TEX]
715\edef\TempA{...}\edef\TempB{...}\ifx\TempA\TempB ...\else ...\fi
716\stoptyping
717
718\stopsectionlevel
719
720\startsectionlevel[title={\tex{ifeof}}]
721
722This test checks if a the pointer in a given input channel has reached its end.
723It is also true when the file is not present. The argument is a number which
724relates to the \type {\openin} primitive that is used to open files for reading.
725
726\stopsectionlevel
727
728\startsectionlevel[title={\tex{iftrue}}]
729
730It does what it says: always true.
731
732\stopsectionlevel
733
734\startsectionlevel[title={\tex{iffalse}}]
735
736It does what it says: always false.
737
738\stopsectionlevel
739
740\startsectionlevel[title={\tex{ifcase}}]
741
742The general layout of an \type {\ifcase} tests is as follows:
743
744\starttyping[option=TEX]
745\ifcase<number>
746 when zero
747\or
748 when one
749\or
750 when two
751\or
752 ...
753\else
754 when something else
755\fi
756\stoptyping
757
758As in other places a number is a sequence of signs followed by one of more digits
759
760\stopsectionlevel
761
762\stopsectionlevel
763
764\startsectionlevel[title={\ETEX\ primitives}]
765
766\startsectionlevel[title={\tex{ifdefined}}]
767
768This primitive was introduced for checking the existence of a macro (or primitive)
769and with good reason. Say that you want to know if \type {\MyMacro} is defined? One
770way to do that is:
771
772\startbuffer
773\ifx\MyMacro\undefined
774 {\bf undefined indeed}
775\fi
776\stopbuffer
777
778\typebuffer[option=TEX]
779
780This results in: \inlinebuffer , but is this macro really undefined? When \TEX\
781scans your source and sees a the escape character (the forward slash) it will
782grab the next characters and construct a control sequence from it. Then it finds
783out that there is nothing with that name and it will create a hash entry for a
784macro with that name but with no meaning. Because \type {\undefined} is also not
785defined, these two macros have the same meaning and therefore the \type {\ifx} is
786true. Imagine that you do this many times, with different macro names, then your
787hash can fill up. Also, when a user defined \type {\undefined} youre suddenly
788get a different outcome.
789
790In order to catch the last problem there is the option to test directly:
791
792\startbuffer
793\ifdefined\MyOtherMacro \else
794 {\bf also undefined}
795\fi
796\stopbuffer
797
798\typebuffer[option=TEX]
799
800This (or course) results in: \inlinebuffer, but the macro is still sort of
801defined (with no meaning). The next section shows how to get around this.
802
803\stopsectionlevel
804
805\startsectionlevel[title={\tex{ifcsname}}]
806
807A macro is often defined using a ready made name, as in:
808
809\starttyping[option=TEX]
810\def\OhYes{yes}
811\stoptyping
812
813The name is made from characters with catcode letter which means that you cannot
814use for instance digits or underscores unless you also give these characters that
815catcode, which is not that handy in a document. You can however use \type
816{\csname} to define a control sequence with any character in the name, like:
817
818\starttyping[option=TEX]
819\expandafter\def\csname Oh Yes : 1\endcsname{yes}
820\stoptyping
821
822Later on you can get this one with \type {\csname}:
823
824\starttyping[option=TEX]
825\csname Oh Yes : 1\endcsname
826\stoptyping
827
828However, if you say:
829
830\starttyping[option=TEX]
831\csname Oh Yes : 2\endcsname
832\stoptyping
833
834you wont get some result, nor a message about an undefined control sequence, but
835the name triggers a define anyway, this time not with no meaning (undefined) but
836as equivalent to \type {\relax}, which is why
837
838\starttyping[option=TEX]
839\expandafter\ifx\csname Oh Yes : 2\endcsname\relax
840 {\bf relaxed indeed}
841\fi
842\stoptyping
843
844is the way to test its existence. As with the test in the previous section,
845this can deplete the hash when you do lots of such tests. The way out of this
846is:
847
848\starttyping[option=TEX]
849\ifcsname Oh Yes : 2\endcsname \else
850 {\bf unknown indeed}
851\fi
852\stoptyping
853
854This time there is no hash entry created and therefore there is not even an
855undefined control sequence.
856
857In \LUATEX\ there is an option to return false in case of a messy expansion
858during this test, and in \LUAMETATEX\ that is default. This means that tests can
859be made quite robust as it is pretty safe to assume that names that make sense
860are constructed from regular characters and not boxes, font switches, etc.
861
862\stopsectionlevel
863
864\startsectionlevel[title={\tex{iffontchar}}]
865
866This test was also part of the \ETEX\ extensions and it can be used to see if
867a font has a character.
868
869\startbuffer
870\iffontchar\fontA
871 {\em This font has an A!}
872\fi
873\stopbuffer
874
875\typebuffer[option=TEX]
876
877And, as expected, the outcome is: \quotation {\inlinebuffer}. The test takes two
878arguments, the first being a font identifier and the second a character number,
879so the next checks are all valid:
880
881\starttyping[option=TEX]
882\iffontchar\font A yes\else nop\fi\par
883\iffontchar\nullfont A yes\else nop\fi\par
884\iffontchar\textfont0A yes\else nop\fi\par
885\stoptyping
886
887In the perspective of \LUAMETATEX\ I considered also supporting \type {\fontid}
888but it got a bit messy due to the fact that this primitive expands in a different
889way so this extension was rejected.
890
891\stopsectionlevel
892
893\startsectionlevel[title={\tex{unless}}]
894
895You can negate the results of a test by using the \type {\unless} prefix, so for
896instance you can replace:
897
898\starttyping[option=TEX]
899\ifdim\scratchdimen=10pt
900 \dosomething
901\else\ifdim\scratchdimen<10pt
902 \dosomething
903\fi\fi
904\stoptyping
905
906by:
907
908\starttyping[option=TEX]
909\unless\ifdim\scratchdimen>10pt
910 \dosomething
911\fi
912\stoptyping
913
914An \type {\unless} makes little sense when used with \type {\ifcase} but contrary
915to the other engines we dont error or it; we just give a warning. Some
916conditionals internally use a case so there we can actually provide a variant:
917
918\startbuffer
919\ifcase 1 \relax zero \or one \or two \else else \fi = one \par
920\ifcase 2 \relax zero \or one \or two \else else \fi = two \par
921
922\unless\ifcase 1 \relax zero \or one \or two \else else \fi
923\unless\ifcase 2 \relax zero \or one \or two \else else \fi
924
925\ifchkdim1pt\or yes \else nop \fi = yes \par
926\ifchkdim2 \or nop \else yes \fi = yes \par
927
928\unless\ifchkdim1pt\or nop \else yes \fi = yes \par
929\unless\ifchkdim2 \or yes \else nop \fi = yes \par
930\stopbuffer
931
932\typebuffer[option=TEX]
933
934The \typ {\ifchkdim}, \typ {\ifchkdimension}, \typ {\ifchknum}, \typ
935{\ifchknumber} and \typ {\ifparameter} are supported.
936
937\startpacked \getbuffer \stoppacked
938
939\stopsectionlevel
940
941\stopsectionlevel
942
943\startsectionlevel[title={\LUATEX\ primitives}]
944
945\startsectionlevel[title={\tex{ifincsname}}]
946
947As it had no real practical usage uit might get dropped in \LUAMETATEX, so it
948will not be discussed here.
949
950\stopsectionlevel
951
952\startsectionlevel[title={\tex{ifprimitive}}]
953
954As it had no real practical usage due to limitations, this one is not available
955in \LUAMETATEX\ so it will not be discussed here. If really needed you can use \type
956{\ifflags}.
957
958\stopsectionlevel
959
960\startsectionlevel[title={\tex{ifabsnum}}]
961
962This test is inherited from \PDFTEX\ and behaves like \type {\ifnum} but first
963turns a negative number into a positive one.
964
965\stopsectionlevel
966
967\startsectionlevel[title={\tex{ifabsdim}}]
968
969This test is inherited from \PDFTEX\ and behaves like \type {\ifdim} but first
970turns a negative dimension into a positive one.
971
972\stopsectionlevel
973
974\startsectionlevel[title={\tex{ifcondition}}]
975
976This is not really a test but in order to unstand that you need to know how
977\TEX\ internally deals with tests.
978
979\starttyping[option=TEX]
980\ifdimen\scratchdimen>10pt
981 \ifdim\scratchdimen<20pt
982 result a
983 \else
984 result b
985 \fi
986\else
987 result c
988\fi
989\stoptyping
990
991When we end up in the branch of \quotation {result a} we need to skip two \type
992{\else} branches after were done. The \type {\if..} commands increment a level
993while the \type {\fi} decrements a level. The \type {\else} needs to be skipped
994here. In other cases the true branch needs to be skipped till we end up a the
995right \type {\else}. When doing this skipping, \TEX\ is not interested in what it
996encounters beyond these tokens and this skipping (therefore) goes real fast but
997it does see nested conditions and doesnt interpret grouping related tokens.
998
999A side effect of this is that the next is not working as expected:
1000
1001\starttyping[option=TEX]
1002\def\ifmorethan{\ifdim\scratchdimen>}
1003\def\iflessthan{\ifdim\scratchdimen<}
1004
1005\ifmorethan10pt
1006 \iflessthan20pt
1007 result a
1008 \else
1009 result b
1010 \fi
1011\else
1012 result c
1013\fi
1014\stoptyping
1015
1016The \type{\iflessthan} macro is not seen as an \type {\if...} so the nesting gets
1017messed up. The solution is to fool the scanner in thinking that it is. Say we have:
1018
1019\startbuffer
1020\scratchdimen=25pt
1021
1022\def\ifmorethan{\ifdim\scratchdimen>}
1023\def\iflessthan{\ifdim\scratchdimen<}
1024\stopbuffer
1025
1026\typebuffer[option=TEX] \getbuffer
1027
1028and:
1029
1030\startbuffer
1031\ifcondition\ifmorethan10pt
1032 \ifcondition\iflessthan20pt
1033 result a
1034 \else
1035 result b
1036 \fi
1037\else
1038 result c
1039\fi
1040\stopbuffer
1041
1042\typebuffer[option=TEX]
1043
1044When we expand this snippet we get: \quotation {\inlinebuffer} and no error
1045concerning a failure in locating the right \type {\fis}. So, when scanning the
1046\type {\ifcondition} is seen as a valid \type {\if...} but when the condition is
1047really expanded it gets ignored and the \type {\ifmorethan} has better come up
1048with a match or not.
1049
1050In this perspective it is also worth mentioning that nesting problems can be
1051avoided this way:
1052
1053\starttyping[option=TEX]
1054\def\WhenTrue {something \iftrue ...}
1055\def\WhenFalse{something \iffalse ...}
1056
1057\ifnum\scratchcounter>123
1058 \let\next\WhenTrue
1059\else
1060 \let\next\WhenFalse
1061\fi
1062\next
1063\stoptyping
1064
1065This trick is mentioned in The \TeX book and can also be found in the plain \TEX\
1066format. A variant is this:
1067
1068\starttyping[option=TEX]
1069\ifnum\scratchcounter>123
1070 \expandafter\WhenTrue
1071\else
1072 \expandafter\WhenFalse
1073\fi
1074\stoptyping
1075
1076but using \type {\expandafter} can be quite intimidating especially when there
1077are multiple in a row. It can also be confusing. Take this: an \type
1078{\ifcondition} expects the code that follows to produce a test. So:
1079
1080\starttyping[option=TEX]
1081\def\ifwhatever#1
1082 {\ifdim#1>10pt
1083 \expandafter\iftrue
1084 \else
1085 \expandafter\iffalse
1086 \fi}
1087
1088\ifcondition\ifwhatever{10pt}
1089 result a
1090\else
1091 result b
1092\fi
1093\stoptyping
1094
1095This will not work! The reason is in the already mentioned fact that when we end
1096up in the greater than \type {10pt} case, the scanner will happily push the \type
1097{\iftrue} after the \type {\fi}, which is okay, but when skipping over the \type
1098{\else} it sees a nested condition without matching \type {\fi}, which makes ity
1099fail. I will spare you a solution with lots of nasty tricks, so here is the clean
1100solution using \type {\ifcondition}:
1101
1102\starttyping[option=TEX]
1103\def\truecondition {\iftrue}
1104\def\falsecondition{\iffalse}
1105
1106\def\ifwhatever#1
1107 {\ifdim#1>10pt
1108 \expandafter\truecondition
1109 \else
1110 \expandafter\falsecondition
1111 \fi}
1112
1113\ifcondition\ifwhatever{10pt}
1114 result a
1115\else
1116 result b
1117\fi
1118\stoptyping
1119
1120It will be no surprise that the two macros at the top are predefined in \CONTEXT.
1121It might be more of a surprise that at the time of this writing the usage in
1122\CONTEXT\ of this \type {\ifcondition} primitive is rather minimal. But that
1123might change.
1124
1125As a further teaser Ill show another simple one,
1126
1127\startbuffer
1128\def\HowOdd#1{\unless\ifnum\numexpr ((#1):2)*2\relax=\numexpr#1\relax}
1129
1130\ifcondition\HowOdd{1}very \else not so \fi odd
1131\ifcondition\HowOdd{2}very \else not so \fi odd
1132\ifcondition\HowOdd{3}very \else not so \fi odd
1133\stopbuffer
1134
1135\typebuffer[option=TEX]
1136
1137This renders:
1138
1139\startlines
1140\getbuffer
1141\stoplines
1142
1143The code demonstrates several tricks. First of all we use \type {\numexpr} which
1144permits more complex arguments, like:
1145
1146\starttyping[option=TEX]
1147\ifcondition\HowOdd{41}very \else not so \fi odd
1148\ifcondition\HowOdd{2\scratchcounter9}very \else not so \fi odd
1149\stoptyping
1150
1151Another trick is that we use an integer division (the \type {:}) which is an
1152operator supported by \LUAMETATEX .
1153
1154\stopsectionlevel
1155
1156\stopsectionlevel
1157
1158\startsectionlevel[title={\LUAMETATEX\ primitives}]
1159
1160\startsectionlevel[title={\tex{ifnum} and \type {ifdim}}]
1161
1162These have been extended with a few more operators. For instance, we can use a
1163negation:
1164
1165\startbuffer
1166\ifnum 10 > 5 Y\else N\fi
1167\ifnum 10 !> 5 Y\else N\fi
1168\stopbuffer
1169
1170\typebuffer[option=TEX]
1171
1172Results in: \inlinebuffer. A bitwise comparison is possible too:
1173
1174\startbuffer
1175\ifnum "02 2 Y\else N\fi
1176\ifnum "02 4 Y\else N\fi
1177\ifnum "02 ! 8 Y\else N\fi
1178\stopbuffer
1179
1180\typebuffer[option=TEX]
1181
1182yields: \inlinebuffer. You can also use the \UNICODE\ variants \type {∈}, \type
1183{∉}, \type {≠}, \type {≤}, \type {≥}, \type {≰}, and \type {≱}.
1184
1185\stopsectionlevel
1186
1187\startsectionlevel[title={\tex{iffloat}}]
1188
1189This is a test for a float, much like a test for a dimen without unit.
1190
1191\stopsectionlevel
1192
1193\startsectionlevel[title={\tex{ifabsfloat}}]
1194
1195This is a test for a float, much like a test for a dimen without unit.
1196
1197\stopsectionlevel
1198
1199\startsectionlevel[title={\tex{ifintervalnum}}]
1200
1201This is a test for equality of two numbers within an interval, as in:
1202
1203\startbuffer
1204\ifintervalnum 1 2 1 Y\else N\fi
1205\ifintervalnum 1 3 1 Y\else N\fi
1206\ifintervalnum 100 102 1 Y\else N\fi
1207\ifintervalnum 100 102 3 Y\else N\fi
1208\stopbuffer
1209
1210\typebuffer[option=TEX]
1211
1212which results in: \inlinebuffer.
1213
1214\stopsectionlevel
1215
1216\startsectionlevel[title={\tex{ifintervaldim}}]
1217
1218This is a test for equality of two dimensions within an interval, as in:
1219
1220\startbuffer
1221\ifintervaldim 1pt 2pt 1pt Y\else N\fi
1222\ifintervaldim 1pt 3pt 1pt Y\else N\fi
1223\ifintervaldim 100pt 102pt 1pt Y\else N\fi
1224\ifintervaldim 100pt 102pt 3pt Y\else N\fi
1225\stopbuffer
1226
1227\typebuffer[option=TEX]
1228
1229We get: \inlinebuffer.
1230
1231\stopsectionlevel
1232
1233\startsectionlevel[title={\tex{ifintervalfloat}}]
1234
1235This is a test for a float, much like a test for a dimen without unit.
1236
1237\stopsectionlevel
1238
1239\startsectionlevel[title={\tex{ifdimexpression}}]
1240
1241This is a boolean checker so the comparison is done as part of the expression, as in:
1242
1243\starttyping[option=TEX]
1244\ifdimexpression{10pt > (4pt 8pt)}Y\else N\fi
1245\stoptyping
1246
1247\stopsectionlevel
1248
1249\startsectionlevel[title={\tex{ifnumexpression}}]
1250
1251This is a boolean checker so the comparison is done as part fo the expression, as in:
1252
1253\starttyping[option=TEX]
1254\ifnumexpression{10 > (4 8)}Y\else N\fi
1255\stoptyping
1256
1257\stopsectionlevel
1258
1259\startsectionlevel[title={\tex{ifcmpnum}}]
1260
1261This one is part of s set of three tests that all are a variant of a \type
1262{\ifcase} test. A simple example of the first test is this:
1263
1264\starttyping[option=TEX]
1265\ifcmpnum 123 345 less \or equal \else more \fi
1266\stoptyping
1267
1268The test scans for two numbers, which of course can be registers or expressions,
1269and sets the case value to 0, 1 or 2, which means that you then use the normal
1270\type {\or} and \type {\else} primitives for follow up on the test.
1271
1272\stopsectionlevel
1273
1274\startsectionlevel[title={\tex{ifchknum}}]
1275
1276This test scans a number and when its okay sets the case value to 1, and otherwise
1277to 2. So you can do the next:
1278
1279\starttyping[option=TEX]
1280\ifchknum 123\or good \else bad \fi
1281\ifchknum bad\or good \else bad \fi
1282\stoptyping
1283
1284An error message is suppressed and the first \type {\or} can be seen as a sort of
1285recovery token, although in fact we just use the fast scanner mode that comes
1286with the \type {\ifcase}: because the result is 1 or 2, we never see invalid
1287tokens.
1288
1289In order to avoid another scan the a valid result it is made available in \type
1290{\lastchknumber}.
1291
1292\stopsectionlevel
1293
1294\startsectionlevel[title={\tex{ifchknumber}}]
1295
1296This one is a more rigorous variant of \type {\ifchknum} and doesnt like
1297trailing non numeric crap.
1298
1299\stopsectionlevel
1300
1301\startsectionlevel[title={\tex{ifchknumexpr}}]
1302
1303This test goes a bit further and accepts an expression.
1304
1305\starttyping[option=TEX]
1306\ifchknumexpr 123 45\or good \else bad \fi
1307\stoptyping
1308
1309As with the other checkers, if there is a valid result it is available in \type
1310{\lastchknumber}.
1311
1312\stopsectionlevel
1313
1314\startsectionlevel[title={\tex{ifnumval}}]
1315
1316A sort of combination of the previous two is \type {\ifnumval} which checks a
1317number but also if its less, equal or more than zero:
1318
1319\starttyping[option=TEX]
1320\ifnumval 123\or less \or equal \or more \else error \fi
1321\ifnumval bad\or less \or equal \or more \else error \fi
1322\stoptyping
1323
1324You can decide to ignore the bad number or do something that makes more sense.
1325Often the to be checked value will be the content of a macro or an argument like
1326\type {#1}.
1327
1328\stopsectionlevel
1329
1330\startsectionlevel[title={\tex{ifcmpdim}}]
1331
1332This test is like \type {\ifcmpnum} but for dimensions.
1333
1334\stopsectionlevel
1335
1336\startsectionlevel[title={\tex{ifchkdim}}]
1337
1338This test is like \type {\ifchknum} but for dimensions. The last checked value is
1339available as \type {\lastchknumber}.
1340
1341\stopsectionlevel
1342
1343\startsectionlevel[title={\tex{ifchkdimension}}]
1344
1345This one is a more rigorous variant of \type {\ifchkdim} and doesnt like
1346trailing rubish.
1347
1348\stopsectionlevel
1349
1350\startsectionlevel[title={\tex{ifchkdimexpr}}]
1351
1352This test is like \type {\ifchknumexpr} but for dimensions. The last checked value is
1353available as \type {\lastchkdimension}.
1354
1355\stopsectionlevel
1356
1357\startsectionlevel[title={\tex{ifdimval}}]
1358
1359This test is like \type {\ifnumval} but for dimensions. The last checked value is
1360available as \type {\lastchkdim}
1361
1362\stopsectionlevel
1363
1364\startsectionlevel[title={\tex{iftok}}]
1365
1366Although this test is still experimental it can be used. What happens is that
1367two to be compared \quote {things} get scanned for. For each we first gobble
1368spaces and \type {\relax} tokens. Then we can have several cases:
1369
1370\startitemize[n,packed]
1371 \startitem
1372 When we see a left brace, a list of tokens is scanned upto the
1373 matching right brace.
1374 \stopitem
1375 \startitem
1376 When a reference to a token register is seen, that register is taken as
1377 value.
1378 \stopitem
1379 \startitem
1380 When a reference to an internal token register is seen, that register is
1381 taken as value.
1382 \stopitem
1383 \startitem
1384 When a macro is seen, its definition becomes the to be compared value.
1385 \stopitem
1386 \startitem
1387 When a number is seen, the value of the corresponding register is taken
1388 \stopitem
1389\stopitemize
1390
1391An example of the first case is:
1392
1393\starttyping[option=TEX]
1394\iftok {abc} {def}
1395 ...
1396\else
1397 ...
1398\fi
1399\stoptyping
1400
1401The second case goes like this:
1402
1403\starttyping[option=TEX]
1404\iftok\scratchtoksone\scratchtokstwo
1405 ...
1406\else
1407 ...
1408\fi
1409\stoptyping
1410
1411Case one and four mixed:
1412
1413\starttyping[option=TEX]
1414\iftok{123}\TempX
1415 ...
1416\else
1417 ...
1418\fi
1419\stoptyping
1420
1421The last case is more a catch: it will issue an error when no number is given.
1422Eventually that might become a bit more clever (depending on our needs.)
1423
1424\stopsectionlevel
1425
1426\startsectionlevel[title={\tex{ifzeronum}, \tex{ifzerodim}, \tex{ifzerofloat}}]
1427
1428The names of these three tells what they do: checking for a zero value.
1429
1430\startbuffer
1431(\ifzerodim 10pt\norelax A\orelse\ifzerodim 0pt\norelax B\else C\fi)
1432(\ifzeronum 10 \norelax A\orelse\ifzeronum 0 \norelax B\else C\fi)
1433(\ifzerofloat 10.0\norelax A\orelse\ifzerofloat 0.0\norelax B\else C\fi)
1434\stopbuffer
1435
1436\typebuffer[option=TEX]
1437
1438Here we use the \type {\norelax} to get rid of trailing spaces: \inlinebuffer.
1439
1440\stopsectionlevel
1441
1442\startsectionlevel[title={\tex{ifhaschar}, \tex{ifhastok}, \tex{ifhastoks},\tex{ifhasxtoks}}]
1443
1444These checkers can be used to identify a (sequence) of token(s) in a given token
1445list. Their working can best be shown with a few examples:
1446
1447\startbuffer
1448\ifhaschar c {abcd}Y\else N\fi
1449\ifhastok c {abcd}Y\else N\fi
1450\ifhastoks {c}{abcd}Y\else N\fi
1451\ifhasxtoks {c}{abcd}Y\else N\fi
1452
1453\def\abcd{abcd}
1454
1455\ifhaschar c {\abcd}Y\else N\fi
1456\ifhastok c {\abcd}Y\else N\fi
1457\ifhastoks {c}{\abcd}Y\else N\fi
1458\ifhasxtoks {c}{\abcd}Y\else N\fi
1459
1460\ifhaschar c {a{bc}d}Y\else N\fi
1461\ifhastok c {a{bc}d}Y\else N\fi
1462\ifhastoks {c}{a{bc}d}Y\else N\fi
1463\ifhasxtoks {c}{a{bc}d}Y\else N\fi
1464
1465\def\abcd{a{bc}d}
1466
1467\ifhaschar c {\abcd}Y\else N\fi
1468\ifhastok c {\abcd}Y\else N\fi
1469\ifhastoks {c}{\abcd}Y\else N\fi
1470\ifhasxtoks {c}{\abcd}Y\else N\fi
1471\stopbuffer
1472
1473\typebuffer[option=TEX] \getbuffer
1474
1475The \type {\ifhaschar} test will not descend into a braced sublist. The \type {x}
1476variants expand the list before comparison.
1477
1478\stopsectionlevel
1479
1480\startsectionlevel[title={\tex{ifcstok}}]
1481
1482There is a subtle difference between this one and \type {\iftok}: spaces
1483and \type {\relax} tokens are skipped but nothing gets expanded. So, when
1484we arrive at the to be compared \quote {things} we look at what is there,
1485asis.
1486
1487\stopsectionlevel
1488
1489\startsectionlevel[title={\tex{iffrozen}}]
1490
1491{\em This is an experimental test.} Commands can be defined with the \type
1492{\frozen} prefix and this test can be used to check if that has been the case.
1493
1494\stopsectionlevel
1495
1496\startsectionlevel[title={\tex{ifprotected}}]
1497
1498Commands can be defined with the \type {\protected} prefix (or in \CONTEXT, for
1499historic reasons, with \type {\unexpanded}) and this test can be used to check if
1500that has been the case.
1501
1502\stopsectionlevel
1503
1504\startsectionlevel[title={\tex{ifarguments}}]
1505
1506This conditional can be used to check how many arguments were matched. It only
1507makes sense when used with macros defined with the \type {\tolerant} prefix
1508andor when the sentinel \type {\ignorearguments} after the arguments is used.
1509More details can be found in the lowlevel macros manual.
1510
1511\stopsectionlevel
1512
1513\startsectionlevel[title={\tex{ifrelax}}]
1514
1515\startbuffer
1516 \ifrelax\relax Y\else N\fi
1517 \ifrelax\norelax Y\else N\fi
1518\expandafter\ifrelax\csname ReLaX\endcsname Y\else N\fi
1519\stopbuffer
1520
1521The following tests all return the same: \inlinebuffer; it is a shortcut for
1522\type {\ifx ... \relax} that looks nicer in code.
1523
1524\typebuffer[option=TEX]
1525
1526\stopsectionlevel
1527
1528\startsectionlevel[title={\tex{ifempty}}]
1529
1530\startbuffer
1531\ifempty{} Y\else N\fi
1532\ifempty{!} Y\else N\fi
1533\ifempty\empty Y\else N\fi
1534\stopbuffer
1535
1536This is again a shortcut, this time for \type {\ifx ...\empty} assuming that
1537\type {\empty} is defined as being nothing. Instead of a token you can also pass
1538a list, so here we get \inlinebuffer.
1539
1540\typebuffer[option=TEX]
1541
1542\stopsectionlevel
1543
1544\startsectionlevel[title={\tex{iflastnamedcs}}]
1545
1546This test is part of the \type {\csname} repertoire and uses the last valid
1547result from such a command.
1548
1549\startbuffer
1550\def\Hello{upper}
1551\def\hello{lower}
1552\ifcsname Hello\endcsname
1553 \iflastnamedcs\hello
1554 world
1555 \orelse\iflastnamedcs\Hello
1556 World
1557 \fi
1558\fi
1559\stopbuffer
1560
1561\typebuffer[option=TEX]
1562
1563Here the \quote {Hello} test result in \quote {\nospacing\inlinebuffer}. It is an
1564example of a follow up test, most likely used in user interfacing.
1565
1566\stopsectionlevel
1567
1568\startsectionlevel[title={\tex{ifboolean}}]
1569
1570Another new one is the following: it tests a number for being zero or not. As
1571with any primitive that scans for a number, it accepts a braced expression too.
1572
1573\startbuffer
1574(\ifboolean 0 T\else F\fi)
1575(\ifboolean 1 T\else F\fi)
1576(\ifboolean {(2 * 4) < 5} T\else F\fi)
1577(\ifboolean \dimexpression{(1em > 20pt) or (1ex > 15pt)} T\else F\fi)
1578(\ifboolean \dimexpression{(1em > 3pt) and (1ex < 3pt)} T\else F\fi)
1579\stopbuffer
1580
1581\typebuffer[option=TEX]
1582
1583We get: \inlinebuffer.
1584
1585\stopsectionlevel
1586
1587\startsectionlevel[title={\tex{iflist}}]
1588
1589The \type {\ifvoid} test doesnt really test for a box being empty, which is
1590why we have an additional primitive. Compare the following:
1591
1592\startbuffer
1593\setbox0\hbox{}
1594\setbox2\hbox{!}
1595\setbox4\emptybox
1596\setbox8\box6
1597
1598\wd0 10pt \wd2 10pt \wd4 10pt \wd6 10pt
1599
1600[\ifvoid0 Y\else N\fi \iflist0 Y\else N\fi \the\wd0]
1601[\ifvoid2 Y\else N\fi \iflist2 Y\else N\fi \the\wd2]
1602[\ifvoid4 Y\else N\fi \iflist4 Y\else N\fi \the\wd4]
1603[\ifvoid6 Y\else N\fi \iflist6 Y\else N\fi \the\wd6]
1604\stopbuffer
1605
1606\typebuffer[option=TEX]
1607
1608The result demonstrates that we check if there is any content at all, independent
1609of dimensions or the presence of a wrapping list node.
1610
1611\startlines
1612\getbuffer
1613\stoplines
1614
1615\stopsectionlevel
1616
1617\startsectionlevel[title={\tex{ifcramped}}]
1618
1619This test relates to math and in particular to four of the eight states:
1620
1621\startbuffer
1622\im {
1623 \sqrt
1624 {\ifcramped\mathstyle y\else n\fi}
1625 {\ifcramped\mathstyle y\else n\fi}
1626 {\ifcramped\mathstyle y\else n\fi}
1627}
1628\stopbuffer
1629
1630\typebuffer[option=TEX]
1631
1632Because a math formula is first read and then processed in several passes you
1633need to be aware of this state not always being easily predictable because there
1634can be a delay between that read and successive treatments.
1635
1636\startlinecorrection
1637\scale[s=2]{\showglyphs\getbuffer}
1638\stoplinecorrection
1639
1640\stopsectionlevel
1641
1642\startsectionlevel[title={\tex{ifmathparameter}}]
1643
1644The next example demonstrates what this test provides:
1645
1646\startbuffer
1647[\ifmathparameter\Umathextrasubspace \displaystyle zero\or set\else unset\fi]
1648[\ifmathparameter\Umathaccentbaseheight\displaystyle zero\or set\else unset\fi]
1649[\ifmathparameter\Umathaccentbasedepth \displaystyle zero\or set\else unset\fi]
1650\stopbuffer
1651
1652\typebuffer[option=TEX]
1653
1654There are three possible outcomes; here we get: \inlinebuffer. In \LUAMETATEX\ we
1655have more math parameters than in \LUATEX, and some are set in font specific so
1656called \quote {goodie} files.
1657
1658\stopsectionlevel
1659
1660\startsectionlevel[title={\tex{ifmathstyle}}]
1661
1662Here you need to keep in mind that you test the style that is set when \TEX\ scans for
1663formula. Processing happens afterwards and then styles can change.
1664
1665\startbuffer
1666 {\ifmathstyle D\or D\or T\or T\or S\or S\or SS\or SS\else ?\fi}
1667\im{\ifmathstyle D\or D\or T\or T\or S\or S\or SS\or SS\else ?\fi}
1668\dm{\ifmathstyle D\or D\or T\or T\or S\or S\or SS\or SS\else ?\fi}
1669\stopbuffer
1670
1671\typebuffer[option=TEX]
1672
1673We get: \inlinebuffer. The odd values are cramped.
1674
1675
1676\stopsectionlevel
1677
1678\startsectionlevel[title={\tex{ifinalignment}}]
1679
1680This test is an experimental one:
1681
1682\startbuffer
1683\halign \bgroup
1684 \aligncontent
1685 \aligntab
1686 \aligncontent
1687 \cr
1688 one \aligntab \ifinalignment two\else three\fi \cr
1689 \noalign{\ifinalignment yes\else no\fi}
1690 one \aligntab \hbox{\ifinalignment two\else three\fi} \cr
1691\egroup
1692
1693\hbox{\ifinalignment two\else three\fi}
1694\stopbuffer
1695
1696\typebuffer[option=TEX]
1697
1698We get:
1699
1700\startpacked\getbuffer \stoppacked
1701
1702\stopsectionlevel
1703
1704\startsectionlevel[title={\tex{ifinsert}}]
1705
1706This primitive checks if an insert box has content. Usage depends on the macro
1707package so for instance in \CONTEXT, after \type {\footnote {A note.}} you can
1708actually check it with:
1709
1710\starttyping[option=TEX]
1711\setupheadertexts[\ifinsert\namedinsertionnumber{footnote} Y\else N\fi]
1712\stoptyping
1713
1714You pass the number of a insert class and in this example the content, set by the
1715page builder, hasnt yet been flushed.
1716
1717\stopsectionlevel
1718
1719\startsectionlevel[title={\tex{ifflags}}]
1720
1721This one related to interfacing. When a macro is defined, one can apply several
1722prefixes to that macro. Some of these prefixes result in a specific kind of
1723macro, for instance a protected, tolerant, tolerant protected, or regular macro.
1724When a macro is defined global, its (internal) level value indicates that. In
1725addition macros, or actually any control sequence, also the builtin ones, can
1726have a set of flags. Some, have consequences in the engine, so for instance an
1727untraced macro will present itself as a primitive, without details that clutter a
1728log. Other flags get meaning when the overload protection mechanisms are enabled.
1729
1730Testing flags can give some insight but in \CONTEXT\ there is little reason to
1731use this test other than for illustrative purposes. Take this definition
1732
1733\startbuffer
1734\global\protected\def\Foo{Foo}
1735\stopbuffer
1736
1737\typebuffer[option=TEX]
1738
1739This macro is internally represented as follows; here we used \type
1740{\meaningasis}:
1741
1742\start
1743\getbuffer \tt \meaningasis\Foo
1744\stop
1745
1746When we use \type {\meaning} we get:
1747
1748\start
1749\getbuffer \tt \meaning\Foo
1750\stop
1751
1752With \type {\meaningfull} we get:
1753
1754\start
1755\getbuffer \tt \meaningfull\Foo
1756\stop
1757
1758Here is how you can test what properties and flags are set.
1759
1760\startbuffer[test]
1761\ifflags\Foo\global global \fi
1762\ifflags\Foo\protected protected \fi
1763\ifflags\Foo\tolerant tolerant \fi
1764\stopbuffer
1765
1766\typebuffer[test][option=TEX]
1767
1768We only show a few tests here:
1769
1770\start \getbuffer \tt \getbuffer[test] \stop
1771
1772Instead of a prefix you can also pass a number:
1773
1774\startbuffer[test]
1775\ifflags\relax\primitiveflagcode primitive \fi
1776\ifflags\relax\permanentflagcode permanent \fi
1777\stopbuffer
1778
1779\typebuffer[test][option=TEX]
1780
1781\start \tt \getbuffer[test] \stop
1782
1783In \CONTEXT\ many macros are defined as permanent which in terms of overload
1784protection has the same impact. Relevant flag values are available in \typ
1785{tex.getflagvalues()} but in \CONTEXT\ we prefer predefined constants:
1786
1787\startalign[flushleft]
1788\startluacode
1789local done = false
1790for k, v in table.sortedhash(tex.flagcodes) do
1791 if type(k) == "string" then
1792 if done then
1793 context(", ")
1794 else
1795 done = true
1796 end
1797 context.tex(k .. "flagcode")
1798 end
1799end
1800\stopluacode
1801\stopalign
1802
1803\stopsectionlevel
1804
1805\startsectionlevel[title={\tex{ifparameters}}]
1806
1807This is an \type {\ifcase} where the number is the number of parameters passed to
1808the current macro. Of course, when used in a macro one should be aware of the
1809fact that another macro call will change this number.
1810
1811\stopsectionlevel
1812
1813\startsectionlevel[title={\tex{ifparameter}}]
1814
1815This test checks if a parameter has been set, and its used as follows:
1816
1817\starttyping
1818\ifparameter#4\or set\else unset\fi
1819\stoptyping
1820
1821because \type {#4} is actually a reference it refers to the parameter in the
1822current macro and is not influences by nested macro calls which makes if more
1823reliable than a \type {\ifparameters} test.
1824
1825\stopsectionlevel
1826
1827\startsectionlevel[title={\tex{orelse}}]
1828
1829This it not really a test primitive but it does act that way. Say that we have this:
1830
1831\starttyping[option=TEX]
1832\ifdim\scratchdimen>10pt
1833 case 1
1834\else\ifdim\scratchdimen<20pt
1835 case 2
1836\else\ifcount\scratchcounter>10
1837 case 3
1838\else\ifcount\scratchcounter<20
1839 case 4
1840\fi\fi\fi\fi
1841\stoptyping
1842
1843A bit nicer looks this:
1844
1845\starttyping[option=TEX]
1846\ifdim\scratchdimen>10pt
1847 case 1
1848\orelse\ifdim\scratchdimen<20pt
1849 case 2
1850\orelse\ifcount\scratchcounter>10
1851 case 3
1852\orelse\ifcount\scratchcounter<20
1853 case 4
1854\fi
1855\stoptyping
1856
1857
1858
1859
1860We stay at the same level. Sometimes a more flat test tree had advantages but if
1861you think that it gives better performance then you will be disappointed. The
1862fact that we stay at the same level is compensated by a bit more parsing, so
1863unless you have millions such cases (or expansions) it might make a bit of a
1864difference. As mentioned, Im a bit sensitive for how code looks so that was the
1865main motivation for introducing it. There is a companion \type {\orunless}
1866continuation primitive.
1867
1868A rather neat trick is the definition of \type {\quitcondition}:
1869
1870\starttyping[option=TEX]
1871\def\quitcondition{\orelse\iffalse}
1872\stoptyping
1873
1874This permits:
1875
1876\starttyping[option=TEX]
1877\ifdim\scratchdimen>10pt
1878 case 1a
1879 \quitcondition
1880 case 4b
1881\fi
1882\stoptyping
1883
1884where, of course, the quitting normally is the result of some intermediate extra
1885test. But let me play safe here: beware of side effects.
1886
1887\stopsectionlevel
1888
1889\startsectionlevel[title={\tex{orunless}}]
1890
1891This is the negated variant of \type {\orelse}.
1892
1893\stopsectionlevel
1894
1895\stopsectionlevel
1896
1897\startsectionlevel[title={For the brave}]
1898
1899\startsectionlevel[title={Full expansion}]
1900
1901If you dont understand the following code, dont worry. There is seldom much
1902reason to go this complex but obscure \TEX\ code attracts some users so \unknown
1903
1904When you have a macro that has for instance assignments, and when you expand that
1905macro inside an \type {\edef}, these assignments are not actually expanded but
1906tokenized. In \LUAMETATEX\ there is a way to apply these assignments without side
1907effects and that feature can be used to write a fully expandable user test. For
1908instance:
1909
1910\startbuffer
1911\def\truecondition {\iftrue}
1912\def\falsecondition{\iffalse}
1913
1914\def\fontwithidhaschar#1#2
1915 {\beginlocalcontrol
1916 \scratchcounter\numexpr\fontid\font\relax
1917 \setfontid\numexpr#1\relax
1918 \endlocalcontrol
1919 \iffontchar\font\numexpr#2\relax
1920 \beginlocalcontrol
1921 \setfontid\scratchcounter
1922 \endlocalcontrol
1923 \expandafter\truecondition
1924 \else
1925 \expandafter\falsecondition
1926 \fi}
1927\stopbuffer
1928
1929\typebuffer[option=TEX] \getbuffer
1930
1931The \type {\iffontchar} test doesnt handle numeric font id, simply because
1932at the time it was added to \ETEX, there was no access to these ids. Now we
1933can do:
1934
1935\startbuffer
1936\edef\foo{\fontwithidhaschar{1} {75}yes\else nop\fi} \meaning\foo
1937\edef\foo{\fontwithidhaschar{1}{999}yes\else nop\fi} \meaning\foo
1938
1939[\ifcondition\fontwithidhaschar{1} {75}yes\else nop\fi]
1940[\ifcondition\fontwithidhaschar{1}{999}yes\else nop\fi]
1941\stopbuffer
1942
1943\typebuffer[option=TEX]
1944
1945These result in:
1946
1947\startlines
1948\getbuffer
1949\stoplines
1950
1951If you remove the \type {\immediateassignment} in the definition above then the
1952typeset results are still the same but the meanings of \type {\foo} look
1953different: they contain the assignments and the test for the character is
1954actually done when constructing the content of the \type {\edef}, but for the
1955current font. So, basically that test is now useless.
1956
1957\stopsectionlevel
1958
1959\startsectionlevel[title={User defined ifs}]
1960
1961There is a \type {\newif} macro that defines three other macros:
1962
1963\starttyping[option=TEX]
1964\newif\ifOnMyOwnTerms
1965\stoptyping
1966
1967After this, not only \type {\ifOnMyOwnTerms} is defined, but also:
1968
1969\starttyping[option=TEX]
1970\OnMyOwnTermstrue
1971\OnMyOwnTermsfalse
1972\stoptyping
1973
1974These two actually are macros that redefine \type {\ifOnMyOwnTerms} to be either
1975equivalent to \type {\iftrue} and \type {\iffalse}. The (often derived from plain
1976\TEX) definition of \type {\newif} is a bit if a challenge as it has to deal with
1977removing the \type {if} in order to create the two extra macros and also make
1978sure that it doesnt get mixed up in a catcode jungle.
1979
1980In \CONTEXT\ we have a variant:
1981
1982\starttyping[option=TEX]
1983\newconditional\MyConditional
1984\stoptyping
1985
1986that can be used with:
1987
1988\starttyping[option=TEX]
1989\settrue\MyConditional
1990\setfalse\MyConditional
1991\stoptyping
1992
1993and tested like:
1994
1995\starttyping[option=TEX]
1996\ifconditional\MyConditional
1997 ...
1998\else
1999 ...
2000\fi
2001\stoptyping
2002
2003This one is cheaper on the hash and doesnt need the two extra macros per test.
2004The price is the use of \type {\ifconditional}, which is {\em not} to confused
2005with \type {\ifcondition} (it has bitten me already a few times).
2006
2007\stopsectionlevel
2008
2009\stopsectionlevel
2010
2011\startsectionlevel[title=Relaxing]
2012
2013When \TEX\ scans for a number or dimension it has to check tokens one by one. On
2014the case of a number, the scanning stops when there is no digit, in the case of a
2015dimension the unit determine the end of scanning. In the case of a number, when a
2016token is not a digit that token gets pushed back. When digits are scanned a
2017trailing space or \type {\relax} is pushed back. Instead of a number of dimension
2018made from digits, periods and units, the scanner also accepts registers, both the
2019direct accessors like \type {\count} and \type {\dimen} and those represented by
2020one token. Take these definitions:
2021
2022\startbuffer
2023\newdimen\MyDimenA \MyDimenA=1pt \dimen0=\MyDimenA
2024\newdimen\MyDimenB \MyDimenB=2pt \dimen2=\MyDimenB
2025\stopbuffer
2026
2027\typebuffer[option=TEX] \getbuffer
2028
2029I will use these to illustrate the side effects of scanning. Watch the spaces
2030in the result.
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043\startbuffer[b]
2044\starttabulate[TT]
2045\NC \type {\whatever{1pt}{2pt}} \NC \edef\temp{\whatever {1pt}{2pt}}[\meaning\temp] \NC \NR
2046\NC \type {\whatever{1pt}{1pt}} \NC \edef\temp{\whatever {1pt}{1pt}}[\meaning\temp] \NC \NR
2047\NC \type {\whatever{\dimen0}{\dimen2}} \NC \edef\temp{\whatever{\dimen0}{\dimen2}}[\meaning\temp] \NC \NR
2048\NC \type {\whatever{\dimen0}{\dimen0}} \NC \edef\temp{\whatever{\dimen0}{\dimen0}}[\meaning\temp] \NC \NR
2049\NC \type {\whatever\MyDimenA\MyDimenB} \NC \edef\temp{\whatever\MyDimenA\MyDimenB}[\meaning\temp] \NC \NR
2050\NC \type {\whatever\MyDimenA\MyDimenB} \NC \edef\temp{\whatever\MyDimenA\MyDimenB}[\meaning\temp] \NC \NR
2051\stoptabulate
2052\stopbuffer
2053
2054First I show what effect we want to avoid. When second argument contains a number
2055(digits) the zero will become part of it so we actually check \type {\dimen00}
2056here.
2057
2058\startbuffer[c][option=TEX]
2059\def\whatever#1#2
2060 {\ifdim#1=#20\else1\fi}
2061\stopbuffer
2062
2063\typebuffer[c][option=TEX] \getbuffer[c,b]
2064
2065The solution is to add a space but watch how that one can end up in the result:
2066
2067\startbuffer[c][option=TEX]
2068\def\whatever#1#2
2069 {\ifdim#1=#2 0\else1\fi}
2070\stopbuffer
2071
2072\typebuffer[c][option=TEX] \getbuffer[c,b]
2073
2074A variant is using \type {\relax} and this time we get this token retained in
2075the output.
2076
2077\startbuffer[c][option=TEX]
2078\def\whatever#1#2
2079 {\ifdim#1=#2\relax0\else1\fi}
2080\stopbuffer
2081
2082\typebuffer[c][option=TEX] \getbuffer[c,b]
2083
2084A solution that doesnt have side effects of forcing the end of a number (using a
2085space or \type {\relax} is one where we use expressions. The added overhead of
2086scanning expressions is taken for granted because the effect is what we like:
2087
2088\startbuffer[c][option=TEX]
2089\def\whatever#1#2
2090 {\ifdim\dimexpr#1\relax=\dimexpr#2\relax0\else1\fi}
2091\stopbuffer
2092
2093\typebuffer[c][option=TEX] \getbuffer[c,b]
2094
2095Just for completeness we show a more obscure trick: this one hides assignments to
2096temporary variables. Although performance is okay, it is the least efficient
2097one so far.
2098
2099\ifdefined\beginlocalcontrol
2100
2101\startbuffer[c][option=TEX]
2102\def\whatever#1#2
2103 {\beginlocalcontrol
2104 \MyDimenA#1\relax
2105 \MyDimenB#2\relax
2106 \endlocalcontrol
2107 \ifdim\MyDimenA=\MyDimenB0\else1\fi}
2108\stopbuffer
2109
2110\typebuffer[c][option=TEX] \getbuffer[c,b]
2111
2112\fi
2113
2114It is kind of a game to come up with alternatives but for sure those involve
2115dirty tricks and more tokens (and runtime). The next can be considered a dirty
2116trick too: we use a special variant of \type {\relax}. When a number is scanned
2117it acts as relax, but otherwise it just is ignored and disappears.
2118
2119\ifdefined\norelax\else\let\norelax\relax\fi
2120
2121\startbuffer[c][option=TEX]
2122\def\whatever#1#2
2123 {\ifdim#1=#2\norelax0\else1\fi}
2124\stopbuffer
2125
2126\typebuffer[c][option=TEX] \getbuffer[c,b]
2127
2128\stopsectionlevel
2129
2130\popoverloadmode
2131
2132\stopdocument
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151 |