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