1.1 Invoking And Defining Macros

Oz macros are invoked with the following new syntax:

<<repeat 5 {Show hello}>>

angle brackets delimit a sequence of phrases (expressions and statements). The first element should be an atom (here repeat) and names the macro to be invoked.

When the compiler encounters a macro, it invokes the macro-expansion facility. The latter eventually returns a syntactic representation that is guaranteed to no longer contain any macro invocation.

A macro is implemented by a macro expander. This is a function of two arguments: (1) a macro form (a syntactic representation), (2) a macro environment. It returns a transformed syntactic representation. Syntactic representations of Oz code use the ``Syntax Tree Format'' which is described in Appendix C of ``The Mozart Compiler''.

Let's define a very simple macro:

<<unless B S1 S2 S3>>

which executes the body S1 S2 S3 iff B evaluates to false.i. e. it is precisely equivalent to:

if B then skip else S1 S2 S3 end

Here is the code for the expander:

declare 
fun {UnlessExpander fMacro(M|B|L C) Env}
   fBoolCase(
      B
      fSkip(unit)
      {Macro.listToSequence L}
      C)
end

The first argument fMacro(M|B|L C) is the macro call form. M is the representation of the atom which is the first element of the macro invocation (here fAtom(unless C2), where C2 is a coordinate indicating the provenance and location of the token in the input). B is the 2nd element of the macro call and represents the boolean condition. L is the list of the remainder of the macro call and represents the body. C is also a coordinate. Env is a macro environment and can usually be ignored.

{Macro.listToSequence L} turns the list L of instructions into the syntactic representation of a sequence. Module Macro is described in Section 1.2.

We must now globally register the macro expander:

{Macro.defmacro unless UnlessExpander}

Here is a test:

declare 
proc {DO X Y}
   <<unless X<Y {Show X} {Show Y}>> 
end

{DO 2 5} prints nothing while {DO 5 2} prints 5 then 2.

A common mistake while using the interactive development environment is to feed a buffer that contains both macro definitions and uses of these macros. This cannot work: when the code in the buffer is being compiled the macros are not yet defined. They become defined only after execution of the code. Thus you must first feed your macro definitions, and only then feed any code that makes use of them.


Denys Duchier
Version 1.4.0 (20100209)