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.