Previous: Ifelse, Up: Conditionals
There is no direct support for loops in m4
, but macros can be
recursive. There is no limit on the number of recursion levels, other
than those enforced by your hardware and operating system.
Loops can be programmed using recursion and the conditionals described previously.
There is a builtin macro, shift
, which can, among other things,
be used for iterating through the actual arguments to a macro:
Takes any number of arguments, and expands to all its arguments except arg1, separated by commas, with each argument quoted.
The macro
shift
is recognized only with parameters.
shift =>shift shift(`bar') => shift(`foo', `bar', `baz') =>bar,baz
An example of the use of shift
is this macro:
It is implemented as:
define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'', `reverse(shift($@)), `$1'')') => reverse => reverse(`foo') =>foo reverse(`foo', `bar', `gnats', `and gnus') =>and gnus, gnats, bar, foo
While not a very interesting macro, it does show how simple loops can be
made with shift
, ifelse
and recursion. It also shows
that shift
is usually used with `$@'.
Here is an example of a loop macro that implements a simple for loop.
Takes the name in iterator, which must be a valid macro name, and successively assign it each integer value from start to end, inclusive. For each assignment to iterator, append text to the expansion of the
forloop
. text may refer to iterator. Any definition of iterator prior to this invocation is restored.
It can, for example, be used for simple counting:
include(`forloop.m4') => forloop(`i', `1', `8', `i ') =>1 2 3 4 5 6 7 8
For-loops can be nested, like:
include(`forloop.m4') => forloop(`i', `1', `4', `forloop(`j', `1', `8', ` (i, j)') ') => (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6) (1, 7) (1, 8) => (2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (2, 7) (2, 8) => (3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (3, 6) (3, 7) (3, 8) => (4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (4, 6) (4, 7) (4, 8) =>
The implementation of the forloop
macro is fairly
straightforward. The forloop
macro itself is simply a wrapper,
which saves the previous definition of the first argument, calls the
internal macro _forloop
, and re-establishes the saved definition of
the first argument.
The macro _forloop
expands the fourth argument once, and tests
to see if it is finished. If it has not finished, it increments
the iteration variable (using the predefined macro incr
,
see Incr), and recurses.
Here is the actual implementation of forloop
, distributed as
examples/forloop.m4 in this package:
undivert(`forloop.m4') =>divert(`-1') =># forloop(var, from, to, stmt) =>define(`forloop', => `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')') =>define(`_forloop', => `$4`'ifelse($1, `$3', , => `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')') =>divert`'dnl =>
Notice the careful use of quotes. Only three macro arguments are unquoted, each for its own reason. Try to find out why these three arguments are left unquoted, and see what happens if they are quoted.
Now, even though these two macros are useful, they are still not robust enough for general use. They lack even basic error handling of cases like start value less than final value, and the first argument not being a name. Correcting these errors are left as an exercise to the reader.