The ifthen package^{∗}

David Carlisle

1996/08/02

Abstract This file implements an \ifthenelse command for L^{A}TEX 2ε. The algorithm used is compatible with that used in the L^{A}TEX 2.09 ifthen style option. It has been recoded, making the resulting definitions somewhat more compact and efficient.

1 Introduction

\ifthenelse \ifthenelse{⟨test⟩}{⟨then clause⟩}{⟨else clause⟩}

Evaluates ⟨test⟩ as a boolean function, and then executes either ⟨then clause⟩ or ⟨else clause⟩.

⟨test⟩ is a boolean expression using the infix connectives, \and, \or, the unary \not and parentheses \( \).

The atomic propositions are: ⟨number⟩ < ⟨number⟩ ⟨number⟩ = ⟨number⟩ ⟨number⟩ > ⟨number⟩

\isodd{ ⟨number⟩ }

\equal{⟨string⟩}{⟨string⟩}

\lengthtest{⟨dimen⟩<⟨dimen⟩}
\lengthtest{⟨dimen⟩=⟨dimen⟩}
\lengthtest{⟨dimen⟩>⟨dimen⟩}
\boolean{⟨name⟩}

The ⟨string⟩s tested by \equal may be any sequence of commands that expand to a list of tokens. If these expansions are equal, then the proposition is true.

\isodd is true if the ⟨number⟩ is odd, and false otherwise (even if the argument is not a number).

\boolean{xyz} returns the truth value contained in the primitive TEX \if, \ifxyz. This is usually used with boolean flags created with \newboolean and \provideboolean described below. It can also be used with the names of \newif created tokens, and primitive TEX \if constructs, for example \boolean{true}

(\iftrue), \boolean{mmode} (\ifmmode) etc.

∗This file has version number v1.0m, last revised 1996/08/02.

The commands:

\newboolean  \newboolean{⟨name⟩} and \provideboolean{⟨name⟩} are provided so the user

\provideboolean can easily create new boolean flags. As for \newcommand, \newboolean generates an error if the command name is not new. \provideboolean silently does nothing in that case.

The boolean flags may be set with:

\setboolean  \setboolean{⟨name⟩}{⟨value⟩}

⟨value⟩ may be either true or false (any CaSe).

Note that there is no precedence between \and and \or. The proposition is evaluated in a left right manner. \not only applies to the immediately following proposition. (This is consistent with Lamport’s ifthen.sty. In this style, though the test is ‘lazily’ evaluated, so for instance if the first proposition in an \or is true, the second one is skipped. (On the second pass—The first pass in an \edef expands clauses in all propositions).

Apart from the addition of the extra atomic propositions \isodd, \boolean and \lengthtest, the only known incompatibility is that in this package the expression\not\not⟨P⟩ is equivalent to ⟨P⟩. However in the original style it was equivalent to \not⟨P⟩. This is intentional (bug fix:-).

\whiledo The command \whiledo is also defined (copied directly from The L^{A}TEX2.09

definition).

\whiledo{⟨test⟩}{⟨while clause⟩}

With ⟨test⟩ as above, repeatedly executes ⟨while clause⟩ while the test remains true.

2 The Implementation

1 ⟨∗package⟩

\TE@throw In order to support the syntax of ifthen.sty, which allows access to the primitive TEX syntax for a numeric test, rather than a {} delimited argument form, it is most convenient to work ‘within’ an \ifnum. \ift@throw ‘throws’ you out of the current \ifnum so that you can (eg) start an \ifdim for the length tests.

 2 \def\TE@throw{\@ne=\@ne\noexpand\fi}

\boolean A non-standard extension to ifthen, supporting boolean was previously available, this is a simpler implementation.

 3 \def\boolean#1#2{%
 4 \TE@throw\expandafter\noexpand\csname if#1\endcsname#2}

\TE@length Testing lengths. #1 is the test. The extra argument gobbles spaces.

 5 \def\TE@length#1#2{\TE@throw\noexpand\ifdim#1#2}

\TE@odd \TE@@odd Testing odd/even. This is true if #1 is an odd number, and false otherwise (even

if #1 is not a number at all).

It is hard to make this completely reliable. Here I have erred on the side of safety. This should not generate a TEX error if given any robust commands as its argument. However it returns true on any argument that starts with an odd number 11xx which is bad, and it can not deal with TEX’s count registers, although L^{A}TEX counters work (via \value).

 6 \def\TE@odd#1#2{%
 7 \TE@throw\noexpand\TE@@odd#1\noexpand\@nil\noexpand\ifodd\count@#2}

\TE@@odd is not expanded on the first pass.

 8 \def\TE@@odd#1#2\@nil{%
 9 \@defaultunits
10 \count@\if-#1-0\else0\expandafter#1\fi#2\relax\@nnil}

\TE@repl \TE@repl replaces the single token #1 by #2. (Not within {} groups.) It is used to replace \or by \TE@or without the need to redefine \or. Earlier versions just \let\or\TE@or but this has a bad effect on the expansion of commands which use the primitive \or internally, eg \alph, and so caused surprising results if these commands were used inside \equal.

11 \def\TE@repl#1#2{%
12 \long\def\@tempc##1#1##2{%
13  \def\@tempa{##2}\def\@tempb{\@tempc}%
14  \ifx\@tempa\@tempb
15  \toks@\expandafter{\the\toks@##1}%
16  \expandafter\@gobble
17  \else
18  \toks@\expandafter{\the\toks@##1#2}%
19  \expandafter\@tempc
20  \fi
21  ##2}%
22 \expandafter\toks@\expandafter{\expandafter}%
23 \expandafter\@tempc\the\toks@#1\@tempc}

\ifthenelse The remaining macros in this file are derived from the ones in ifthen.sty but recoded and simplified. The main simplification is that the original style (and the \boolean extensions) expressed logical values always in terms of \ifnum. As \fi is ‘untyped’ this is not necessary, so for example the length tests can return values via \ifdim, the trailing \fi will not complain, even though it was ‘expecting’ an \ifnum. Also the system of passing information via macros expanding to T or F has been completely replaced by a simpler system using \iftrue, which furthermore allows lazy evaluation on the second pass.

24 \long\def\ifthenelse#1{%
25 \toks@{#1}%
26 \TE@repl\or\TE@or
27 \TE@repl\and\TE@and
28 \TE@repl\not\TE@neg

The original ifthen.sty processed everything inside a box assignment, to catch any extra spaces before they appeared in the output. Instead I have added extra arguments to the commands so they each remove any following space.

Set up the user level names \not etc.

29  \begingroup
30  \let\protect\@unexpandable@protect
31  \def\@setref##1##2##3{%
32  \ifx##1\relax\z@\else\expandafter##2##1\fi}%
33  \def\value##1{\the\csname c@##1\endcsname}%
34  \let\equal\TE@equal \let\(\TE@lparen \let\)\TE@rparen
35  \let\isodd\TE@odd \let\lengthtest\TE@length

For the first pass, in a group, make various tokens non-expandable.

It is unfortunate that in order to remain compatible with ifthen syntax, it is necessary to have a two pass system. The first pass inside an \edef ‘exposes’ the \if… \fi tokens, so the corect clauses may be skipped on the second pass. This means that the whole \ifthenelse command does not work by expansion, and so possibly has only limited usefulness for macro code writers. The main problem with the ifthen: syntax is that (unique for L^{A}TEX) it does not uses a brace delimited argument form, and exposes the primitive TEX syntax for ⟨number⟩. Pretty much the only way of parsing 1 > 2 \or 2 < 1 is to actually evaluate the primitive \ifnums. A syntax such as:

\or{\numtest{1<2}}{\lengthtest{1pt<1in}}

could easily be evaluated in a one pass way, operating directly via expansion, and leaving no extra tokens in the token stream.

Still, on with the code… make \@tempa and \@tempb tokens non-expandable on the first pass.

36  \begingroup
37  \let\@tempa\relax\let\@tempb\relax
38  \xdef\@gtempa{\expandafter\TE@eval\the\toks@\TE@endeval}%
39  \endgroup

Now outside the group, execute \@gtempa which causes all the \ifs etc., to be evaluated, the final truth value is contained in the \newif token \ifTE@val. Finally this is tested and either the first or second following argument is chosen accordingly.

40  \@gtempa
41  \expandafter\endgroup\ifTE@val
42  \expandafter\@firstoftwo
43  \else
44  \expandafter\@secondoftwo
45  \fi}

\TE@eval Initialise a term. (Expanded on the first pass).

46 \def\TE@eval{\noexpand\TE@negatefalse\noexpand\iftrue\noexpand\ifnum}

\ifTE@val \ifTE@negate Two \newifs the first holds the current truth value of the expression. The second is a temporary flag which is true if we need to negate the current proposition.

47 \newif\ifTE@val
48 \newif\ifTE@negate

\TE@endeval Finalise a term. (Expanded on the first pass).

49 \def\TE@endeval{\relax
50  \noexpand\TE@setvaltrue\noexpand
51  \else
52  \noexpand\TE@setvalfalse\noexpand
53  \fi
54  \noexpand\TE@negatefalse\noexpand
55 \fi}

\TE@setvaltrue \TE@setvalfalse Set the \ifTE@val to true or false depending on the value of the current proposition, and the negate flag. (Not expanded on the first pass.)

56 \def\TE@setvaltrue{%
57 \ifTE@negate\TE@valfalse\else\TE@valtrue\fi}
58 \def\TE@setvalfalse{\let\ifTE@val\ifTE@negate}

\TE@or The internal version of \or. Ends the current term. If true skip the remaining terms.

59 \def\TE@or{\TE@endeval\noexpand\ifTE@val\noexpand\else\noexpand\ifnum}

\TE@and The internal version of \and. If false skip the remaining terms.

60 \def\TE@and{\TE@endeval\noexpand\ifTE@val\noexpand\ifnum}

\TE@neg \TE@negswitch \not. Throw the current context, set a negate flag, then restart the \ifnum. \TE@negswitch is not expanded on the first pass.

61 \def\TE@neg{\TE@throw\noexpand\TE@negswitch\noexpand\ifnum}
62 \def\TE@negswitch{\ifTE@negate\TE@negatefalse\else\TE@negatetrue\fi}

\TE@lparen \(. Throw the current context, then restart a term inside a group.

63 \def\TE@lparen#1{\TE@throw\begingroup\TE@eval#1}

\TE@rparen \) end the current term, and the local group started by \(, but pass on the boolean value in \if\@val T. The \noexpand stops the \expandafter from expanding on the first pass.

64 \def\TE@rparen#1{%
65 \TE@endeval
66 \noexpand\expandafter\endgroup\noexpand\ifTE@val#1}

\TE@equal \equal greatly simplified from the original. \def may be used rather than \edef as the whole thing is expanded anyway in the first pass. The boolean can be directly encoded with the \ifx, there is no need to start an equivalent \ifnum.

67 \long\def\TE@equal#1#2#3{\TE@throw
68  \def\@tempa{#1}\def\@tempb{#2}%
69  \noexpand\ifx\@tempa\@tempb#3}
\boolean  \setboolean takes true or false, as #2, and sets #1 accordingly.
70 \def\setboolean#1#2{%
71 \lowercase{\def\@tempa{#2}}%
72 \@ifundefined{@tempswa\@tempa}%
73  {\PackageError{ifthen}%

74  {You can only set a boolean to `true' or `false'}\@ehc}%
75  {\@ifundefined{#1\@tempa}%
76  {\PackageError{ifthen}{Boolean #1 undefined}\@ehc}%
77  {\csname#1\@tempa\endcsname}}}

\newboolean Define a new ‘boolean’.

78 \def\newboolean#1{%
79 \expandafter\@ifdefinable\csname if#1\endcsname{%
80  \expandafter\newif\csname if#1\endcsname}}

\provideboolean Define a new ‘boolean’ if it is not already defined.

81 \def\provideboolean#1{%
82 \@ifundefined{if#1}{%
83  \expandafter\newif\csname if#1\endcsname}\relax}

\whiledo \whiledo copied directly from the original.

\whiledo{⟨test⟩}{⟨body⟩}

repeatedly evaluates ⟨body⟩ until ⟨test⟩ is true.

84 \long\def\whiledo#1#2{%
85 \ifthenelse{#1}%
86  {\@whiledotrue
87  \@whilesw\if@whiledo\fi
88  {#2%
89  \ifthenelse{#1}\@whiledotrue\@whiledofalse}}%
90  {}%

91 }

\if@whiledo Internal switch for \whiledo.
92 \newif\if@whiledo

93 ⟨/package⟩

Change History

v1.0a

General: New implementation … 1 v1.0b

General: Upgrade to LaTeX2e … 1 \TE@@odd: Improve \isodd . . . . . . 3

v1.0c

   \ifthenelse: Modify\protect and

\value . . . . . . . . . . . . . . . . . . 3 \TE@@odd: Improve \isodd again . 3

v1.0d

   \ifthenelse: Use \@firstoftwo

not \@leftmark . . . . . . . . . . . 4 v1.0e

General: Improve documentation . 1 v1.0f

General: Remove need for dtx file . 1 v1.0g

General: Modify for the new ltx-

doc.cls . . . . . . . . . . . . . . . . . . 1 v1.0h

                                          \ifthenelse: Use \TE@repl . . . . . 3

\TE@equal: make long . . . . . . . . . 5 \TE@repl: macro added . . . . . . . . 3

v1.0i \boolean: New style error com-

mands . . . . . . . . . . . . . . . . . . 5 v1.0j

   \ifthenelse: Modify\protect add

\@setref . . . . . . . . . . . . . . . . 3 v1.0k

General: Fix ‘driver’ docstrip

guards. . . . . . . . . . . . . . . . . . . 1 v1.0l

   \ifthenelse: Use \begingroup not

\bgroup for latex/2105 . . . . . . 3 Use \begingroup not { for latex/2105 . . . . . . . . . . . . . . . . . 4

v1.0m

                                          \newboolean: Add \@ifdefinable

test for latex/2220 . . . . . . . . . 6

                                          \provideboolean: Macro added for

latex/2220 . . . . . . . . . . . . . . . 6

Index

Numbers written in italic refer to the page where the corresponding entry is described, the ones underlined to the code line of the definition, the rest to the code lines where the entry is used.

Symbols

\( . . . . . . . . . . . . . . 34 \) . . . . . . . . . . . . . . 34

\@defaultunits . . . . . 9

\@ehc . . . . . . . . . . 74, 76

\@firstoftwo . . . . . . 42

\@gobble . . . . . . . . . 16 \@gtempa . . . . . . . 38, 40

\@ifdefinable . . . . . 79
\@ifundefined 72, 75, 82

\@ne . . . . . . . . . . . . . . 2 \@nil . . . . . . . . . . . 7, 8 \@nnil . . . . . . . . . . . 10

\@secondoftwo . . . . . 44

\@setref . . . . . . . . . 31 \@tempa 13, 14, 37, 68,

69, 71, 72, 75, 77 \@tempb 13, 14, 37, 68, 69 \@tempc . . 12, 13, 19, 23

\@unexpandable@protect

. . . . . . . . . . . . 30

\@whiledofalse . . . . 89
\@whiledotrue . . . 86, 89

\@whilesw . . . . . . . . 87

A

\and . . . . . . . . . . . . . 27

B

\begingroup . . 29, 36, 63

\boolean . . . . . . . . \underline{3}, \underline{70}

C \count@ . . . . . . . . . 7, 10 \csname . . . . . . . . 4,

33, 77, 79, 80, 83

D \def . . . . . . 2, 3, 5, 6,

8, 11–13, 24, 31, 33, 46, 49, 56, 58–64, 67, 68, 70, 71, 78, 81, 84

E \else …. 10, 17, 32,

43, 51, 57, 59, 62

                          \endcsname . . . . . 4,

33, 77, 79, 80, 83 \endgroup … 39, 41, 66 \equal . . . . . . . . . . . 34

                          \expandafter . . . . . .

. . 4, 10, 15, 16, 18, 19, 22, 23, 32, 38, 41, 42, 44, 66, 79, 80, 83

F \fi 2, 10, 20, 32, 45,

53, 55, 57, 62, 87

I \if . . . . . . . . . . . . . 10

                          \if@whiledo . . . . . 87, \underline{92}

\ifdim . . . . . . . . . . . . 5 \ifnum . . . . . . 46, 59–61 \ifodd . . . . . . . . . . . . 7

                                                   \ifTE@negate . . . . . .

…. \underline{47}, 57, 58, 62

                                                   \ifTE@val . . . . . . . .

. 41, \underline{47}, 58–60, 66

                                                   \ifthenelse 1, \underline{24}, 85, 89

\iftrue . . . . . . . . . . 46 \ifx . . . . . . . . 14, 32, 69 \isodd . . . . . . . . . . . 35

L

                                                   \lengthtest . . . . . . . 35

\let … 30, 34, 35, 37, 58 \long …. 12, 24, 67, 84

                                                   \lowercase . . . . . . . 71

N

                                                   \newboolean . . . . . . 2, \underline{78}

\newif . 47, 48, 80, 83, 92

                                                   \noexpand . . . . 2, 4,

5, 7, 46, 50, 52, 54, 59–61, 66, 69

\not . . . . . . . . . . . . . 28

O \or . . . . . . . . . . . . . 26

P

                                                   \PackageError . . . 73, 76

\protect . . . . . . . . . 30

                                                   \provideboolean . . 2, \underline{81}

R

\relax . 10, 32, 37, 49, 83

S

\setboolean . . . . . . 2, 70

T

\TE@@odd . . . . . . . . . . \underline{6} \TE@and . . . . . . . . 27, \underline{60}

\TE@endeval . . . . . . .

. 38, \underline{49}, 59, 60, 65 \TE@equal . . . . . . 34, \underline{67} \TE@eval …. 38, \underline{46}, 63

\TE@length . . . . . . \underline{5}, 35
\TE@lparen . . . . . 34, \underline{63}

\TE@neg . . . . . . . . 28, \underline{61}

\TE@negatefalse . . .

. . . . . . . 46, 54, 62

\TE@negatetrue . . . . 62
\TE@negswitch . . . . . \underline{61}

\TE@odd . . . . . . . . . \underline{6}, 35 \TE@or . . . . . . . . . 26, \underline{59} \TE@repl …. \underline{11}, 26–28

\TE@rparen . . . . . 34, \underline{64}
\TE@setvalfalse . 52, \underline{56}
\TE@setvaltrue . . 50, \underline{56}
\TE@throw . . . . . . \underline{2},

4, 5, 7, 61, 63, 67

\TE@valfalse . . . . . . 57
\TE@valtrue . . . . . . . 57

\the … 15, 18, 23, 33, 38 \toks@ . . . . . . . . 15,

18, 22, 23, 25, 38

V \value . . . . . . . . . . . 33

W \whiledo . . . . . . . . 2, \underline{84}

X \xdef . . . . . . . . . . . . 38

Z \z@ . . . . . . . . . . . . . 32