;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; vanilla interpreter   ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define value-of
  (lambda (exp env)
    (match exp
           [`,n #:when (number? n) n]
           [`,y #:when (symbol? y) (env y)]
           [`(lambda (,x) ,body)
            (lambda (arg)
              (value-of body
                        (lambda (y)
                          (if (eqv? y x)
                              arg
                              (env y)))))]
           [`(,rator ,rand)
            ((value-of rator env)
             (value-of rand env))])))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; functional representation ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define empty-env
  (lambda ()
    (lambda (y)
      (error 'empty-env "unbound var ~s" y))))

(define extend-env
  (lambda (x arg env)
    (lambda (y)
      (if (eqv? y x)
          arg
          (env y)))))

(define apply-env
  (lambda (env y)
    (env y)))


(define make-closure
  (lambda (body x env)
    (lambda (arg)
      (value-of-fn body
                   (extend-env x arg env)))))

(define apply-closure
  (lambda (c a)
    (c a)))

(define value-of-fn
  (lambda (exp env)
    (match exp
           [`,n #:when (number? n) n]
           [`,y #:when (symbol? y) (apply-env env y)]
           [`(lambda (,x) ,body)
            (make-closure x body env)]
           [`(,rator ,rand)
            (apply-closure (value-of rator env)
                           (value-of rand env))])))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; data structure representation ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define empty-env-ds
  (lambda () `(empty-env-ds)))

(define extend-env-ds
  (lambda (x arg env)
    `(extended-env ,x ,arg ,env)))

(define apply-env-ds
  (lambda (env y)
    (match env
           [`(empty-env-ds)
            (error `empty-env-ds "unbound var ~s" y)]
           [`(extended-env ,x ,arg ,env)
            (if (eqv? x y) arg (apply-env-ds env y))]
           [else (env y)]))) ;; optional

(define make-closure-ds
  (lambda (x body env)
    `(closure ,x ,body ,env)))

(define apply-closure-ds
  (lambda (c a)
    (match c
           [`(closure ,x ,body ,env)
            (value-of-ds body
                         (extend-env-ds x a env))])))

(define value-of-ds
  (lambda (exp env)
    (match exp
           [`,n #:when (number? n) n]
           [`,y #:when (symbol? y) (apply-env-ds env y)]
           [`(lambda (,x) ,body)
            (make-closure-ds x body env)]
           [`(,rator ,rand)
            (apply-closure-ds (value-of-ds rator env)
                              (value-of-ds rand env))])))