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