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