Search code examples
m4

number of non-empty args passed to a macro


In GNU M4, I've been trying to write a function that determines how many non-empty (args that don't equal `') passed to it.

It's supposed to be callable like so:

ne_nargs(0, `1', `')
# 1

ne_nargs(0, `')
# 0

ne_nargs(0, `1')
# 1

Note that the first argument should always be 0.

Here's the code that I've got so far:

define(`ne_nargs', `ifelse(`$#', `1`, $1,
   `ifelse(`$2', `', `pushdef(`$2', $1)ne_nargs(shift($@))',
     `pushdef(`$2', incr($1))ne_nargs(shift($@))')')')

And here's the pseudo-code for it:

if (number_of_args == 1); then
  return first_arg; // which is `0` by default.
else if (second_arg == ''); then
  second_arg = first_arg;
  return ne_nargs(shift(all_args));
else
  second_arg = (++first_arg);
  return ne_nargs(shift(all_args));

ne_nargs(`0', `', `1', `i', `', `l')

# what I get
m4:test.m4:8: empty string treated as 0 in builtin `incr'
m4:test.m4:8: empty string treated as 0 in builtin `incr'
1

# what I expect
3

I can't figure out what I'm doing wrong in the definition of ne_nargs, and after trying a few different ways of abstracting parts of the macro, I'm about ready to give up.


Solution

  • I don't think you should try to overwrite $@; you can see from the warnings that overwriting $2 definitely isn't affecting $@. The join example in the docs instead shifts out the second argument with:

    `$0(`$1', shift(shift($@)))'
    

    So the following works:

    define(`ne_nargs', `ifelse(`$#', `2', `ifelse(`$2', `', `$1', incr(`$1'))',
      `ifelse(`$2', `', `$0(`$1', shift(shift($@)))',
        `$0(incr($1), shift(shift($@)))')')')
    ne_nargs(`0', `', `1', `i', `', `l')
    

    (I don't know a good way to cleanup the recursive call so the 2 argument call doesn't have to be independently checked, so that may be something to improve on if you are concerned about DRYness.)