Implement step A
[jackhill/mal.git] / groovy / step9_try.groovy
CommitLineData
a9cd6543
JM
1import reader
2import printer
3import types
4import types.MalException
5import types.MalSymbol
6import types.MalFunc
7import env.Env
8import core
9
10// READ
11READ = { str ->
12 reader.read_str str
13}
14
15// EVAL
16macro_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}
28macroexpand = { 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
36pair_Q = { ast -> types.sequential_Q(ast) && ast.size() > 0}
37quasiquote = { 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
52eval_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
67EVAL = { 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// PRINT
150PRINT = { exp ->
151 printer.pr_str exp, true
152}
153
154// REPL
155repl_env = new Env();
156REP = { str ->
157 PRINT(EVAL(READ(str), repl_env))
158}
159
160// core.EXT: defined using Groovy
161core.ns.each { k,v ->
162 repl_env.set(new MalSymbol(k), v)
163}
164repl_env.set(new MalSymbol("eval"), { a -> EVAL(a[0], repl_env)})
165repl_env.set(new MalSymbol("*ARGV*"), this.args as List)
166
167// core.mal: defined using mal itself
168REP("(def! not (fn* (a) (if a false true)))")
169REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
170REP("(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)))))))");
171REP("(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
174if (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
180while (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}