Move implementations into impls/ dir
[jackhill/mal.git] / impls / java / src / main / java / mal / step9_try.java
1 package mal;
2
3 import java.io.IOException;
4
5 import java.io.StringWriter;
6 import java.io.PrintWriter;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.HashMap;
10 import java.util.Iterator;
11 import mal.types.*;
12 import mal.readline;
13 import mal.reader;
14 import mal.printer;
15 import mal.env.Env;
16 import mal.core;
17
18 public class step9_try {
19 // read
20 public static MalVal READ(String str) throws MalThrowable {
21 return reader.read_str(str);
22 }
23
24 // eval
25 public static Boolean is_pair(MalVal x) {
26 return x instanceof MalList && ((MalList)x).size() > 0;
27 }
28
29 public static MalVal quasiquote(MalVal ast) {
30 if (!is_pair(ast)) {
31 return new MalList(new MalSymbol("quote"), ast);
32 } else {
33 MalVal a0 = ((MalList)ast).nth(0);
34 if ((a0 instanceof MalSymbol) &&
35 (((MalSymbol)a0).getName().equals("unquote"))) {
36 return ((MalList)ast).nth(1);
37 } else if (is_pair(a0)) {
38 MalVal a00 = ((MalList)a0).nth(0);
39 if ((a00 instanceof MalSymbol) &&
40 (((MalSymbol)a00).getName().equals("splice-unquote"))) {
41 return new MalList(new MalSymbol("concat"),
42 ((MalList)a0).nth(1),
43 quasiquote(((MalList)ast).rest()));
44 }
45 }
46 return new MalList(new MalSymbol("cons"),
47 quasiquote(a0),
48 quasiquote(((MalList)ast).rest()));
49 }
50 }
51
52 public static Boolean is_macro_call(MalVal ast, Env env)
53 throws MalThrowable {
54 if (ast instanceof MalList) {
55 MalVal a0 = ((MalList)ast).nth(0);
56 if (a0 instanceof MalSymbol &&
57 env.find(((MalSymbol)a0)) != null) {
58 MalVal mac = env.get(((MalSymbol)a0));
59 if (mac instanceof MalFunction &&
60 ((MalFunction)mac).isMacro()) {
61 return true;
62 }
63 }
64 }
65 return false;
66 }
67
68 public static MalVal macroexpand(MalVal ast, Env env)
69 throws MalThrowable {
70 while (is_macro_call(ast, env)) {
71 MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0);
72 MalFunction mac = (MalFunction) env.get(a0);
73 ast = mac.apply(((MalList)ast).rest());
74 }
75 return ast;
76 }
77
78 public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable {
79 if (ast instanceof MalSymbol) {
80 return env.get((MalSymbol)ast);
81 } else if (ast instanceof MalList) {
82 MalList old_lst = (MalList)ast;
83 MalList new_lst = ast.list_Q() ? new MalList()
84 : (MalList)new MalVector();
85 for (MalVal mv : (List<MalVal>)old_lst.value) {
86 new_lst.conj_BANG(EVAL(mv, env));
87 }
88 return new_lst;
89 } else if (ast instanceof MalHashMap) {
90 MalHashMap new_hm = new MalHashMap();
91 Iterator it = ((MalHashMap)ast).value.entrySet().iterator();
92 while (it.hasNext()) {
93 Map.Entry entry = (Map.Entry)it.next();
94 new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));
95 }
96 return new_hm;
97 } else {
98 return ast;
99 }
100 }
101
102 public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
103 MalVal a0, a1,a2, a3, res;
104 MalList el;
105
106 while (true) {
107
108 //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
109 if (!orig_ast.list_Q()) {
110 return eval_ast(orig_ast, env);
111 }
112 if (((MalList)orig_ast).size() == 0) { return orig_ast; }
113
114 // apply list
115 MalVal expanded = macroexpand(orig_ast, env);
116 if (!expanded.list_Q()) {
117 return eval_ast(expanded, env);
118 }
119 MalList ast = (MalList) expanded;
120 if (ast.size() == 0) { return ast; }
121 a0 = ast.nth(0);
122 String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
123 : "__<*fn*>__";
124 switch (a0sym) {
125 case "def!":
126 a1 = ast.nth(1);
127 a2 = ast.nth(2);
128 res = EVAL(a2, env);
129 env.set(((MalSymbol)a1), res);
130 return res;
131 case "let*":
132 a1 = ast.nth(1);
133 a2 = ast.nth(2);
134 MalSymbol key;
135 MalVal val;
136 Env let_env = new Env(env);
137 for(int i=0; i<((MalList)a1).size(); i+=2) {
138 key = (MalSymbol)((MalList)a1).nth(i);
139 val = ((MalList)a1).nth(i+1);
140 let_env.set(key, EVAL(val, let_env));
141 }
142 orig_ast = a2;
143 env = let_env;
144 break;
145 case "quote":
146 return ast.nth(1);
147 case "quasiquote":
148 orig_ast = quasiquote(ast.nth(1));
149 break;
150 case "defmacro!":
151 a1 = ast.nth(1);
152 a2 = ast.nth(2);
153 res = EVAL(a2, env);
154 ((MalFunction)res).setMacro();
155 env.set((MalSymbol)a1, res);
156 return res;
157 case "macroexpand":
158 a1 = ast.nth(1);
159 return macroexpand(a1, env);
160 case "try*":
161 try {
162 return EVAL(ast.nth(1), env);
163 } catch (Throwable t) {
164 if (ast.size() > 2) {
165 MalVal exc;
166 a2 = ast.nth(2);
167 MalVal a20 = ((MalList)a2).nth(0);
168 if (((MalSymbol)a20).getName().equals("catch*")) {
169 if (t instanceof MalException) {
170 exc = ((MalException)t).getValue();
171 } else {
172 StringWriter sw = new StringWriter();
173 t.printStackTrace(new PrintWriter(sw));
174 String tstr = sw.toString();
175 exc = new MalString(t.getMessage() + ": " + tstr);
176 }
177 return EVAL(((MalList)a2).nth(2),
178 new Env(env, ((MalList)a2).slice(1,2),
179 new MalList(exc)));
180 }
181 }
182 throw t;
183 }
184 case "do":
185 eval_ast(ast.slice(1, ast.size()-1), env);
186 orig_ast = ast.nth(ast.size()-1);
187 break;
188 case "if":
189 a1 = ast.nth(1);
190 MalVal cond = EVAL(a1, env);
191 if (cond == types.Nil || cond == types.False) {
192 // eval false slot form
193 if (ast.size() > 3) {
194 orig_ast = ast.nth(3);
195 } else {
196 return types.Nil;
197 }
198 } else {
199 // eval true slot form
200 orig_ast = ast.nth(2);
201 }
202 break;
203 case "fn*":
204 final MalList a1f = (MalList)ast.nth(1);
205 final MalVal a2f = ast.nth(2);
206 final Env cur_env = env;
207 return new MalFunction (a2f, (mal.env.Env)env, a1f) {
208 public MalVal apply(MalList args) throws MalThrowable {
209 return EVAL(a2f, new Env(cur_env, a1f, args));
210 }
211 };
212 default:
213 el = (MalList)eval_ast(ast, env);
214 MalFunction f = (MalFunction)el.nth(0);
215 MalVal fnast = f.getAst();
216 if (fnast != null) {
217 orig_ast = fnast;
218 env = f.genEnv(el.slice(1));
219 } else {
220 return f.apply(el.rest());
221 }
222 }
223
224 }
225 }
226
227 // print
228 public static String PRINT(MalVal exp) {
229 return printer._pr_str(exp, true);
230 }
231
232 // repl
233 public static MalVal RE(Env env, String str) throws MalThrowable {
234 return EVAL(READ(str), env);
235 }
236
237 public static void main(String[] args) throws MalThrowable {
238 String prompt = "user> ";
239
240 final Env repl_env = new Env(null);
241
242 // core.java: defined using Java
243 for (String key : core.ns.keySet()) {
244 repl_env.set(new MalSymbol(key), core.ns.get(key));
245 }
246 repl_env.set(new MalSymbol("eval"), new MalFunction() {
247 public MalVal apply(MalList args) throws MalThrowable {
248 return EVAL(args.nth(0), repl_env);
249 }
250 });
251 MalList _argv = new MalList();
252 for (Integer i=1; i < args.length; i++) {
253 _argv.conj_BANG(new MalString(args[i]));
254 }
255 repl_env.set(new MalSymbol("*ARGV*"), _argv);
256
257
258 // core.mal: defined using the language itself
259 RE(repl_env, "(def! not (fn* (a) (if a false true)))");
260 RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
261 RE(repl_env, "(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)))))))");
262
263 Integer fileIdx = 0;
264 if (args.length > 0 && args[0].equals("--raw")) {
265 readline.mode = readline.Mode.JAVA;
266 fileIdx = 1;
267 }
268 if (args.length > fileIdx) {
269 RE(repl_env, "(load-file \"" + args[fileIdx] + "\")");
270 return;
271 }
272
273 // repl loop
274 while (true) {
275 String line;
276 try {
277 line = readline.readline(prompt);
278 if (line == null) { continue; }
279 } catch (readline.EOFException e) {
280 break;
281 } catch (IOException e) {
282 System.out.println("IOException: " + e.getMessage());
283 break;
284 }
285 try {
286 System.out.println(PRINT(RE(repl_env, line)));
287 } catch (MalContinue e) {
288 continue;
289 } catch (MalException e) {
290 System.out.println("Error: " + printer._pr_str(e.getValue(), false));
291 continue;
292 } catch (MalThrowable t) {
293 System.out.println("Error: " + t.getMessage());
294 continue;
295 } catch (Throwable t) {
296 System.out.println("Uncaught " + t + ": " + t.getMessage());
297 continue;
298 }
299 }
300 }
301 }