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