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