Macros allow you to create new special forms at runtime. Unlike a function, the arguments to a macro are not evaluated. The result of evaluating the body of the macro is then itself evaluated.
Note: these are (essentially) Common LISP macros. Scheme has a different macro system, which avoids problems with identifiers introduced by the macro, but is more complex.
We will define macros using the following syntax:
(DEFMACRO (name arg...) body...)This matches our
DEFINE
syntax for functions, but is
slightly different from the form used in Common LISP.
Take the macro IGNORE
defined by:
(DEFMACRO (IGNORE X) (CONS 'QUOTE (CONS X NIL)))
If we then evaluate the expression
(IGNORE FOO)where
FOO
need not be bound, the body of IGNORE
will first be evaluated with the argument X
bound to the
unevaluated symbol FOO
. The result of evaluating
the nested CONS
expressions within this environment is:
(QUOTE . (FOO . NIL))which is of course equivalent to:
(QUOTE FOO)Finally, evaluating this value (which is the result of evaluating the macro body) gives us:
FOO
We will define a new type of atom:
AtomType_Macrothe value of which is the same as
AtomType_Closure
.
And now simply teach eval_expr
about our new macro
type.
int eval_expr(Atom expr, Atom env, Atom *result) { . . . if (op.type == AtomType_Symbol) { if (strcmp(op.value.symbol, "QUOTE") == 0) { . . . } else if (strcmp(op.value.symbol, "DEFMACRO") == 0) { Atom name, macro; Error err; if (nilp(args) || nilp(cdr(args))) return Error_Args; if (car(args).type != AtomType_Pair) return Error_Syntax; name = car(car(args)); if (name.type != AtomType_Symbol) return Error_Type; err = make_closure(env, cdr(car(args)), cdr(args), ¯o); if (err) return err; macro.type = AtomType_Macro; *result = name; return env_set(env, name, macro); } } /* Evaluate operator */ . . . /* Is it a macro? */ if (op.type == AtomType_Macro) { Atom expansion; op.type = AtomType_Closure; err = apply(op, args, &expansion); if (err) return err; return eval_expr(expansion, env, result); } /* Evaulate arguments */ . . . }
> (defmacro (ignore x) (cons 'quote (cons x nil))) IGNORE > (ignore foo) FOO > foo Symbol not bound
We will use macros in the future to define some new special forms.