(m4.info)Improved copy


Next: Improved m4wrap Prev: Improved foreach Up: Answers
Enter node , (file) or (file)node

17.4 Solution for 'copy'
========================

The macro 'copy' presented above is unable to handle builtin tokens with
M4 1.4.x, because it tries to pass the builtin token through the macro
'curry', where it is silently flattened to an empty string (Note:
Composition).  Rather than using the problematic 'curry' to work
around the limitation that 'stack_foreach' expects to invoke a macro
that takes exactly one argument, we can write a new macro that lets us
form the exact two-argument 'pushdef' call sequence needed, so that we
are no longer passing a builtin token through a text macro.

 -- Composite: stack_foreach_sep (MACRO, PRE, POST, SEP)
 -- Composite: stack_foreach_sep_lifo (MACRO, PRE, POST, SEP)
     For each of the 'pushdef' definitions associated with MACRO, expand
     the sequence 'PRE`'definition`'POST'.  Additionally, expand SEP
     between definitions.  'stack_foreach_sep' visits the oldest
     definition first, while 'stack_foreach_sep_lifo' visits the current
     definition first.  The expansion may dereference MACRO, but should
     not modify it.  There are a few special macros, such as 'defn',
     which cannot be used as the MACRO parameter.

   Note that 'stack_foreach(`MACRO', `ACTION')' is equivalent to
'stack_foreach_sep(`MACRO', `ACTION(', `)')'.  By supplying explicit
parentheses, split among the PRE and POST arguments to
'stack_foreach_sep', it is now possible to construct macro calls with
more than one argument, without passing builtin tokens through a macro
call.  It is likewise possible to directly reference the stack
definitions without a macro call, by leaving PRE and POST empty.  Thus,
in addition to fixing 'copy' on builtin tokens, it also executes with
fewer macro invocations.

   The new macro also adds a separator that is only output after the
first iteration of the helper '_stack_reverse_sep', implemented by
prepending the original SEP to PRE and omitting a SEP argument in
subsequent iterations.  Note that the empty string that separates SEP
from PRE is provided as part of the fourth argument when originally
calling '_stack_reverse_sep', and not by writing '$4`'$3' as the third
argument in the recursive call; while the other approach would give the
same output, it does so at the expense of increasing the argument size
on each iteration of '_stack_reverse_sep', which results in quadratic
instead of linear execution time.  The improved stack walking macros are
available in 'm4-1.4.18/examples/stack_sep.m4':

     $ m4 -I examples
     include(`stack_sep.m4')
     =>
     define(`copy', `ifdef(`$2', `errprint(`$2 already defined
     ')m4exit(`1')',
        `stack_foreach_sep(`$1', `pushdef(`$2',', `)')')')dnl
     pushdef(`a', `1')pushdef(`a', defn(`divnum'))
     =>
     copy(`a', `b')
     =>
     b
     =>0
     popdef(`b')
     =>
     b
     =>1
     pushdef(`c', `1')pushdef(`c', `2')
     =>
     stack_foreach_sep_lifo(`c', `', `', `, ')
     =>2, 1
     undivert(`stack_sep.m4')dnl
     =>divert(`-1')
     =># stack_foreach_sep(macro, pre, post, sep)
     =># Invoke PRE`'defn`'POST with a single argument of each definition
     =># from the definition stack of MACRO, starting with the oldest, and
     =># separated by SEP between definitions.
     =>define(`stack_foreach_sep',
     =>`_stack_reverse_sep(`$1', `tmp-$1')'dnl
     =>`_stack_reverse_sep(`tmp-$1', `$1', `$2`'defn(`$1')$3', `$4`'')')
     =># stack_foreach_sep_lifo(macro, pre, post, sep)
     =># Like stack_foreach_sep, but starting with the newest definition.
     =>define(`stack_foreach_sep_lifo',
     =>`_stack_reverse_sep(`$1', `tmp-$1', `$2`'defn(`$1')$3', `$4`'')'dnl
     =>`_stack_reverse_sep(`tmp-$1', `$1')')
     =>define(`_stack_reverse_sep',
     =>`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0(
     =>  `$1', `$2', `$4$3')')')
     =>divert`'dnl


automatically generated by info2www version 1.2.2.9