m4: Improved copy

1 
1 17.4 Solution for 'copy'
1 ========================
1 
1 The macro 'copy' presented above is unable to handle builtin tokens with
1 M4 1.4.x, because it tries to pass the builtin token through the macro
11 'curry', where it is silently flattened to an empty string (⇒
 Composition).  Rather than using the problematic 'curry' to work
1 around the limitation that 'stack_foreach' expects to invoke a macro
1 that takes exactly one argument, we can write a new macro that lets us
1 form the exact two-argument 'pushdef' call sequence needed, so that we
1 are no longer passing a builtin token through a text macro.
1 
1  -- Composite: stack_foreach_sep (MACRO, PRE, POST, SEP)
1  -- Composite: stack_foreach_sep_lifo (MACRO, PRE, POST, SEP)
1      For each of the 'pushdef' definitions associated with MACRO, expand
1      the sequence 'PRE`'definition`'POST'.  Additionally, expand SEP
1      between definitions.  'stack_foreach_sep' visits the oldest
1      definition first, while 'stack_foreach_sep_lifo' visits the current
1      definition first.  The expansion may dereference MACRO, but should
1      not modify it.  There are a few special macros, such as 'defn',
1      which cannot be used as the MACRO parameter.
1 
1    Note that 'stack_foreach(`MACRO', `ACTION')' is equivalent to
1 'stack_foreach_sep(`MACRO', `ACTION(', `)')'.  By supplying explicit
1 parentheses, split among the PRE and POST arguments to
1 'stack_foreach_sep', it is now possible to construct macro calls with
1 more than one argument, without passing builtin tokens through a macro
1 call.  It is likewise possible to directly reference the stack
1 definitions without a macro call, by leaving PRE and POST empty.  Thus,
1 in addition to fixing 'copy' on builtin tokens, it also executes with
1 fewer macro invocations.
1 
1    The new macro also adds a separator that is only output after the
1 first iteration of the helper '_stack_reverse_sep', implemented by
1 prepending the original SEP to PRE and omitting a SEP argument in
1 subsequent iterations.  Note that the empty string that separates SEP
1 from PRE is provided as part of the fourth argument when originally
1 calling '_stack_reverse_sep', and not by writing '$4`'$3' as the third
1 argument in the recursive call; while the other approach would give the
1 same output, it does so at the expense of increasing the argument size
1 on each iteration of '_stack_reverse_sep', which results in quadratic
1 instead of linear execution time.  The improved stack walking macros are
1 available in 'm4-1.4.18/examples/stack_sep.m4':
1 
1      $ m4 -I examples
1      include(`stack_sep.m4')
1      =>
1      define(`copy', `ifdef(`$2', `errprint(`$2 already defined
1      ')m4exit(`1')',
1         `stack_foreach_sep(`$1', `pushdef(`$2',', `)')')')dnl
1      pushdef(`a', `1')pushdef(`a', defn(`divnum'))
1      =>
1      copy(`a', `b')
1      =>
1      b
1      =>0
1      popdef(`b')
1      =>
1      b
1      =>1
1      pushdef(`c', `1')pushdef(`c', `2')
1      =>
1      stack_foreach_sep_lifo(`c', `', `', `, ')
1      =>2, 1
1      undivert(`stack_sep.m4')dnl
1      =>divert(`-1')
1      =># stack_foreach_sep(macro, pre, post, sep)
1      =># Invoke PRE`'defn`'POST with a single argument of each definition
1      =># from the definition stack of MACRO, starting with the oldest, and
1      =># separated by SEP between definitions.
1      =>define(`stack_foreach_sep',
1      =>`_stack_reverse_sep(`$1', `tmp-$1')'dnl
1      =>`_stack_reverse_sep(`tmp-$1', `$1', `$2`'defn(`$1')$3', `$4`'')')
1      =># stack_foreach_sep_lifo(macro, pre, post, sep)
1      =># Like stack_foreach_sep, but starting with the newest definition.
1      =>define(`stack_foreach_sep_lifo',
1      =>`_stack_reverse_sep(`$1', `tmp-$1', `$2`'defn(`$1')$3', `$4`'')'dnl
1      =>`_stack_reverse_sep(`tmp-$1', `$1')')
1      =>define(`_stack_reverse_sep',
1      =>`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0(
1      =>  `$1', `$2', `$4$3')')')
1      =>divert`'dnl
1