m4: Composition

 
 6.7 Building macros with macros
 ===============================
 
 Since m4 is a macro language, it is possible to write macros that can
 build other macros.  First on the list is a way to automate the creation
 of blind macros.
 
  -- Composite: define_blind (NAME, [VALUE])
      Defines NAME as a blind macro, such that NAME will expand to VALUE
      only when given explicit arguments.  VALUE should not be the result
      of 'defn' (⇒Defn).  This macro is only recognized with
      parameters, and results in an empty string.
 
    Defining a macro to define another macro can be a bit tricky.  We
 want to use a literal '$#' in the argument to the nested 'define'.
 However, if '$' and '#' are adjacent in the definition of
 'define_blind', then it would be expanded as the number of arguments to
 'define_blind' rather than the intended number of arguments to NAME.
 The solution is to pass the difficult characters through extra arguments
 to a helper macro '_define_blind'.  When composing macros, it is a
 common idiom to need a helper macro to concatenate text that forms
 parameters in the composed macro, rather than interpreting the text as a
 parameter of the composing macro.
 
    As for the limitation against using 'defn', there are two reasons.
 If a macro was previously defined with 'define_blind', then it can
 safely be renamed to a new blind macro using plain 'define'; using
 'define_blind' to rename it just adds another layer of 'ifelse',
 occupying memory and slowing down execution.  And if a macro is a
 builtin, then it would result in an attempt to define a macro consisting
 of both text and a builtin token; this is not supported, and the builtin
 token is flattened to an empty string.
 
    With that explanation, here's the definition, and some sample usage.
 Notice that 'define_blind' is itself a blind macro.
 
      $ m4 -d
      define(`define_blind', `ifelse(`$#', `0', ``$0'',
      `_$0(`$1', `$2', `$'`#', `$'`0')')')
      =>
      define(`_define_blind', `define(`$1',
      `ifelse(`$3', `0', ``$4'', `$2')')')
      =>
      define_blind
      =>define_blind
      define_blind(`foo', `arguments were $*')
      =>
      foo
      =>foo
      foo(`bar')
      =>arguments were bar
      define(`blah', defn(`foo'))
      =>
      blah
      =>blah
      blah(`a', `b')
      =>arguments were a,b
      defn(`blah')
      =>ifelse(`$#', `0', ``$0'', `arguments were $*')
 
    Another interesting composition tactic is argument "currying", or
 factoring a macro that takes multiple arguments for use in a context
 that provides exactly one argument.
 
  -- Composite: curry (MACRO, ...)
      Expand to a macro call that takes exactly one argument, then
      appends that argument to the original arguments and invokes MACRO
      with the resulting list of arguments.
 
    A demonstration of currying makes the intent of this macro a little
 more obvious.  The macro 'stack_foreach' mentioned earlier is an example
 of a context that provides exactly one argument to a macro name.  But
 coupled with currying, we can invoke 'reverse' with two arguments for
 each definition of a macro stack.  This example uses the file
 'm4-1.4.18/examples/curry.m4' included in the distribution.
 
      $ m4 -I examples
      include(`curry.m4')include(`stack.m4')
      =>
      define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'',
                                `reverse(shift($@)), `$1'')')
      =>
      pushdef(`a', `1')pushdef(`a', `2')pushdef(`a', `3')
      =>
      stack_foreach(`a', `:curry(`reverse', `4')')
      =>:1, 4:2, 4:3, 4
      curry(`curry', `reverse', `1')(`2')(`3')
      =>3, 2, 1
 
    Now for the implementation.  Notice how 'curry' leaves off with a
 macro name but no open parenthesis, while still in the middle of
 collecting arguments for '$1'.  The macro '_curry' is the helper macro
 that takes one argument, then adds it to the list and finally supplies
 the closing parenthesis.  The use of a comma inside the 'shift' call
 allows currying to also work for a macro that takes one argument,
 although it often makes more sense to invoke that macro directly rather
 than going through 'curry'.
 
      $ m4 -I examples
      undivert(`curry.m4')dnl
      =>divert(`-1')
      =># curry(macro, args)
      =># Expand to a macro call that takes one argument, then invoke
      =># macro(args, extra).
      =>define(`curry', `$1(shift($@,)_$0')
      =>define(`_curry', ``$1')')
      =>divert`'dnl
 
    Unfortunately, with M4 1.4.x, 'curry' is unable to handle builtin
 tokens, which are silently flattened to the empty string when passed
 through another text macro.  This limitation will be lifted in a future
 release of M4.
 
    Putting the last few concepts together, it is possible to copy or
 rename an entire stack of macro definitions.
 
  -- Composite: copy (SOURCE, DEST)
  -- Composite: rename (SOURCE, DEST)
      Ensure that DEST is undefined, then define it to the same stack of
      definitions currently in SOURCE.  'copy' leaves SOURCE unchanged,
      while 'rename' undefines SOURCE.  There are only a few macros, such
      as 'copy' or 'defn', which cannot be copied via this macro.
 
    The implementation is relatively straightforward (although since it
 uses 'curry', it is unable to copy builtin macros, such as the second
 definition of 'a' as a synonym for 'divnum'.  See if you can design a
 version that works around this limitation, or ⇒Answers Improved
 copy.).
 
      $ m4 -I examples
      include(`curry.m4')include(`stack.m4')
      =>
      define(`rename', `copy($@)undefine(`$1')')dnl
      define(`copy', `ifdef(`$2', `errprint(`$2 already defined
      ')m4exit(`1')',
         `stack_foreach(`$1', `curry(`pushdef', `$2')')')')dnl
      pushdef(`a', `1')pushdef(`a', defn(`divnum'))pushdef(`a', `2')
      =>
      copy(`a', `b')
      =>
      rename(`b', `c')
      =>
      a b c
      =>2 b 2
      popdef(`a', `c')c a
      => 0
      popdef(`a', `c')a c
      =>1 1