Merge pull request #256 from vvakame/impl-ts
[jackhill/mal.git] / mal / step8_macros.mal
CommitLineData
31690700 1(load-file "../mal/env.mal")
ea81a808 2(load-file "../mal/core.mal")
31690700
JM
3
4;; read
5(def! READ (fn* [strng]
6 (read-string strng)))
7
8
9;; eval
10(def! is-pair (fn* [x]
11 (if (sequential? x)
12 (if (> (count x) 0)
13 true))))
14
15(def! QUASIQUOTE (fn* [ast]
16 (cond
17 (not (is-pair ast))
18 (list 'quote ast)
19
20 (= 'unquote (first ast))
21 (nth ast 1)
22
23 (if (is-pair (first ast))
24 (if (= 'splice-unquote (first (first ast)))
25 true))
26 (list 'concat (nth (first ast) 1) (QUASIQUOTE (rest ast)))
27
28 "else"
29 (list 'cons (QUASIQUOTE (first ast)) (QUASIQUOTE (rest ast))))))
30
31(def! is-macro-call (fn* [ast env]
32 (if (list? ast)
33 (let* [a0 (first ast)]
34 (if (symbol? a0)
35 (if (env-find env a0)
36 (let* [m (meta (env-get env a0))]
37 (if m
38 (if (get m "ismacro")
39 true)))))))))
40
41(def! MACROEXPAND (fn* [ast env]
42 (if (is-macro-call ast env)
43 (let* [mac (env-get env (first ast))]
44 (MACROEXPAND (apply mac (rest ast)) env))
45 ast)))
46
47(def! eval-ast (fn* [ast env] (do
48 ;;(do (prn "eval-ast" ast "/" (keys env)) )
49 (cond
50 (symbol? ast) (env-get env ast)
51
52 (list? ast) (map (fn* [exp] (EVAL exp env)) ast)
53
54 (vector? ast) (apply vector (map (fn* [exp] (EVAL exp env)) ast))
55
56 (map? ast) (apply hash-map
57 (apply concat
58 (map (fn* [k] [k (EVAL (get ast k) env)])
59 (keys ast))))
60
61 "else" ast))))
62
63(def! LET (fn* [env args]
64 (if (> (count args) 0)
65 (do
66 (env-set env (nth args 0) (EVAL (nth args 1) env))
67 (LET env (rest (rest args)))))))
68
69(def! EVAL (fn* [ast env] (do
70 ;;(do (prn "EVAL" ast "/" (keys @env)) )
71 (if (not (list? ast))
72 (eval-ast ast env)
73
74 ;; apply list
75 (let* [ast (MACROEXPAND ast env)]
76 (if (not (list? ast))
bfa3dd35 77 (eval-ast ast env)
31690700
JM
78
79 (let* [a0 (first ast)]
80 (cond
3178009c
DM
81 (nil? a0)
82 ast
83
31690700
JM
84 (= 'def! a0)
85 (env-set env (nth ast 1) (EVAL (nth ast 2) env))
a2849f89 86
31690700
JM
87 (= 'let* a0)
88 (let* [let-env (new-env env)]
89 (do
90 (LET let-env (nth ast 1))
91 (EVAL (nth ast 2) let-env)))
a2849f89 92
31690700
JM
93 (= 'quote a0)
94 (nth ast 1)
a2849f89 95
31690700
JM
96 (= 'quasiquote a0)
97 (let* [a1 (nth ast 1)]
98 (EVAL (QUASIQUOTE a1) env))
a2849f89 99
31690700
JM
100 (= 'defmacro! a0)
101 (let* [a1 (nth ast 1)
102 a2 (nth ast 2)
103 f (EVAL a2 env)
104 m (or (meta f) {})
105 mac (with-meta f (assoc m "ismacro" true))]
106 (env-set env a1 mac))
a2849f89 107
31690700
JM
108 (= 'macroexpand a0)
109 (let* [a1 (nth ast 1)]
110 (MACROEXPAND a1 env))
1617910a 111
31690700
JM
112 (= 'do a0)
113 (let* [el (eval-ast (rest ast) env)]
114 (nth el (- (count el) 1)))
a2849f89 115
31690700
JM
116 (= 'if a0)
117 (let* [cond (EVAL (nth ast 1) env)]
118 (if (or (= cond nil) (= cond false))
119 (if (> (count ast) 3)
120 (EVAL (nth ast 3) env)
121 nil)
122 (EVAL (nth ast 2) env)))
a2849f89 123
31690700
JM
124 (= 'fn* a0)
125 (fn* [& args]
126 (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
a2849f89 127
31690700
JM
128 "else"
129 (let* [el (eval-ast ast env)
130 f (first el)
131 args (rest el)]
132 (apply f args))))))))))
133
134
135;; print
136(def! PRINT (fn* [exp] (pr-str exp)))
137
138;; repl
139(def! repl-env (new-env))
140(def! rep (fn* [strng]
141 (PRINT (EVAL (READ strng) repl-env))))
142
8cb5cda4
JM
143;; core.mal: defined directly using mal
144(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns)
145(env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env)))
86b689f3 146(env-set repl-env '*ARGV* (rest *ARGV*))
31690700 147
8cb5cda4 148;; core.mal: defined using the new language itself
31690700 149(rep "(def! not (fn* [a] (if a false true)))")
1617910a 150(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
8cb5cda4
JM
151(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)))))))")
152(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))))))))")
31690700 153
86b689f3
JM
154;; repl loop
155(def! repl-loop (fn* []
31690700
JM
156 (let* [line (readline "mal-user> ")]
157 (if line
158 (do
159 (if (not (= "" line))
160 (try*
86b689f3 161 (println (rep line))
31690700
JM
162 (catch* exc
163 (println "Uncaught exception:" exc))))
86b689f3
JM
164 (repl-loop))))))
165
166(def! -main (fn* [& args]
167 (if (> (count args) 0)
168 (rep (str "(load-file \"" (first args) "\")"))
169 (repl-loop))))
170(apply -main *ARGV*)