Commit | Line | Data |
---|---|---|
a9cd6543 JM |
1 | import reader |
2 | import printer | |
3 | import types | |
4 | import types.MalException | |
5 | import types.MalSymbol | |
6 | import types.MalFunc | |
7 | import env.Env | |
8 | import core | |
9 | ||
10 | // READ | |
11 | READ = { str -> | |
12 | reader.read_str str | |
13 | } | |
14 | ||
15 | // EVAL | |
16 | macro_Q = { ast, env -> | |
17 | if (types.list_Q(ast) && | |
8ec0c2ea | 18 | ast.size() > 0 && |
a9cd6543 JM |
19 | ast[0].class == MalSymbol && |
20 | env.find(ast[0])) { | |
21 | def obj = env.get(ast[0]) | |
22 | if (obj instanceof MalFunc && obj.ismacro) { | |
23 | return true | |
24 | } | |
25 | } | |
26 | return false | |
27 | } | |
28 | macroexpand = { ast, env -> | |
29 | while (macro_Q(ast, env)) { | |
30 | def mac = env.get(ast[0]) | |
31 | ast = mac(ast.drop(1)) | |
32 | } | |
33 | return ast | |
34 | } | |
35 | ||
36 | pair_Q = { ast -> types.sequential_Q(ast) && ast.size() > 0} | |
37 | quasiquote = { ast -> | |
38 | if (! pair_Q(ast)) { | |
39 | [new MalSymbol("quote"), ast] | |
c963be7a JM |
40 | } else if (ast[0] != null && |
41 | ast[0].class == MalSymbol && | |
a9cd6543 JM |
42 | ast[0].value == "unquote") { |
43 | ast[1] | |
44 | } else if (pair_Q(ast[0]) && ast[0][0].class == MalSymbol && | |
45 | ast[0][0].value == "splice-unquote") { | |
46 | [new MalSymbol("concat"), ast[0][1], quasiquote(ast.drop(1))] | |
47 | } else { | |
48 | [new MalSymbol("cons"), quasiquote(ast[0]), quasiquote(ast.drop(1))] | |
49 | } | |
50 | } | |
51 | ||
52 | eval_ast = { ast, env -> | |
53 | switch (ast) { | |
54 | case MalSymbol: return env.get(ast); | |
55 | case List: return types.vector_Q(ast) ? | |
56 | types.vector(ast.collect { EVAL(it,env) }) : | |
57 | ast.collect { EVAL(it,env) } | |
58 | case Map: def new_hm = [:] | |
59 | ast.each { k,v -> | |
60 | new_hm[EVAL(k, env)] = EVAL(v, env) | |
61 | } | |
62 | return new_hm | |
63 | default: return ast | |
64 | } | |
65 | } | |
66 | ||
67 | EVAL = { ast, env -> | |
68 | while (true) { | |
69 | //println("EVAL: ${printer.pr_str(ast,true)}") | |
70 | if (! types.list_Q(ast)) return eval_ast(ast, env) | |
71 | ||
72 | ast = macroexpand(ast, env) | |
44aef1f4 | 73 | if (! types.list_Q(ast)) return eval_ast(ast, env) |
8ec0c2ea | 74 | if (ast.size() == 0) return ast |
a9cd6543 JM |
75 | |
76 | switch (ast[0]) { | |
77 | case { it instanceof MalSymbol && it.value == "def!" }: | |
78 | return env.set(ast[1], EVAL(ast[2], env)) | |
79 | case { it instanceof MalSymbol && it.value == "let*" }: | |
80 | def let_env = new Env(env) | |
81 | for (int i=0; i < ast[1].size(); i += 2) { | |
82 | let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env)) | |
83 | } | |
84 | env = let_env | |
85 | ast = ast[2] | |
86 | break // TCO | |
87 | case { it instanceof MalSymbol && it.value == "quote" }: | |
88 | return ast[1] | |
89 | case { it instanceof MalSymbol && it.value == "quasiquote" }: | |
90 | ast = quasiquote(ast[1]) | |
91 | break // TCO | |
92 | case { it instanceof MalSymbol && it.value == "defmacro!" }: | |
93 | def f = EVAL(ast[2], env) | |
94 | f.ismacro = true | |
95 | return env.set(ast[1], f) | |
96 | case { it instanceof MalSymbol && it.value == "macroexpand" }: | |
97 | return macroexpand(ast[1], env) | |
98 | case { it instanceof MalSymbol && it.value == "try*" }: | |
99 | try { | |
100 | return EVAL(ast[1], env) | |
101 | } catch(exc) { | |
102 | if (ast.size() > 2 && | |
103 | ast[2][0] instanceof MalSymbol && | |
104 | ast[2][0].value == "catch*") { | |
105 | def e = null | |
106 | if (exc instanceof MalException) { | |
107 | e = exc.obj | |
108 | } else { | |
109 | e = exc.message | |
110 | } | |
111 | return EVAL(ast[2][2], new Env(env, [ast[2][1]], [e])) | |
112 | } else { | |
113 | throw exc | |
114 | } | |
115 | } | |
116 | case { it instanceof MalSymbol && it.value == "do" }: | |
117 | ast.size() > 2 ? eval_ast(ast[1..-2], env) : null | |
118 | ast = ast[-1] | |
119 | break // TCO | |
120 | case { it instanceof MalSymbol && it.value == "if" }: | |
121 | def cond = EVAL(ast[1], env) | |
122 | if (cond == false || cond == null) { | |
123 | if (ast.size > 3) { | |
124 | ast = ast[3] | |
125 | break // TCO | |
126 | } else { | |
127 | return null | |
128 | } | |
129 | } else { | |
130 | ast = ast[2] | |
131 | break // TCO | |
132 | } | |
133 | case { it instanceof MalSymbol && it.value == "fn*" }: | |
134 | return new MalFunc(EVAL, ast[2], env, ast[1]) | |
135 | default: | |
136 | def el = eval_ast(ast, env) | |
137 | def (f, args) = [el[0], el.drop(1)] | |
138 | if (f instanceof MalFunc) { | |
139 | env = new Env(f.env, f.params, args) | |
140 | ast = f.ast | |
141 | break // TCO | |
142 | } else { | |
143 | return f(args) | |
144 | } | |
145 | } | |
146 | } | |
147 | } | |
148 | ||
149 | ||
150 | PRINT = { exp -> | |
151 | printer.pr_str exp, true | |
152 | } | |
153 | ||
154 | // REPL | |
155 | repl_env = new Env(); | |
156 | REP = { str -> | |
157 | PRINT(EVAL(READ(str), repl_env)) | |
158 | } | |
159 | ||
160 | // core.EXT: defined using Groovy | |
161 | core.ns.each { k,v -> | |
162 | repl_env.set(new MalSymbol(k), v) | |
163 | } | |
164 | repl_env.set(new MalSymbol("eval"), { a -> EVAL(a[0], repl_env)}) | |
165 | repl_env.set(new MalSymbol("*ARGV*"), this.args as List) | |
166 | ||
167 | // core.mal: defined using mal itself | |
168 | REP("(def! not (fn* (a) (if a false true)))") | |
169 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
170 | 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)))))))"); | |
171 | 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))))))))"); | |
172 | ||
173 | ||
174 | if (this.args.size() > 0) { | |
175 | repl_env.set(new MalSymbol("*ARGV*"), this.args.drop(1) as List) | |
176 | REP("(load-file \"${this.args[0]}\")") | |
177 | System.exit(0) | |
178 | } | |
179 | ||
180 | while (true) { | |
181 | line = System.console().readLine 'user> ' | |
182 | if (line == null) { | |
183 | break; | |
184 | } | |
185 | try { | |
186 | println REP(line) | |
187 | } catch(MalException ex) { | |
188 | println "Error: ${ex.message}" | |
189 | } catch(StackOverflowError ex) { | |
190 | println "Error: ${ex}" | |
191 | } catch(ex) { | |
192 | println "Error: $ex" | |
193 | ex.printStackTrace() | |
194 | } | |
195 | } |