Next: , Previous: Foreach, Up: Conditionals


6.6 Working with definition stacks

Thanks to pushdef, manipulation of a stack is an intrinsic operation in m4. Normally, only the topmost definition in a stack is important, but sometimes, it is desirable to manipulate the entire definition stack.

— Composite: stack_foreach (macro, action)
— Composite: stack_foreach_lifo (macro, action)

For each of the pushdef definitions associated with macro, invoke the macro action with a single argument of that definition. stack_foreach visits the oldest definition first, while stack_foreach_lifo visits the current definition first. action should not modify or dereference macro. There are a few special macros, such as defn, which cannot be used as the macro parameter.

A sample implementation of these macros is distributed in the file m4-1.4.13/examples/stack.m4.

     $ m4 -I examples
     include(`stack.m4')
     ⇒
     pushdef(`a', `1')pushdef(`a', `2')pushdef(`a', `3')
     ⇒
     define(`show', ``$1'
     ')
     ⇒
     stack_foreach(`a', `show')dnl
     ⇒1
     ⇒2
     ⇒3
     stack_foreach_lifo(`a', `show')dnl
     ⇒3
     ⇒2
     ⇒1

Now for the implementation. Note the definition of a helper macro, _stack_reverse, which destructively swaps the contents of one stack of definitions into the reverse order in the temporary macro ‘tmp-$1’. By calling the helper twice, the original order is restored back into the macro ‘$1’; since the operation is destructive, this explains why ‘$1’ must not be modified or dereferenced during the traversal. The caller can then inject additional code to pass the definition currently being visited to ‘$2’. The choice of helper names is intentional; since ‘-’ is not valid as part of a macro name, there is no risk of conflict with a valid macro name, and the code is guaranteed to use defn where necessary. Finally, note that any macro used in the traversal of a pushdef stack, such as pushdef or defn, cannot be handled by stack_foreach, since the macro would temporarily be undefined during the algorithm.

     $ m4 -I examples
     undivert(`stack.m4')dnl
     ⇒divert(`-1')
     ⇒# stack_foreach(macro, action)
     ⇒# Invoke ACTION with a single argument of each definition
     ⇒# from the definition stack of MACRO, starting with the oldest.
     ⇒define(`stack_foreach',
     ⇒`_stack_reverse(`$1', `tmp-$1')'dnl
     ⇒`_stack_reverse(`tmp-$1', `$1', `$2(defn(`$1'))')')
     ⇒# stack_foreach_lifo(macro, action)
     ⇒# Invoke ACTION with a single argument of each definition
     ⇒# from the definition stack of MACRO, starting with the newest.
     ⇒define(`stack_foreach_lifo',
     ⇒`_stack_reverse(`$1', `tmp-$1', `$2(defn(`$1'))')'dnl
     ⇒`_stack_reverse(`tmp-$1', `$1')')
     ⇒define(`_stack_reverse',
     ⇒`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0($@)')')
     ⇒divert`'dnl