ada.2: typo
[jackhill/mal.git] / impls / haxe / Step8_macros.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 Step8_macros {
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 is_macro(ast:MalType, env:Env) {
46 return switch(ast) {
d3ec2994 47 case MalList([]): false;
1d166495
JM
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);
d3ec2994 95 if (alst.length == 0) { return ast; }
1d166495
JM
96 switch (alst[0]) {
97 case MalSymbol("def!"):
98 return env.set(alst[1], EVAL(alst[2], env));
99 case MalSymbol("let*"):
100 var let_env = new Env(env);
101 switch (alst[1]) {
102 case MalList(l) | MalVector(l):
103 for (i in 0...l.length) {
104 if ((i%2) > 0) { continue; }
105 let_env.set(l[i], EVAL(l[i+1], let_env));
106 }
107 case _: throw "Invalid let*";
108 }
109 ast = alst[2];
110 env = let_env;
111 continue; // TCO
112 case MalSymbol("quote"):
113 return alst[1];
114 case MalSymbol("quasiquote"):
115 ast = quasiquote(alst[1]);
116 continue; // TCO
117 case MalSymbol("defmacro!"):
118 var func = EVAL(alst[2], env);
119 return switch (func) {
120 case MalFunc(f,ast,e,params,_,_):
121 env.set(alst[1], MalFunc(f,ast,e,params,true,nil));
122 case _:
123 throw "Invalid defmacro! call";
124 }
125 case MalSymbol("macroexpand"):
126 return macroexpand(alst[1], env);
127 case MalSymbol("do"):
128 var el = eval_ast(MalList(alst.slice(1, alst.length-1)), env);
129 ast = last(ast);
130 continue; // TCO
131 case MalSymbol("if"):
132 var cond = EVAL(alst[1], env);
133 if (cond != MalFalse && cond != MalNil) {
134 ast = alst[2];
135 } else if (alst.length > 3) {
136 ast = alst[3];
137 } else {
138 return MalNil;
139 }
140 continue; // TCO
141 case MalSymbol("fn*"):
142 return MalFunc(function (args) {
143 return EVAL(alst[2], new Env(env, _list(alst[1]), args));
144 },alst[2],env,alst[1],false,nil);
145 case _:
146 var el = eval_ast(ast, env);
147 var lst = _list(el);
148 switch (first(el)) {
149 case MalFunc(f,a,e,params,_,_):
150 var args = _list(el).slice(1);
151 if (a != null) {
152 ast = a;
153 env = new Env(e, _list(params), args);
154 continue; // TCO
155 } else {
156 return f(args);
157 }
158 case _: throw "Call of non-function";
159 }
160 }
161 }
162 }
163
164 // PRINT
165 static function PRINT(exp:MalType):String {
166 return Printer.pr_str(exp, true);
167 }
168
169 // repl
170 static var repl_env = new Env(null);
171
172 static function rep(line:String):String {
173 return PRINT(EVAL(READ(line), repl_env));
174 }
175
176 public static function main() {
1d166495
JM
177 // core.EXT: defined using Haxe
178 for (k in Core.ns.keys()) {
179 repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));
180 }
181
182 var evalfn = MalFunc(function(args) {
183 return EVAL(args[0], repl_env);
184 },null,null,null,false,nil);
185 repl_env.set(MalSymbol("eval"), evalfn);
186
32d0a1cf
JM
187 var cmdargs = Compat.cmdline_args();
188 var argarray = cmdargs.map(function(a) { return MalString(a); });
17946efb 189 repl_env.set(MalSymbol("*ARGV*"), MalList(argarray.slice(1)));
1d166495
JM
190
191 // core.mal: defined using the language itself
192 rep("(def! not (fn* (a) (if a false true)))");
e6d41de4 193 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
1d166495 194 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)))))))");
1d166495
JM
195
196
197 if (cmdargs.length > 0) {
32d0a1cf
JM
198 rep('(load-file "${cmdargs[0]}")');
199 Compat.exit(0);
1d166495
JM
200 }
201
202 while (true) {
203 try {
32d0a1cf 204 var line = Compat.readline("user> ");
1d166495 205 if (line == "") { continue; }
32d0a1cf 206 Compat.println(rep(line));
1d166495
JM
207 } catch (exc:BlankLine) {
208 continue;
209 } catch (exc:haxe.io.Eof) {
32d0a1cf 210 Compat.exit(0);
1d166495 211 } catch (exc:Dynamic) {
dd7a4f55
JM
212 if (Type.getClass(exc) == MalException) {
213 Compat.println("Error: " + Printer.pr_str(exc.obj, true));
214 } else {
215 Compat.println("Error: " + exc);
216 };
1d166495
JM
217 }
218 }
219 }
220}