Whole document tree
Conditionals, loops and recursionMacros, expanding to plain text, perhaps with arguments, are not quite enough. We would like to have macros expand to different things, based on decisions taken at run-time. E.g., we need some kind of conditionals. Also, we would like to have some kind of loop construct, so we could do something a number of times, or while some condition is true. Testing macro definitions
There are two different builtin conditionals in ifdef(name, string-1, opt string-2)
which makes it possible to test whether a macro is defined or not. If
name is defined as a macro, ifdef(`foo', ``foo' is defined', ``foo' is not defined') =>foo is not defined define(`foo', `') => ifdef(`foo', ``foo' is defined', ``foo' is not defined') =>foo is defined
The macro Comparing strings
The other conditional, ifelse(comment) ifelse(string-1, string-2, equal, opt not-equal) ifelse(string-1, string-2, equal, ...)
Used with only one argument, the
If called with three or four arguments, ifelse(foo, bar, `true') => ifelse(foo, foo, `true') =>true ifelse(foo, bar, `true', `false') =>false ifelse(foo, foo, `true', `false') =>true
However, ifelse(foo, bar, `third', gnu, gnats, `sixth', `seventh') =>seventh
Naturally, the normal case will be slightly more advanced than these
examples. A common use of
The macro Loops and recursion
There is no direct support for loops in Loops can be programmed using recursion and the conditionals described previously.
There is a builtin 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 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 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 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
The macro
Here is the actual implementation of 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. Go to the first, previous, next, last section, table of contents. |