Skip to content

Commit

Permalink
moved to use expectations, add cond and bools
Browse files Browse the repository at this point in the history
  • Loading branch information
thattommyhall committed Mar 22, 2015
1 parent 8f1a012 commit 02f5d51
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 59 deletions.
5 changes: 4 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]])
:dependencies [[org.clojure/clojure "1.6.0"]]
:profiles {:dev {:dependencies [[expectations "1.4.41"]]
:plugins [[lein-expectations "0.0.7"]
[lein-autoexpect "1.4.2"]]}})
98 changes: 75 additions & 23 deletions src/minilisp/core.clj
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
(ns minilisp.core
(:refer-clojure :exclude [fn?]))
(:refer-clojure :exclude [fn? eval true?]))

(declare apply-proc primative-procedure value-of)
(declare apply-proc primative-procedure eval)

(defn error [& msg] (apply println msg))

(defn third [col]
(nth col 2))

;; Identifying expressions
(defn self-evaluating? [exp]
(or (number? exp)
(primative-procedure exp)))
(primative-procedure exp)
(= 'true exp)
(= 'false exp)))

(defn variable? [exp]
(symbol? exp))
Expand All @@ -16,15 +22,15 @@
(and (seq? exp)
(= (first exp) 'def)))

(defn third [col]
(nth col 2))
(def get-env second)

(defn def-name [exp]
(second exp))

(defn def-value [exp]
(third exp))

;; Dealing with applications, ie (<operator> *<operands>)
(defn application? [sexp]
(seq? sexp))

Expand All @@ -34,6 +40,7 @@
(defn operands [sexp]
(rest sexp))

;; Dealing with fns
(defn fn? [exp]
(and (seq? exp)
(= 'fn (first exp))))
Expand All @@ -44,11 +51,45 @@
(defn fn-body [exp]
(third exp))

;; Turn fns to an 'applyable' object
(defn make-procedure [params body env]
{:params params
:body body
:env env})

(def value-of first)

(defn if? [sexp]
(and (seq? sexp)
(= 'if (first sexp))))

(defn true? [sexp]
(if (= 'false sexp)
false
true))

(defn cond? [sexp]
(and (seq? sexp)
(= 'cond (first sexp))))

(defn eval-if [[_ pred consequent alternative] env]
(if (true? (eval pred env))
(eval consequent env)
(eval alternative env)))

(defn pairs->if [[pred consequence & pairs]]
(if (nil? pairs)
(list 'if pred consequence)
(let [tail (pairs->if pairs)]
(list 'if
pred
consequence
tail))))

(defn cond->if [sexp]
(let [pairs (rest sexp)]
(pairs->if pairs)))

(defn eval-sexp
([sexp] (eval-sexp sexp {}))
([sexp env]
Expand All @@ -63,30 +104,41 @@
value (def-value sexp)]
(assoc env name (value-of (eval-sexp value env))))]

(if? sexp)
[(eval-if sexp env)
nil]

(cond? sexp)
(eval-sexp (cond->if sexp) env)

(fn? sexp)
[(make-procedure (fn-params sexp)
(fn-body sexp)
env)
env]

(application? sexp)
[(apply-proc (value-of (eval-sexp (operator sexp) env))
(map (fn [operand]
(value-of (eval-sexp operand env)))
(operands sexp)))
env]

:else
(error "EVAL FAIL" sexp))))
(application? sexp)
[(apply-proc (value-of (eval-sexp (operator sexp) env))
(map (fn [operand]
(value-of (eval-sexp operand env)))
(operands sexp)))
env]

:else
(error "EVAL FAIL" sexp))))

(defn eval
([sexp] (eval sexp {}))
([sexp env]
(value-of (eval-sexp sexp env))))

(defn eval-program [sexps]
(value-of (reduce (fn [[return-value env] sexp]
(eval-sexp sexp env))
[nil {}]
sexps)))

(def value-of first)

(def primative-procedure {'+ + '- - '* * '/ /})

(def compound-procedure? map?)
Expand All @@ -100,11 +152,11 @@
(apply-primative-procedure proc args)

(compound-procedure? proc)
(value-of (eval-sexp (:body proc)
(merge
(:env proc)
(zipmap (:params proc)
args))))

:else
(error "apply-proc error")))
(eval (:body proc)
(merge
(:env proc)
(zipmap (:params proc)
args)))

:else
(error "apply-proc error")))
106 changes: 71 additions & 35 deletions test/minilisp/core_test.clj
Original file line number Diff line number Diff line change
@@ -1,36 +1,72 @@
(ns minilisp.core-test
(:require [clojure.test :refer :all]
[minilisp.core :refer :all])
(:refer-clojure :exclude [eval apply fn?]))

(deftest test-self-evaluating?
(testing "numbers"
(is (= 3 (value-of (eval-sexp '3 {}))))))

(deftest test-variable?
(is (= 2 (value-of (eval-sexp 'a {'a 2})))))

(deftest test-def?
(is (= (second (eval-sexp '(def a 3) {}))
{'a 3})))

(deftest test-apply-promative
(is (= 2 (value-of (eval-sexp '(+ 1 1)))))
(is (= 5 (value-of (eval-sexp '(+ (+ 1 2) 1 1))))))

(deftest test-eval-program
(is (= (eval-program '[3]) 3))
(is (= (eval-program
'[(def a 3)
a])
3)))

(deftest test-fn
(is (= (value-of (eval-sexp '((fn [x] x) 2)))
2))
(is (= (eval-program '[(def f (fn [y] y))
(f 4)])
4))
(is (= (eval-program '[(def square (fn [x] (* x x)))
(+ (square 3) (square 4))])
25)))
(:require
[minilisp.core :refer :all]
[expectations :refer :all])
(:refer-clojure :exclude [eval fn? true?]))

(expect '3
(eval '3))

(expect 'true
(eval 'true))

(expect 2
(eval 'a {'a 2}))

(expect 2
(eval '(+ 1 1)))

(expect 5
(eval '(+ (+ 1 2) 1 1)))

(expect {'a 3}
(get-env (eval-sexp '(def a 3) {})))

(expect '2
(eval '(if true 2 3)))

(expect '3
(eval '(if false 2 3)))

(expect '(if test
result)
(cond->if '(cond test result)))

(expect '(if c1
r1
(if c2
r2))
(cond->if '(cond c1
r1
c2
r2)))

(expect 3
(eval '(if true 3)))

(expect '(if true 3)
(cond->if '(cond true
3
)))

(expect 3
(eval '(cond true 3)))

(expect 3
(eval-program '[3]))

(expect 3
(eval-program
'[(def a 3)
a]))

(expect 2
(eval '((fn [x] x) 2)))

(expect 4
(eval-program '[(def f (fn [y] y))
(f 4)]))

(expect 25
(eval-program '[(def square (fn [x] (* x x)))
(+ (square 3) (square 4))]))

0 comments on commit 02f5d51

Please sign in to comment.