I'm trying out the m4 CLI on linux and trying to create a for(begin, end, step) macro that will print out a comma separated list of numbers in the given range. I also want it to support a default step of 1. I tried the following
[tom@sp4 ~]$ m4
define(`for',`ifelse($#,2,`for($1,$2,1)',eval($1+$3>$2),1,$1,`$1, for(eval($1+$3),$2,$3)')')
for(3,9)
m4:stdin:3: bad expression in eval: 3+>9
3, 4, 5, 6, 7, 8, 9
While it works OK I can't understand why I get the error message as $3 is only blank on first pass. Why is it even bothering to read the eval($1+$3>$2) when $#==2?
EDIT: Using the information William gave me in his answer I've came up with the macro below. It accepts 0 to 3 parameters and allows count down as well as up. I have learned a lot from messing with this one example.
[tom@sp4 ~]$ m4
define(`for',
`ifelse(
$#,0,``for'',
eval($#==1 && $1+0>0),1,`for(1,$1,1)',
$#,1,,
$#,2,`for($1,$2,ifelse(eval($1+0>$2+0),1,-1,1))',
eval($3+0==0 || $3+0>0 && $1+0>$2+0 || $3+0<0 && $1+0<$2+0),1,,
eval($1+0+($3+0)ifelse(eval($3+0<0),1,<,>)$2+0),1,$1,
`$1, for(eval($1+$3),$2,$3)'dnl
)'dnl
)
for
for
for(0)
for(5)
1, 2, 3, 4, 5
for(5,-23,-5)
5, 0, -5, -10, -15, -20
for(5,9)
5, 6, 7, 8, 9
for(6,1)
6, 5, 4, 3, 2, 1
When m4
encounters foo(3,9)
, it expands that to the text of the ifelse
as defined in the macro. In order to invoke ifelse
, it has to pass arguments to it. To determine those arguments, it has to expand eval
. Hence the error message.
You can avoid the error message with:
define(`for',
`ifelse(
`$#', `2', `for(`$1', `$2', `1')',
eval(`$1' + ifelse(`$3', `', `0', `1') > `$2'), `1', `$1',
`$1, for(eval(`$1' + `$3'), `$2', `$3')'dnl
)')dnl