One of TeX's core features, delimited arguments/parameters, doesn't seem to be widely used in LaTeX user code, so some of the common idioms related to them may not be immediately clear when you encounter them in package code. The syntax and argument reading for those parameters is explained in this answer, so I'd like to concentrate on some examples of more or less common idioms using here.
Special argument delimiters
Special argument delimiters are usually implemented using delimited parameters, most notably optional parameters [...]
. The typical pattern for LaTeX macros with optional arguments looks like
\def\foo{\@ifnextchar[{\foo@aux}{\foo@aux[default]}}
\def\foo@aux[#1]{Foo with parameter `#1'}
where \foo
tests if the following character is [
and then passes over to \foo@aux
which uses delimited parameters to read the following optional argument. Arguments with delimiters like (...)
or <...>
work similarly.
Skipping code until end marker
A common idiom is to use delimited parameters to skip parts of the code. As explained in Ryan Reich's answer, sometimes we have to get rid of unwanted tokens like an extra \else
or \fi
. The answer presents one approach to deal with them.
Another one is to define helper macros that use delimited parameters to skip through the conditional up to the final \fi
, and then move the corresponding parameter after the \fi
that is required to end the conditional. Using the macros
\def\handletrue#1\else#2\fi{\fi#1}
\def\handlefalse#1\fi{\fi#1}
we get the following expansions series for different conditions:
\iftrue \handletrue{\textbf}\else \handlefalse{\textit}\fi{foo}
--> \handletrue{\textbf}\else \handlefalse{\textit}\fi{foo}
--> \fi\textbf{foo}
--> \textbf{foo}
\iffalse \handletrue{\textbf}\else \handlefalse{\textit}\fi{bar}
--> \handlefalse{\textit}\fi{bar}
--> \fi\textit{bar}
--> \textit{bar}
thus correctly outputting "foo"/"bar". The lazylist
package uses something similar:
\def\TeXif#1{#1\gobblefalse\else\gobbletrue\fi}
\def\gobblefalse\else\gobbletrue\fi#1#2{\fi#1}
\def\gobbletrue\fi#1#2{\fi#2}
A second use case of this idea are multiway branches (switch/case). TeX doesn't provide this control structure natively, so usually nested conditionals are used to avoid redundant tests in the code. Again, delimited parameters can be useful here. l3regex
, for example, defines macros \__l3regex_break_true:w
and \__l3regex_break_point:TF
(renamed below for readability) which can be used to emulate a multiway branch:
\def\breaktrue#1\breakpoint#2#3{#2}
\def\breakpoint#1#2{#2}
\def\testchar{y}
\if\testchar x
An `x` was found.\breaktrue
\fi
\if\testchar y
An `y` was found.\breaktrue
\fi
\if\testchar z
An `y` was found.\breaktrue
\fi
\breakpoint{\fi}{No character matched.}
Whenever an \breaktrue
is found, it will skip anything up to the next \breakpoint
, in particular all the remaining alternative tests. If any of the tests was successful, the first parameter of \breakpoint
is inserted into the token stream (here the skipped \fi
for the successful test must be reinserted), otherwise a common "else branch" is used.
Splitting token lists
Delimited parameters are also often used for testing if a certain separator token occurs in a list of tokens, such that the sublists before and after that token can be handled in a special way. As an example, say we want to replace all occurences of \textbf{...}
in a token list by \textit{...}
. We start with a helper macro \@replacebold
that uses delimited parameters to split a token list, passed to it without surrounding braces:
\def\@replacebold#1\textbf#2#3\@end{...}
\textbf
here delimits any tokens that occur before the first occurence of this control sequence in the list (#1
), \@end
is the delimiter for all the rest. As \textbf
takes a single argument, we add another parameter #2
to match on this argument. Finally, #3
matches anything after the bold part. With this decomposition we can easily replace the bold text with an italic one and handle the rest of the list recursively.
Delimited parameters are mandatory in the actual argument if used in the macro definition, so we still need to handle the case in which \textbf{...}
doesn't occur in the argument at all (which is also the case at the end of recursion). For this we already pass \@replacebold
a single, faked call of \textbf
which will match in any case:
\def\replacebold#1{\@replacebold#1\textbf{}\@end}
If this final occurrence is reached when calling \@replacebold
, #3
will be empty as \textbf{}
is immediately followed by \@end
. We can use this fact for our test if another recursion step is necessary. Putting all pieces together, we might define \replacebold
as follows:
\def\replacebold#1{\@replacebold#1\textbf{}\@end}
\def\@replacebold#1\textbf#2#3\@end{%
#1%
% Real code should use a saner test for emptiness here
\ifx\relax#3\relax%
\expandafter\@gobble
\else
\textit{#2}%
\expandafter\@firstofone
\fi
{\@replacebold#3\@end}%
}
This idiom is used quite frequently in many variations. LaTeX's \@for
loop macro, for example, uses a more sophisticated version to split a comma-separated list into its fields. You might also enjoy trying to find out how the various \quark_if_recursion
functions form expl3
use this pattern for looping.
Other uses
Paragraphs as arguments: When TeX finds an end-of-line character in the input text that is immediately followed by another such character, it converts both into a \par
token. This token can also be used as a delimited parameter. For example, the macro
\def\warning#1\par{%
\textbf{Attention!} {\itshape #1\par}
}
could be used like \warning Do not use ...
and will then read all text up to the end of the current paragraph, i.e. the next explict \par
or empty line in the input file.
Simple loops: Plain TeX defines a simple loop macro \loop ... \repeat
which uses a delimiter to mark the end of the loop body:
\def\loop#1\repeat{\def\body{#1}\iterate}
\def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next}
\let\repeat=\fi % this makes \loop...\if...\repeat skippable
\count0=1
\loop
... use \count0 here ...
\ifnum\count0<10
\advance \count0 by 1
\repeat
Use without parameter: Plain TeX also has a special use of delimiters in an internal macro to make sure that the command name passed to \newif
actually starts with the letters i
f
. The macro \@if
then gobbles those letters and builds a control sequence from the rest of the letters:
\def\@if#1#2{\csname\expandafter\if@\string#1#2\endcsname}
{\uccode`1=`i \uccode`2=`f \uppercase{\gdef\if@12{}}}
% The letters "12" here -----------------------^^
% are converted to the letters "if" with catcode 12 and
% act as delimiter (no actual argument is used).
\usepackage{trace} ... \traceon
:-)\romannumeral
expansion trick fits perfectly here. Joseph, you might want to (more or less) copy-paste your blog post here;).