Bring your own switch
by Etienne Millon on June 5, 2014
Tagged as: tex.
TeX is a very primitive language. Everything is dynamic, even parsing. This explains in part why it’s so long to compile.
It also means that it’s very flexible : it’s possible to define your own control structures. Here is a small explanation of an implementation of a “switch” macro I made last year. It is released as part of my discotex library (a collection of macros, really).
We want to define a control structure that we can use in the following way:
\switch{what}{case1}{then1}
{case2}{then2}
{case3}{then3} {END}
Then, if what
is equal to case1
, the whole construct evaluates to then1
,
etc. This looks like a function with a variable number of arguments, but
actually this is well adapted to how TeX works.
In TeX, control is provided through macros, i.e. rules to rewrite text. Suppose we want to do a macro that expands to “x and y”. LaTeX users are used to the following syntax:
\newcommand{\couple}[2]{#1 and #2}
But in TeX this is written:
\def\couple#1#2{#1 and #2}
Which roughly means that after reading \couple
, TeX will read two strings1
and bind them to #1
and #2
in the body. So \couple{A}{B}
is expanded to A and B
.
Here comes the trick used for defining variadic functions: if more arguments are provided than the number of arguments at the definition point, the extra ones are kept at their place. If fewer arguments are provided, the strings after the call site will be used. So, one can look at TeX functions as just a system to pop strings from the calling site.
Using this, we can implement \switch
:
After reading
\switch
, read two arguments so that we’re considering\switch{what}{x}
.- If
x
is equal toEND
, it is an error: we did not find the entry. TheEND
string is not special to TeX, it is just a convention of our macro.
- If
Otherwise, pop one more string so that we’re considering
\switch{what}{case}{then}
.- If
what
is not equal tocase
, we have to recursively call\switch{what}
which will pop the rest.
- If
If
what
is equal tocase
, then the result isthen
. But it is not enough to return it: we have to pop strings untilEND
is reached. Otherwise they would be output normally and put it the document.
These 3 points map well to the final TeX code.
To read the first case, we write a function with only two parameters. For string
comparison we use \ifstrequal{a}{b}{t}{f}
2 which expands to t
if a
and
b
are equal, or f
otherwise. Note that \switch@next
is the name of a
function. In .sty
files, it is possible to use @
in symbol names. It is a
convention for private macros as they can not be directly used in .tex
files.
\def\switch#1#2{
\ifstrequal{#2}{END}{
\errmessage{switch : case "#1" not found}
}{\switch@next{#1}{#2}
} }
It is also used to do the actual comparison and the recursive call.
\def\switch@next#1#2#3{
\ifstrequal{#1}{#2}
\switch@last}
{#3
{\switch{#1}
} }
Then \switch@last
is a simple recursive function which simulates a loop.
Because the recursive call is done without an explicit parameter, it will keep
on popping strings until finding END
.
\def\switch@last#1{
\ifstrequal{#1}{END}{}
\switch@last}
{ }
That’s it, the macro works. You can even try it!
I am not sure that I would like to write more complex control structures but this was useful to me both in writing it and using it. I hope that you enjoyed it!
I am not sure that this is the correct denomination. For example it will read a string between curly braces, or a single character if they are omitted. In that case it also eats whitespace, which is why you need stuff like
\xspace
to prevent your macros from glueing string together.↩︎It is from the
etoolbox
package. How it works is an implementation detail here, though it would probably be interesting.↩︎