Merge pull request #378 from asarhaddon/test-macro-not-changing-function
[jackhill/mal.git] / haxe / Step6_file.hx
1 import Compat;
2 import types.Types.MalType;
3 import types.Types.*;
4 import types.MalException;
5 import reader.*;
6 import printer.*;
7 import env.*;
8 import core.*;
9
10 class Step6_file {
11 // READ
12 static function READ(str:String):MalType {
13 return Reader.read_str(str);
14 }
15
16 // EVAL
17 static function eval_ast(ast:MalType, env:Env) {
18 return switch (ast) {
19 case MalSymbol(s): env.get(ast);
20 case MalList(l):
21 MalList(l.map(function(x) { return EVAL(x, env); }));
22 case MalVector(l):
23 MalVector(l.map(function(x) { return EVAL(x, env); }));
24 case MalHashMap(m):
25 var new_map = new Map<String,MalType>();
26 for (k in m.keys()) {
27 new_map[k] = EVAL(m[k], env);
28 }
29 MalHashMap(new_map);
30 case _: ast;
31 }
32 }
33
34 static function EVAL(ast:MalType, env:Env):MalType {
35 while (true) {
36 if (!list_Q(ast)) { return eval_ast(ast, env); }
37
38 // apply
39 var alst = _list(ast);
40 if (alst.length == 0) { return ast; }
41
42 switch (alst[0]) {
43 case MalSymbol("def!"):
44 return env.set(alst[1], EVAL(alst[2], env));
45 case MalSymbol("let*"):
46 var let_env = new Env(env);
47 switch (alst[1]) {
48 case MalList(l) | MalVector(l):
49 for (i in 0...l.length) {
50 if ((i%2) > 0) { continue; }
51 let_env.set(l[i], EVAL(l[i+1], let_env));
52 }
53 case _: throw "Invalid let*";
54 }
55 ast = alst[2];
56 env = let_env;
57 continue; // TCO
58 case MalSymbol("do"):
59 var el = eval_ast(MalList(alst.slice(1, alst.length-1)), env);
60 ast = last(ast);
61 continue; // TCO
62 case MalSymbol("if"):
63 var cond = EVAL(alst[1], env);
64 if (cond != MalFalse && cond != MalNil) {
65 ast = alst[2];
66 } else if (alst.length > 3) {
67 ast = alst[3];
68 } else {
69 return MalNil;
70 }
71 continue; // TCO
72 case MalSymbol("fn*"):
73 return MalFunc(function (args) {
74 return EVAL(alst[2], new Env(env, _list(alst[1]), args));
75 },alst[2],env,alst[1],false,nil);
76 case _:
77 var el = eval_ast(ast, env);
78 var lst = _list(el);
79 switch (first(el)) {
80 case MalFunc(f,a,e,params,_,_):
81 var args = _list(el).slice(1);
82 if (a != null) {
83 ast = a;
84 env = new Env(e, _list(params), args);
85 continue; // TCO
86 } else {
87 return f(args);
88 }
89 case _: throw "Call of non-function";
90 }
91 }
92 }
93 }
94
95 // PRINT
96 static function PRINT(exp:MalType):String {
97 return Printer.pr_str(exp, true);
98 }
99
100 // repl
101 static var repl_env = new Env(null);
102
103 static function rep(line:String):String {
104 return PRINT(EVAL(READ(line), repl_env));
105 }
106
107 public static function main() {
108 // core.EXT: defined using Haxe
109 for (k in Core.ns.keys()) {
110 repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));
111 }
112
113 var evalfn = MalFunc(function(args) {
114 return EVAL(args[0], repl_env);
115 },null,null,null,false,nil);
116 repl_env.set(MalSymbol("eval"), evalfn);
117
118 var cmdargs = Compat.cmdline_args();
119 var argarray = cmdargs.map(function(a) { return MalString(a); });
120 repl_env.set(MalSymbol("*ARGV*"), MalList(argarray.slice(1)));
121
122 // core.mal: defined using the language itself
123 rep("(def! not (fn* (a) (if a false true)))");
124 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
125
126 if (cmdargs.length > 0) {
127 rep('(load-file "${cmdargs[0]}")');
128 Compat.exit(0);
129 }
130
131 while (true) {
132 try {
133 var line = Compat.readline("user> ");
134 if (line == "") { continue; }
135 Compat.println(rep(line));
136 } catch (exc:BlankLine) {
137 continue;
138 } catch (exc:haxe.io.Eof) {
139 Compat.exit(0);
140 } catch (exc:Dynamic) {
141 if (Type.getClass(exc) == MalException) {
142 Compat.println("Error: " + Printer.pr_str(exc.obj, true));
143 } else {
144 Compat.println("Error: " + exc);
145 };
146 }
147 }
148 }
149 }