m4: Forloop

 
 6.4 Iteration by counting
 =========================
 
 Here is an example of a loop macro that implements a simple for loop.
 
  -- Composite: forloop (ITERATOR, START, END, TEXT)
      Takes the name in ITERATOR, which must be a valid macro name, and
      successively assign it each integer value from START to END,
      inclusive.  For each assignment to ITERATOR, append TEXT to the
      expansion of the 'forloop'.  TEXT may refer to ITERATOR.  Any
      definition of ITERATOR prior to this invocation is restored.
 
    It can, for example, be used for simple counting:
 
      $ m4 -I examples
      include(`forloop.m4')
      =>
      forloop(`i', `1', `8', `i ')
      =>1 2 3 4 5 6 7 8 
 
    For-loops can be nested, like:
 
      $ m4 -I examples
      include(`forloop.m4')
      =>
      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 the iterator has reached the final value.  If it has not
 finished, it increments the iterator (using the predefined macro 'incr',
 ⇒Incr), and recurses.
 
    Here is an actual implementation of 'forloop', distributed as
 'm4-1.4.18/examples/forloop.m4' in this package:
 
      $ m4 -I examples
      undivert(`forloop.m4')dnl
      =>divert(`-1')
      =># forloop(var, from, to, stmt) - simple version
      =>define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')')
      =>define(`_forloop',
      =>       `$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@)')')
      =>divert`'dnl
 
    Notice the careful use of quotes.  Certain macro arguments are left
 unquoted, each for its own reason.  Try to find out _why_ these
 arguments are left unquoted, and see what happens if they are quoted.
 (As presented, these two macros are useful but not very robust for
 general use.  They lack even basic error handling for cases like START
 less than END, END not numeric, or ITERATOR not being a macro name.  See
 if you can improve these macros; or ⇒Answers Improved forloop.).