Loops and recursion
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
There is a builtin macro, `shift', which can, among other things, be
used for iterating through the actual arguments to a macro:
It takes any number of arguments, and expands to all but the first
argument, separated by commas, with each argument quoted.
shift(foo, bar, baz)
An example of the use of `shift' is this macro, which reverses the
order of its arguments:
define(`reverse', `ifelse($#, 0, , $#, 1, ``$1'',
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.
Here is an example of a loop macro that implements a simple forloop.
It can, for example, be used for simple counting:
forloop(`i', 1, 8, `i ')
=>1 2 3 4 5 6 7 8
The arguments are a name for the iteration variable, the starting
value, the final value, and the text to be expanded for each iteration.
With this macro, the macro `i' is defined only within the loop. After
the loop, it retains whatever value it might have had before.
For-loops can be nested, like
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
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', Incr.),
Here is the actual implementation of `forloop':
`pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
`$4`'ifelse($1, `$3', ,
`define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
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
automatically generated byinfo2html