--- /dev/null
+; this allows (begin), which is almost certainly undesirable behavior
+(define (eprogn exps env trace)
+ (if (pair? exps)
+ (if (pair? (cdr exps))
+ (begin (evaluate (car exps) env trace)
+ (eprogn (cdr exps) env trace))
+ (evaluate (car exps env trace)))
+ '()))
+
+; Complication is to avoid excess recursion (exercise 1.2)
+(define (evlis exps env trace)
+ (define (evlis-internal exps env trace)
+ (let ((current-result (evaluate (car exps) env trace)))
+ (cons current-result (if (pair? (cdr exps))
+ (evlis (cdr exps) env trace)
+ '()))))
+ (if (pair? exps)
+ (evlis-internal exps env trace)
+ '()))
+
+(define (lookup id env)
+ (if (pair? env)
+ (if (eq? (caar env) id)
+ (cdar env)
+ (lookup id (cdr env)))
+ (wrong "No such binding" id)))
+
+(define (update! id env value)
+ (if (pair? env)
+ (if (eq? (caar env) id)
+ (begin (set-cdr! (car env) value)
+ value)
+ (update! id (cdr env) value))
+ (wrong "No such binding" id)))
+
+(define (extend env variables values)
+ (cond ((pair? variables) (if (pair? values)
+ (cons (cons (car variables) (car values))
+ (extend env (cdr variables) (cdr values)))
+ (wrong "Too many variables")))
+ ((null? variables) (if (null? values)
+ env
+ (wrong "Too many values")))
+ ; I'm not sure I like this, it creates an inconsistent
+ ; interface for extend
+ ((symbol? variables?) (cons (cons variables values) env))))
+
+(define (invoke fn args)
+ (if (procedure? fn)
+ (fn args)
+ (wrong "Not a function" fn)))
+
+; variables are lexically scoped
+(define (make-function variables body env trace)
+ (lambda (values) (eprogn body (extend env variables values) trace)))
+
+(define env.init '())
+
+(define env.global env.init)
+
+(define-syntax def-initial
+ (syntax-rules ()
+ ((def-initial name) (begin (set! env.global (cons (cons 'name 'void) env.global))
+ 'name))
+ ((def-initial name value) (begin (set! env.global (cons (cons 'name value) env.global))
+ 'name))))
+
+(define-syntax def-primitive
+ (syntax-rules ()
+ ((def-primitive name value arity) (def-initial name (lambda (values) (if (= arity (length values))
+ (apply value values) ; the real apply of Scheme
+ (wrong "Incorrect arity" (list 'name values))))))))
+
+
+; Implementing the exercise suggested in section 1.8
+(define exit-sentinel (gensym 'exit-sentinel-))
+(define (exit) exit-sentinel)
+
+(def-initial true #t)
+(def-initial false #f)
+(def-initial nil '())
+
+(def-primitive cons cons 2)
+(def-primitive car car 1)
+(def-primitive cdr cdr 1)
+(def-primitive set-car! set-car! 2)
+(def-primitive set-cdr! set-cdr! 2)
+(def-primitive + + 2)
+(def-primitive - - 2)
+(def-primitive * * 2)
+(def-primitive / / 2)
+(def-primitive eq? eq? 2)
+(def-primitive < < 2)
+(def-primitive > > 2)
+(def-primitive <= <= 2)
+(def-primitive >= >= 2)
+(def-primitive exit exit 0)
+
+(define (atom? e)
+ (not (pair? e)))
+
+(define (display-args args)
+ (if (pair? args)
+ (begin (display " ")
+ (display (car args))
+ (display-args (cdr args)))))
+
+; `trace` is there to implement exercise 1.1
+; Pass #t to output trace information
+(define (evaluate e env trace)
+ (if (atom? e)
+ (cond ((symbol? e) (lookup e env))
+ ((or (number? e) (string? e) (char? e) (boolean? e) (vector? e)) e)
+ (else (wrong "Cannot evaluate" e)))
+ (case (car e)
+ ((quote) (cadr e))
+ ((if) (if (evaluate (cadr e) env trace)
+ (evaluate (caddr e) env trace)
+ (evaluate (cadddr e) env trace)))
+ ((begin) (eprogn (cdr e) env trace))
+ ((set!) (update! (cadr e) env (evaluate (caddr e) env trace)))
+ ((lambda) (make-function (cadr e) (cddr e) env trace))
+ (else
+ (let ((fn (evaluate (car e) env trace))
+ (args (evlis (cdr e) env trace)))
+ (begin (if trace
+ (begin (display "(")
+ (display (car e))
+ (display-args args)
+ (display ")\n")))
+ (let ((result (invoke fn args)))
+ (begin (display result)
+ (display "\n")
+ result))))))))
+
+(define (chapter1-scheme trace)
+ (define (toplevel)
+ (let ((result (evaluate (read) env.global trace)))
+ (if (not (and (symbol? result) (equal? result exit-sentinel)))
+ (begin (display result)
+ (display "\n")
+ (toplevel)))))
+ (toplevel))