factor: Fix exception on literal empty list
[jackhill/mal.git] / haxe / Step9_try.hx
CommitLineData
32d0a1cf 1import Compat;
1d166495
JM
2import types.Types.MalType;
3import types.Types.*;
4import types.MalException;
5import reader.*;
6import printer.*;
7import env.*;
8import core.*;
9import haxe.rtti.Meta;
10
11class Step9_try {
12 // READ
13 static function READ(str:String):MalType {
14 return Reader.read_str(str);
15 }
16
17 // EVAL
18 static function is_pair(ast:MalType) {
19 return switch (ast) {
20 case MalList(l) | MalVector(l): l.length > 0;
21 case _: false;
22 }
23 }
24
25 static function quasiquote(ast:MalType) {
26 if (!is_pair(ast)) {
27 return MalList([MalSymbol("quote"), ast]);
28 } else {
29 var a0 = first(ast);
30 if (_equal_Q(a0, MalSymbol("unquote"))) {
31 return _nth(ast, 1);
32 } else if (is_pair(a0)) {
33 var a00 = first(a0);
34 if (_equal_Q(a00, MalSymbol("splice-unquote"))) {
35 return MalList([MalSymbol("concat"),
36 _nth(a0, 1),
37 quasiquote(rest(ast))]);
38 }
39 }
40 return MalList([MalSymbol("cons"),
41 quasiquote(a0),
42 quasiquote(rest(ast))]);
43 }
44 }
45
46 static function is_macro(ast:MalType, env:Env) {
47 return switch(ast) {
48 case MalList(a):
49 var a0 = a[0];
50 return symbol_Q(a0) &&
51 env.find(a0) != null &&
52 _macro_Q(env.get(a0));
53 case _: false;
54 }
55 }
56
57 static function macroexpand(ast:MalType, env:Env) {
58 while (is_macro(ast, env)) {
59 var mac = env.get(first(ast));
60 switch (mac) {
61 case MalFunc(f,_,_,_,_,_):
62 ast = f(_list(ast).slice(1));
63 case _: break;
64 }
65 }
66 return ast;
67 }
68
69 static function eval_ast(ast:MalType, env:Env) {
70 return switch (ast) {
71 case MalSymbol(s): env.get(ast);
72 case MalList(l):
73 MalList(l.map(function(x) { return EVAL(x, env); }));
74 case MalVector(l):
75 MalVector(l.map(function(x) { return EVAL(x, env); }));
76 case MalHashMap(m):
77 var new_map = new Map<String,MalType>();
78 for (k in m.keys()) {
79 new_map[k] = EVAL(m[k], env);
80 }
81 MalHashMap(new_map);
82 case _: ast;
83 }
84 }
85
86 static function EVAL(ast:MalType, env:Env):MalType {
87 while (true) {
88 if (!list_Q(ast)) { return eval_ast(ast, env); }
89
90 // apply
91 ast = macroexpand(ast, env);
33dec7af 92 if (!list_Q(ast)) { return eval_ast(ast, env); }
1d166495
JM
93
94 var alst = _list(ast);
95 switch (alst[0]) {
96 case MalSymbol("def!"):
97 return env.set(alst[1], EVAL(alst[2], env));
98 case MalSymbol("let*"):
99 var let_env = new Env(env);
100 switch (alst[1]) {
101 case MalList(l) | MalVector(l):
102 for (i in 0...l.length) {
103 if ((i%2) > 0) { continue; }
104 let_env.set(l[i], EVAL(l[i+1], let_env));
105 }
106 case _: throw "Invalid let*";
107 }
108 ast = alst[2];
109 env = let_env;
110 continue; // TCO
111 case MalSymbol("quote"):
112 return alst[1];
113 case MalSymbol("quasiquote"):
114 ast = quasiquote(alst[1]);
115 continue; // TCO
116 case MalSymbol("defmacro!"):
117 var func = EVAL(alst[2], env);
118 return switch (func) {
119 case MalFunc(f,ast,e,params,_,_):
120 env.set(alst[1], MalFunc(f,ast,e,params,true,nil));
121 case _:
122 throw "Invalid defmacro! call";
123 }
124 case MalSymbol("macroexpand"):
125 return macroexpand(alst[1], env);
126 case MalSymbol("try*"):
127 try {
128 return EVAL(alst[1], env);
129 } catch (err:Dynamic) {
130 if (alst.length > 2) {
131 switch (alst[2]) {
132 case MalList([MalSymbol("catch*"), a21, a22]):
133 var exc;
134 if (Type.getClass(err) == MalException) {
135 exc = err.obj;
136 } else {
137 exc = MalString(Std.string(err));
138 };
139 return EVAL(a22, new Env(env, [a21], [exc]));
140 case _:
141 throw err;
142 }
143 } else {
144 throw err;
145 }
146 }
147 case MalSymbol("do"):
148 var el = eval_ast(MalList(alst.slice(1, alst.length-1)), env);
149 ast = last(ast);
150 continue; // TCO
151 case MalSymbol("if"):
152 var cond = EVAL(alst[1], env);
153 if (cond != MalFalse && cond != MalNil) {
154 ast = alst[2];
155 } else if (alst.length > 3) {
156 ast = alst[3];
157 } else {
158 return MalNil;
159 }
160 continue; // TCO
161 case MalSymbol("fn*"):
162 return MalFunc(function (args) {
163 return EVAL(alst[2], new Env(env, _list(alst[1]), args));
164 },alst[2],env,alst[1],false,nil);
165 case _:
166 var el = eval_ast(ast, env);
167 var lst = _list(el);
168 switch (first(el)) {
169 case MalFunc(f,a,e,params,_,_):
170 var args = _list(el).slice(1);
171 if (a != null) {
172 ast = a;
173 env = new Env(e, _list(params), args);
174 continue; // TCO
175 } else {
176 return f(args);
177 }
178 case _: throw "Call of non-function";
179 }
180 }
181 }
182 }
183
184 // PRINT
185 static function PRINT(exp:MalType):String {
186 return Printer.pr_str(exp, true);
187 }
188
189 // repl
190 static var repl_env = new Env(null);
191
192 static function rep(line:String):String {
193 return PRINT(EVAL(READ(line), repl_env));
194 }
195
196 public static function main() {
1d166495
JM
197 // core.EXT: defined using Haxe
198 for (k in Core.ns.keys()) {
199 repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));
200 }
201
202 var evalfn = MalFunc(function(args) {
203 return EVAL(args[0], repl_env);
204 },null,null,null,false,nil);
205 repl_env.set(MalSymbol("eval"), evalfn);
206
32d0a1cf
JM
207 var cmdargs = Compat.cmdline_args();
208 var argarray = cmdargs.map(function(a) { return MalString(a); });
17946efb 209 repl_env.set(MalSymbol("*ARGV*"), MalList(argarray.slice(1)));
1d166495
JM
210
211 // core.mal: defined using the language itself
212 rep("(def! not (fn* (a) (if a false true)))");
213 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
214 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)))))))");
215 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))))))))");
216
217
218 if (cmdargs.length > 0) {
32d0a1cf
JM
219 rep('(load-file "${cmdargs[0]}")');
220 Compat.exit(0);
1d166495
JM
221 }
222
223 while (true) {
224 try {
32d0a1cf 225 var line = Compat.readline("user> ");
1d166495 226 if (line == "") { continue; }
32d0a1cf 227 Compat.println(rep(line));
1d166495
JM
228 } catch (exc:BlankLine) {
229 continue;
230 } catch (exc:haxe.io.Eof) {
32d0a1cf 231 Compat.exit(0);
1d166495 232 } catch (exc:Dynamic) {
32d0a1cf 233 Compat.println(exc);
1d166495
JM
234 }
235 }
236 }
237}