We will define some additional syntax to facilitate entry of some common expressions. Recall that we already allow the user to enter
(A B C)instead of
(A . (B . (C . NIL)))
In order to include a literal symbol or list in an expression, we need
to use the QUOTE
operator. As a shortcut, we will
define
'EXPRto be equivalent to
(QUOTE EXPR)
So for example the following forms are equivalent:
Abbreviation | Canonical form | Evaluates to |
---|---|---|
'FOO |
(QUOTE FOO) |
FOO |
'(+ 1 2) |
(QUOTE (+ 1 2)) |
(+ 1 2) |
'(A . B) |
(QUOTE (A . B)) |
(A . B) |
The lexer needs to know that the quote mark is a prefix (i.e., it can appear immediately before another token but is not necessarily a delimeter).
int lex(const char *str, const char **start, const char **end) { const char *ws = " \t\n"; const char *delim = "() \t\n"; const char *prefix = "()\'"; . . . }
Also read_expr
must convert it to the correct list
expresssion.
int read_expr(const char *input, const char **end, Atom *result) { const char *token; Error err; err = lex(input, &token, end); if (err) return err; if (token[0] == '(') { return read_list(*end, end, result); } else if (token[0] == ')') { return Error_Syntax; } else if (token[0] == '\'') { *result = cons(make_sym("QUOTE"), cons(nil, nil)); return read_expr(*end, end, &car(cdr(*result))); } else { return parse_simple(token, *end, result); } }
> (define x '(a b c)) X > x (A B C) > 'x X > (define foo 'bar) FOO > foo BAR > ''() (QUOTE NIL)
It is cumbersome to have to type a lambda expression every time we wish
to define a function, so we will modify the DEFINE
operator
to avoid this.
(DEFINE (name args...) body...)is equivalent to
(DEFINE name (LAMBDA (args...) body...))
Here's how:
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, "DEFINE") == 0) { Atom sym, val; if (nilp(args) || nilp(cdr(args))) return Error_Args; sym = car(args); if (sym.type == AtomType_Pair) { err = make_closure(env, cdr(sym), cdr(args), &val); sym = car(sym); if (sym.type != AtomType_Symbol) return Error_Type; } else if (sym.type == AtomType_Symbol) { if (!nilp(cdr(cdr(args)))) return Error_Args; err = eval_expr(car(cdr(args)), env, &val); } else { return Error_Type; } if (err) return err; *result = sym; return env_set(env, sym, val); } else if (strcmp(op.value.symbol, "LAMBDA") == 0) { . . . } } . . . }
> (define (square x) (* x x)) SQUARE > (square 3) 9
Sweet!