Next: Improved m4wrap, Previous: Improved foreach, Up: Answers [Contents][Index]
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 (see 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.
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.19/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
Next: Improved m4wrap, Previous: Improved foreach, Up: Answers [Contents][Index]