m4: Foreach

 
 6.5 Iteration by list contents
 ==============================
 
 Here is an example of a loop macro that implements list iteration.
 
  -- Composite: foreach (ITERATOR, PAREN-LIST, TEXT)
  -- Composite: foreachq (ITERATOR, QUOTE-LIST, TEXT)
      Takes the name in ITERATOR, which must be a valid macro name, and
      successively assign it each value from PAREN-LIST or QUOTE-LIST.
      In 'foreach', PAREN-LIST is a comma-separated list of elements
      contained in parentheses.  In 'foreachq', QUOTE-LIST is a
      comma-separated list of elements contained in a quoted string.  For
      each assignment to ITERATOR, append TEXT to the overall expansion.
      TEXT may refer to ITERATOR.  Any definition of ITERATOR prior to
      this invocation is restored.
 
    As an example, this displays each word in a list inside of a
 sentence, using an implementation of 'foreach' distributed as
 'm4-1.4.18/examples/foreach.m4', and 'foreachq' in
 'm4-1.4.18/examples/foreachq.m4'.
 
      $ m4 -I examples
      include(`foreach.m4')
      =>
      foreach(`x', (foo, bar, foobar), `Word was: x
      ')dnl
      =>Word was: foo
      =>Word was: bar
      =>Word was: foobar
      include(`foreachq.m4')
      =>
      foreachq(`x', `foo, bar, foobar', `Word was: x
      ')dnl
      =>Word was: foo
      =>Word was: bar
      =>Word was: foobar
 
    It is possible to be more complex; each element of the PAREN-LIST or
 QUOTE-LIST can itself be a list, to pass as further arguments to a
 helper macro.  This example generates a shell case statement:
 
      $ m4 -I examples
      include(`foreach.m4')
      =>
      define(`_case', `  $1)
          $2=" $1";;
      ')dnl
      define(`_cat', `$1$2')dnl
      case $`'1 in
      =>case $1 in
      foreach(`x', `(`(`a', `vara')', `(`b', `varb')', `(`c', `varc')')',
              `_cat(`_case', x)')dnl
      =>  a)
      =>    vara=" a";;
      =>  b)
      =>    varb=" b";;
      =>  c)
      =>    varc=" c";;
      esac
      =>esac
 
    The implementation of the 'foreach' macro is a bit more involved; it
 is a wrapper around two helper macros.  First, '_arg1' is needed to grab
 the first element of a list.  Second, '_foreach' implements the
 recursion, successively walking through the original list.  Here is a
 simple implementation of 'foreach':
 
      $ m4 -I examples
      undivert(`foreach.m4')dnl
      =>divert(`-1')
      =># foreach(x, (item_1, item_2, ..., item_n), stmt)
      =>#   parenthesized list, simple version
      =>define(`foreach', `pushdef(`$1')_foreach($@)popdef(`$1')')
      =>define(`_arg1', `$1')
      =>define(`_foreach', `ifelse(`$2', `()', `',
      =>  `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
      =>divert`'dnl
 
    Unfortunately, that implementation is not robust to macro names as
 list elements.  Each iteration of '_foreach' is stripping another layer
 of quotes, leading to erratic results if list elements are not already
 fully expanded.  The first cut at implementing 'foreachq' takes this
 into account.  Also, when using quoted elements in a PAREN-LIST, the
 overall list must be quoted.  A QUOTE-LIST has the nice property of
 requiring fewer characters to create a list containing the same quoted
 elements.  To see the difference between the two macros, we attempt to
 pass double-quoted macro names in a list, expecting the macro name on
 output after one layer of quotes is removed during list iteration and
 the final layer removed during the final rescan:
 
      $ m4 -I examples
      define(`a', `1')define(`b', `2')define(`c', `3')
      =>
      include(`foreach.m4')
      =>
      include(`foreachq.m4')
      =>
      foreach(`x', `(``a'', ``(b'', ``c)'')', `x
      ')
      =>1
      =>(2)1
      =>
      =>, x
      =>)
      foreachq(`x', ```a'', ``(b'', ``c)''', `x
      ')dnl
      =>a
      =>(b
      =>c)
 
    Obviously, 'foreachq' did a better job; here is its implementation:
 
      $ m4 -I examples
      undivert(`foreachq.m4')dnl
      =>include(`quote.m4')dnl
      =>divert(`-1')
      =># foreachq(x, `item_1, item_2, ..., item_n', stmt)
      =>#   quoted list, simple version
      =>define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')')
      =>define(`_arg1', `$1')
      =>define(`_foreachq', `ifelse(quote($2), `', `',
      =>  `define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')')
      =>divert`'dnl
 
    Notice that '_foreachq' had to use the helper macro 'quote' defined
 earlier (⇒Shift), to ensure that the embedded 'ifelse' call does
 not go haywire if a list element contains a comma.  Unfortunately, this
 implementation of 'foreachq' has its own severe flaw.  Whereas the
 'foreach' implementation was linear, this macro is quadratic in the
 number of list elements, and is much more likely to trip up the limit
 set by the command line option '--nesting-limit' (or '-L', ⇒
 Invoking m4 Limits control.).  Additionally, this implementation does
 not expand 'defn(`ITERATOR')' very well, when compared with 'foreach'.
 
      $ m4 -I examples
      include(`foreach.m4')include(`foreachq.m4')
      =>
      foreach(`name', `(`a', `b')', ` defn(`name')')
      => a b
      foreachq(`name', ``a', `b'', ` defn(`name')')
      => _arg1(`a', `b') _arg1(shift(`a', `b'))
 
    It is possible to have robust iteration with linear behavior and sane
 ITERATOR contents for either list style.  See if you can learn from the
 best elements of both of these implementations to create robust macros
 (or ⇒Answers Improved foreach.).