2 (:refer-clojure :exclude [macroexpand])
3 (:require [clojure.repl]
11 (defn READ [& [strng]]
12 (let [line (if strng strng (read-line))]
13 (reader/read-string strng)))
18 (and (sequential? x) (> (count x) 0)))
20 (defn quasiquote [ast]
25 (= 'unquote (first ast))
28 (and (is-pair (first ast)) (= 'splice-unquote (ffirst ast)))
29 (list 'concat (-> ast first second) (quasiquote (rest ast)))
32 (list 'cons (quasiquote (first ast)) (quasiquote (rest ast)))))
34 (defn is-macro-call [ast env]
37 (env/env-find env (first ast))
38 (:ismacro (meta (env/env-get env (first ast))))))
40 (defn macroexpand [ast env]
42 (if (is-macro-call ast env)
43 (let [mac (env/env-get env (first ast))]
44 (recur (apply mac (rest ast))))
47 (defn eval-ast [ast env]
49 (symbol? ast) (env/env-get env ast)
51 (seq? ast) (doall (map #(EVAL % env) ast))
53 (vector? ast) (vec (doall (map #(EVAL % env) ast)))
55 (map? ast) (apply hash-map (doall (map #(EVAL % env)
56 (mapcat identity ast))))
63 ;;(prn "EVAL" ast (keys @env)) (flush)
68 (let [ast (macroexpand ast env)]
72 (let [[a0 a1 a2 a3] ast]
75 (env/env-set env a1 (EVAL a2 env))
78 (let [let-env (env/env env)]
79 (doseq [[b e] (partition 2 a1)]
80 (env/env-set let-env b (EVAL e let-env)))
87 (recur (quasiquote a1) env)
90 (let [func (with-meta (EVAL a2 env)
92 (env/env-set env a1 func))
98 (eval (reader/read-string a1))
101 (if (= 'catch* (nth a2 0))
104 (catch clojure.lang.ExceptionInfo ei
105 (EVAL (nth a2 2) (env/env env
107 [(:data (ex-data ei))])))
109 (EVAL (nth a2 2) (env/env env
111 [(.getMessage t)]))))
115 (do (eval-ast (->> ast (drop-last) (drop 1)) env)
116 (recur (last ast) env))
119 (let [cond (EVAL a1 env)]
120 (if (or (= cond nil) (= cond false))
121 (if (> (count ast) 2)
129 (EVAL a2 (env/env env a1 args)))
135 (let [el (eval-ast ast env)
138 {:keys [expression environment parameters]} (meta f)]
140 (recur expression (env/env environment parameters args))
141 (apply f args))))))))))
144 (defn PRINT [exp] (pr-str exp))
147 (def repl-env (env/env))
150 (PRINT (EVAL (READ strng) repl-env)))
152 ;; core.clj: defined using Clojure
153 (doseq [[k v] core/core_ns] (env/env-set repl-env k v))
154 (env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env)))
155 (env/env-set repl-env '*ARGV* ())
157 ;; core.mal: defined using the language itself
158 (rep "(def! *host-language* \"clojure\")")
159 (rep "(def! not (fn* [a] (if a false true)))")
160 (rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))")
161 (rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
162 (rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
166 (let [line (readline/readline "user> ")]
168 (when-not (re-seq #"^\s*$|^\s*;.*$" line) ; blank/comment
172 (clojure.repl/pst e))))
176 (env/env-set repl-env '*ARGV* (rest args))
178 (rep (str "(load-file \"" (first args) "\")"))
180 (rep "(println (str \"Mal [\" *host-language* \"]\"))")