I converted the Structure and Interpretation of Computer Programs (SICP) version of the meta-circular evaluator to Clojure. The main difference (besides syntax) is the handling of the environment structure. Since you cannot use set-car!
and set-cdr!
in Clojure, these are implemented via an atom holding a map (copied from the code of Greg Sexton's chapter 4 notes on GitHub).
The code of the two evaluators can be found here:
The main procedure eval
does a case analysis and then decides what to do next with the expression exp
in the environment env
:
(defn eval [exp env]
(cond (self-evaluating? exp) exp
(variable? exp) (lookup-variable-value exp env)
(quoted? exp) (text-of-quotation exp)
(assignment? exp) (eval-assignment exp env)
(definition? exp) (eval-definition exp env)
(if? exp) (eval-if exp env)
(lambda? exp) (make-procedure (lambda-parameters exp)
(lambda-body exp)
env)
(begin? exp) (eval-sequence (begin-actions exp) env)
(cond? exp) (eval (cond->if exp) env)
(application? exp) (apply (eval (operator exp) env)
(list-of-values (operands exp) env))
:else (throw (Throwable. (str "Unknown expression type \"" exp "\" -- EVAL")))))
When interacting with the Clojure evaluator you can do things like:
;;; Eval input:
(defn hello-string hello)
;;; Eval value:
< environment map >
;;; Eval input:
hello-string
;;; Eval value:
hello
This shows new frames can be stored in and retrieved from the environment.
When the environment is setup initially, true
and false
are explicitly added:
(defn setup-environment []
(let [initial-env
(extend-environment primitive-procedure-names
primitive-procedure-objects
the-empty-environment)]
(define-variable! 'true true initial-env)
(define-variable! 'false false initial-env)
initial-env))
But when an if-expression is entered, the code fails because it cannot find "true". (Same happens if you just evaluate true
, which in the Scheme version evaluates to #t
).
;;; Eval input:
(if true hello-string "hi")
CompilerException java.lang.Throwable: Unknown expression type "true" --
EVAL, compiling:(/home/erooijak/clojure/scheme-interpreter/scheme-
evaluator.clj:314:1)
(I would expect this to be evaluated to "hello")
Since eval-if
works properly in the Scheme version (and does not work if true
and false
are not added to setup-environment
, it looks like eval
does not interpret true
as something that needs to be looked up in the environment in the Clojure version.
Unfortunately, I do not see exactly how this looking-up happens in the Scheme version and why it does not happen in the Clojure version.
I hope someone can steer me in the right direction on why evaluating true
works in the Scheme, but not in the Clojure implementation of the meta-circular evaluator.