www.delorie.com/gnu/docs/m4/m4_30.html   search  
 
Buy GNU books!


GNU macro processor

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.3 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 described previously.

There is a builtin macro, shift, which can, among other things, be used for iterating through the actual arguments to a macro:

 
shift(...)

It takes any number of arguments, and expands to all but the first argument, separated by commas, with each argument quoted.

 
shift(bar)
=>
shift(foo, bar, baz)
=>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(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.

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 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 section 11.1 Decrement and increment operators), and recurses.

Here is the actual implementation of forloop:

 
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')')')

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

  webmaster     delorie software   privacy  
  Copyright 2003   by The Free Software Foundation     Updated Jun 2003