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