Commit | Line | Data |
---|---|---|
a816262a JM |
1 | import types.{MalList, _list, _list_Q, MalVector, MalHashMap, |
2 | Func, MalFunction} | |
3 | import env.Env | |
4 | ||
90f618cb | 5 | object stepA_mal { |
a816262a JM |
6 | // read |
7 | def READ(str: String): Any = { | |
8 | reader.read_str(str) | |
9 | } | |
10 | ||
11 | // eval | |
12 | def is_pair(x: Any): Boolean = { | |
13 | types._sequential_Q(x) && x.asInstanceOf[MalList].value.length > 0 | |
14 | } | |
15 | ||
16 | def quasiquote(ast: Any): Any = { | |
17 | if (!is_pair(ast)) { | |
18 | return _list(Symbol("quote"), ast) | |
19 | } else { | |
20 | val a0 = ast.asInstanceOf[MalList](0) | |
21 | if (types._symbol_Q(a0) && | |
22 | a0.asInstanceOf[Symbol].name == "unquote") { | |
23 | return ast.asInstanceOf[MalList](1) | |
24 | } else if (is_pair(a0)) { | |
25 | val a00 = a0.asInstanceOf[MalList](0) | |
26 | if (types._symbol_Q(a00) && | |
27 | a00.asInstanceOf[Symbol].name == "splice-unquote") { | |
28 | return _list(Symbol("concat"), | |
29 | a0.asInstanceOf[MalList](1), | |
30 | quasiquote(ast.asInstanceOf[MalList].drop(1))) | |
31 | } | |
32 | } | |
33 | return _list(Symbol("cons"), | |
34 | quasiquote(a0), | |
35 | quasiquote(ast.asInstanceOf[MalList].drop(1))) | |
36 | } | |
37 | } | |
38 | ||
39 | def is_macro_call(ast: Any, env: Env): Boolean = { | |
40 | ast match { | |
41 | case ml: MalList => { | |
eb243cd5 DM |
42 | if (ml.value.length > 0 && |
43 | types._symbol_Q(ml(0)) && | |
a816262a JM |
44 | env.find(ml(0).asInstanceOf[Symbol]) != null) { |
45 | env.get(ml(0).asInstanceOf[Symbol]) match { | |
46 | case f: MalFunction => return f.ismacro | |
47 | case _ => return false | |
48 | } | |
49 | } | |
50 | return false | |
51 | } | |
52 | case _ => return false | |
53 | } | |
54 | } | |
55 | ||
56 | def macroexpand(orig_ast: Any, env: Env): Any = { | |
57 | var ast = orig_ast; | |
58 | while (is_macro_call(ast, env)) { | |
59 | ast.asInstanceOf[MalList].value match { | |
60 | case f :: args => { | |
61 | val mac = env.get(f.asInstanceOf[Symbol]) | |
62 | ast = mac.asInstanceOf[MalFunction](args) | |
63 | } | |
64 | case _ => throw new Exception("macroexpand: invalid call") | |
65 | } | |
66 | } | |
67 | ast | |
68 | } | |
69 | ||
70 | def eval_ast(ast: Any, env: Env): Any = { | |
71 | ast match { | |
72 | case s : Symbol => env.get(s) | |
73 | case v: MalVector => v.map(EVAL(_, env)) | |
74 | case l: MalList => l.map(EVAL(_, env)) | |
75 | case m: MalHashMap => { | |
fef22d1c | 76 | m.map{case (k,v) => (k, EVAL(v, env))} |
a816262a JM |
77 | } |
78 | case _ => ast | |
79 | } | |
80 | } | |
81 | ||
82 | def EVAL(orig_ast: Any, orig_env: Env): Any = { | |
83 | var ast = orig_ast; var env = orig_env; | |
84 | while (true) { | |
85 | ||
86 | //println("EVAL: " + printer._pr_str(ast,true)) | |
87 | if (!_list_Q(ast)) | |
88 | return eval_ast(ast, env) | |
89 | ||
90 | // apply list | |
91 | ast = macroexpand(ast, env) | |
44aef1f4 JM |
92 | if (!_list_Q(ast)) |
93 | return eval_ast(ast, env) | |
a816262a JM |
94 | |
95 | ast.asInstanceOf[MalList].value match { | |
eb243cd5 DM |
96 | case Nil => { |
97 | return ast | |
98 | } | |
a816262a JM |
99 | case Symbol("def!") :: a1 :: a2 :: Nil => { |
100 | return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) | |
101 | } | |
102 | case Symbol("let*") :: a1 :: a2 :: Nil => { | |
103 | val let_env = new Env(env) | |
104 | for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { | |
105 | let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) | |
106 | } | |
107 | env = let_env | |
108 | ast = a2 // continue loop (TCO) | |
109 | } | |
110 | case Symbol("quote") :: a1 :: Nil => { | |
111 | return a1 | |
112 | } | |
113 | case Symbol("quasiquote") :: a1 :: Nil => { | |
114 | ast = quasiquote(a1) // continue loop (TCO) | |
115 | } | |
116 | case Symbol("defmacro!") :: a1 :: a2 :: Nil => { | |
117 | val f = EVAL(a2, env) | |
118 | f.asInstanceOf[MalFunction].ismacro = true | |
119 | return env.set(a1.asInstanceOf[Symbol], f) | |
120 | } | |
121 | case Symbol("macroexpand") :: a1 :: Nil => { | |
122 | return macroexpand(a1, env) | |
123 | } | |
124 | case Symbol("try*") :: a1 :: rest => { | |
125 | try { | |
126 | return EVAL(a1, env) | |
127 | } catch { | |
128 | case t: Throwable => { | |
757ebbfb | 129 | if (rest.length == 0) throw t |
a816262a JM |
130 | rest(0).asInstanceOf[MalList].value match { |
131 | case List(Symbol("catch*"), a21, a22) => { | |
132 | val exc: Any = t match { | |
133 | case mex: types.MalException => mex.value | |
134 | case _ => t.getMessage | |
135 | } | |
136 | return EVAL(a22, new Env(env, | |
137 | List(a21).iterator, | |
138 | List(exc).iterator)) | |
139 | } | |
140 | } | |
141 | throw t | |
142 | } | |
143 | } | |
144 | } | |
145 | case Symbol("do") :: rest => { | |
146 | eval_ast(_list(rest.slice(0,rest.length-1):_*), env) | |
147 | ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) | |
148 | } | |
149 | case Symbol("if") :: a1 :: a2 :: rest => { | |
150 | val cond = EVAL(a1, env) | |
151 | if (cond == null || cond == false) { | |
152 | if (rest.length == 0) return null | |
153 | ast = rest(0) // continue loop (TCO) | |
154 | } else { | |
155 | ast = a2 // continue loop (TCO) | |
156 | } | |
157 | } | |
158 | case Symbol("fn*") :: a1 :: a2 :: Nil => { | |
159 | return new MalFunction(a2, env, a1.asInstanceOf[MalList], | |
160 | (args: List[Any]) => { | |
161 | EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) | |
162 | } | |
163 | ) | |
164 | } | |
165 | case _ => { | |
166 | // function call | |
167 | eval_ast(ast, env).asInstanceOf[MalList].value match { | |
168 | case f :: el => { | |
169 | f match { | |
170 | case fn: MalFunction => { | |
171 | env = fn.gen_env(el) | |
172 | ast = fn.ast // continue loop (TCO) | |
173 | } | |
174 | case fn: Func => { | |
175 | return fn(el) | |
176 | } | |
177 | case _ => { | |
178 | throw new Exception("attempt to call non-function: " + f) | |
179 | } | |
180 | } | |
181 | } | |
182 | case _ => throw new Exception("invalid apply") | |
183 | } | |
184 | } | |
185 | } | |
186 | } | |
187 | } | |
188 | ||
189 | ||
190 | def PRINT(exp: Any): String = { | |
191 | printer._pr_str(exp, true) | |
192 | } | |
193 | ||
194 | // repl | |
195 | def main(args: Array[String]) = { | |
196 | val repl_env: Env = new Env() | |
197 | val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) | |
198 | ||
199 | // core.scala: defined using scala | |
200 | core.ns.map{case (k: String,v: Any) => { | |
201 | repl_env.set(Symbol(k), new Func(v)) | |
202 | }} | |
203 | repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) | |
204 | repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) | |
205 | ||
206 | // core.mal: defined using the language itself | |
207 | REP("(def! *host-language* \"scala\")") | |
208 | REP("(def! not (fn* (a) (if a false true)))") | |
209 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
210 | 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)))))))") | |
29ba1fb6 DM |
211 | REP("(def! *gensym-counter* (atom 0))") |
212 | REP("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))") | |
213 | REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") | |
a816262a JM |
214 | |
215 | ||
216 | if (args.length > 0) { | |
217 | REP("(load-file \"" + args(0) + "\")") | |
218 | System.exit(0) | |
219 | } | |
220 | ||
221 | // repl loop | |
222 | REP("(println (str \"Mal [\" *host-language* \"]\"))") | |
223 | var line:String = null | |
224 | while ({line = readLine("user> "); line != null}) { | |
225 | try { | |
226 | println(REP(line)) | |
227 | } catch { | |
228 | case e : Throwable => { | |
229 | println("Error: " + e.getMessage) | |
230 | println(" " + e.getStackTrace.mkString("\n ")) | |
231 | } | |
232 | } | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | // vim: ts=2:sw=2 |