Macro System Extensions#

Traced Macros#

Macros can be defined with an adjective, traced. This causes the macro expander to output some useful information during compilation:

define traced macro when2
  { when2 (?cond:expression) ?:body end } => { if (?cond) ?body end }
end;

when2 (23)
  format-out("Hello, world!\n");
end;

Compiling this will result in this output from the compiler:

{ when2 } > when2 (23) format-out ("Hello, world!\n"); end
{ when2 } < if (23) format-out ("Hello, world!\n"); end

Template Calls#

Note

This extension was originally described in an appendix to D-Expressions: Lisp Power, Dylan Style. This text is largely copied from there.

A number of limitations of local rewrite rules require resorting to top-level auxiliary macros. The first problem is that local rewrite rules do not have access to pattern variables defined in main rule sets. The usual solution is to employ an auxiliary macro which takes those needed variables as extra macro arguments. For example, suppose we want to add a prefix option to allow the creation of properties with a common prefix. We need to introduce an auxiliary macro so that the prefix variable can be in scope when iterating over the properties. For example, consider the following:

define macro properties-definer
  { define properties ?kind:name
         prefixed-by ?prefix:name ?properties:* end }
    => { define variable ?kind
           = concatenate
               (prefixed-props (?"prefix") ?properties end) }
  { define properties ?kind:name ?properties:* end }
    => { define variable ?kind
           = concatenate
               (prefixed-props ("") ?properties end) }
end macro;

define macro prefixed-props
  { prefixed-props (?prefix:name) end }
    => { #() }
  { prefixed-props (?prefix:name) ?prop:name; ?more:* end }
    => { list(?prefix ## ?prop),
         prefixed-props (?prefix) ?more end) }
end macro;

Auxiliary macros are also needed when pattern variables must be walked in two different ways. Consider a macro, called iterate, for creating internally recursive procedures that are as convenient as loops. It has the following basic form:

iterate name (variable = init, ...)
  body
end iterate

and has the semantics of evaluating the body with the variables initially bound to the inits and such that any call to name inside the body recursively calls the procedure with the variables bound to the arguments of the call. In writing a macro for iterate, the parenthesized fragments must be walked once to extract variables and another time to extract initial values. Unfortunately, with the current macro system, there is no way to walk the same pattern variable with more than one set of local rewrite rules. Instead, an extra copy of the pattern variable must be made and passed on to an auxiliary macro:

define macro iterate
  { iterate ?loop:name (?args:*) ?:body end }
    => { iterate-aux ?loop (?args) (?args)
           ?body
         end }
end macro iterate;

define macro iterate-aux
  { iterate-aux ?loop:name (?args) (?inits) ?:body end }
    => { local method ?loop (?args) ?body end;
         ?loop(?inits) }

args:
  { }
    => { }
  { ?:variable = ?:expression, ... }
    => { ?variable, ... }

inits:
  { }
    => { }
  { ?:variable = ?:expression, ... }
    => { ?expression, ... }
end macro iterate-aux;

Both of these reasons for needing auxiliary macros are somewhat artificial because in fact local rewrite rules are really like local functions and should allow extra arguments and should be directly callable on any pattern variable. The problem lies in the fact that the local rewrite rule is artificially tied to one pattern variable by virtue of its name.

In order to overcome this problem, we introduce a direct template call, obviating the need for auxiliary macros in many cases. This leads to a much more elegant solution to these more complicated macros. A template auxiliary rule set call has the following form:

?@rule-name{ <arbitrary-template-stuff> }

where a new macro punctuation ?@ marks a template call. For example, in the prefixed properties-definer macro, we can now directly invoke a local rewrite rule set with extra arguments:

define macro properties-definer
  { define properties ?kind:name
         prefixed-by ?prefix:name ?properties:* end }
    => { define variable ?kind
           = concatenate
               (?@prefixed-properties{ ?"prefix"; ?properties }) }
  { define properties ?kind:name ?properties:* end }
    => { define variable ?kind
           = concatenate(?@prefixed-properties{ ""; ?properties } }

prefixed-properties:
  { ?prefix:name }
    => { #() }
  { ?prefix:name; ?property:name; ?more:* }
    => { list(?prefix ## ?property),
         ?@prefixed-properties{ ?prefix; ?more } }
end macro;

Similarly, iterate can now be written without auxiliary macros using two template calls:

define macro iterate2
  { iterate2 ?:name (?bindings:*) ?:body end }
    => { local method ?name (?@vars{ ?bindings }) ?body end;
         ?name(?@inits{ ?bindings }) }

vars:
  { }
    => { }
  { ?:variable = ?:expression, ... }
    => { ?variable, ... }

inits:
  { }
    => { }
  { ?:variable = ?:expression, ... }
    => { ?expression, ... }
end macro;

We can also introduce a template macro call

?@{ <arbitrary-template-stuff-that-forms-a-macro-call> }

which acts as a kind of shorthand for the :macro constraint and permits the definition of macros for use as shared rewriting tools. For example:

define traced macro mcreverse
  { mcreverse(?list:*) } => { ?list }

list:
  { } => { }
  { ?:expression } => { ?expression }
  { ?:expression, ... } => { ..., ?expression }
end macro;

define traced macro user
  { user(?stuff:*) } => { list(?@{ mcreverse(?stuff) }) }
end macro;

where the traced modifier causes macro expansion to be traced. For example, here’s the trace for user(1, 2, 3):

{ user } > user(1, 2, 3)
{ mcreverse } > mcreverse(1, 2, 3)
{ mcreverse } < 3,2,1
{ user } < list(3,2,1)

Like normal macro calls, a new hygiene context is created for ?@{ } calls, so you could define gensym thusly:

define macro gensym
  { gensym() } => { gensymed-name }
end macro;