1+ #lang racket/base
2+
3+ (define (MAKE-GLOBAL-ENV) (list
4+ (list (quote NOT) not)
5+ (list (quote EQ?) eq?)
6+ (list (quote EQUAL?) equal?)
7+ (list (quote ADD) +)
8+ (list (quote SUB) -)
9+ (list (quote MUL) *)
10+ (list (quote DIV) /)
11+ (list (quote MAP) map)
12+ (list (quote LIST) list)
13+ (list (quote APPEND) append)
14+ ))
15+
16+ (define GLOBAL-ENV (MAKE-GLOBAL-ENV))
17+
18+ (define (LOOKUP name env)
19+ (cond
20+ ((null? env) #f )
21+ ((equal? name (car (car env))) (car (cdr (car env))))
22+ (else (LOOKUP name (cdr env)))))
23+
24+ (define (REST list) (cdr list))
25+ (define (FIRST list) (car list))
26+ (define (SECOND list) (car (cdr list)))
27+ (define (THIRD list) (car (cdr (cdr list))))
28+ (define (FOURTH list) (car (cdr (cdr (cdr list)))))
29+
30+ (define (EVLIS exps env)
31+ (if (null? exps)
32+ (quote ())
33+ (cons (EVAL (FIRST exps) env)
34+ (EVLIS (REST exps) env))
35+ )
36+ )
37+
38+ (define (EVAL exp env)
39+ (cond
40+ ((number? exp) exp)
41+ ((symbol? exp) (LOOKUP exp env))
42+ ((equal? (FIRST exp) (quote QUOTE))
43+ (SECOND exp) )
44+ ((equal? (FIRST exp) (quote IF))
45+ (if (EVAL (SECOND exp) env)
46+ (EVAL (THIRD exp) env)
47+ (EVAL (FOURTH exp) env)))
48+ ((equal? (FIRST exp) (quote LAMBDA))
49+ (MAKE-PROCEDURE (SECOND exp) (THIRD exp) env))
50+ ((equal? (FIRST exp) (quote DEFINE-GLOBAL))
51+ (set! GLOBAL-ENV (cons (list (SECOND exp) (EVAL (THIRD exp) env)) GLOBAL-ENV)))
52+ (else
53+ (apply (EVAL (FIRST exp) env) (EVLIS (REST exp) env)))))
54+
55+ (define (MAKE-PROCEDURE parms body definition-env)
56+ (lambda (args)
57+ (EVAL body
58+ (append
59+ (map list parms (list args))
60+ definition-env))
61+ )
62+ )
63+
64+ (require rackunit)
65+
66+ ; LOOKUP
67+ (check-equal? (LOOKUP 'ADD GLOBAL-ENV) + "Existing name " )
68+ (check-equal? (LOOKUP 'NO-SUCH-NAME GLOBAL-ENV) #f "Non-existing name " )
69+
70+ ; EVAL
71+ (check-equal? (EVAL '1 '() ) 1 "Number literal " )
72+ (check-equal? (EVAL 'ADD GLOBAL-ENV) + "Built-in procedure " )
73+ (check-equal? (EVAL '(QUOTE (A (B C))) GLOBAL-ENV) '(A (B C)) "QUOTE form " )
74+ (check-equal? (EVAL '(IF (EQUAL? 3 3 ) 1 0 ) GLOBAL-ENV) 1 "IF form, consequence branch " )
75+ (check-equal? (EVAL '(IF (EQUAL? 3 4 ) 1 0 ) GLOBAL-ENV) 0 "IF form, alternative branch " )
76+ (check-true (procedure? (EVAL '(LAMBDA (N) (MUL N 2 )) GLOBAL-ENV)) "LAMBDA form, definition " )
77+ (check-equal? (EVAL '((LAMBDA (N) (MUL N 2 )) 4 ) GLOBAL-ENV) 8 "LAMBDA form, application " )
78+ (check-equal? (begin (EVAL '(DEFINE-GLOBAL X 8 ) GLOBAL-ENV) (FIRST GLOBAL-ENV)) '(X 8 )) ; how do I inspect the GLOBAL-ENV?
79+ ; I don't yet know how to write DEFINE, because it would need to update the local environment
80+ (check-equal? (begin (EVAL '(DEFINE-GLOBAL X2 (LAMBDA (N) (MUL N 2 ))) GLOBAL-ENV) (EVAL '(X2 8 ) GLOBAL-ENV)) 16 )
81+
82+
0 commit comments