2 import types
.Types
.MalType
;
4 import types
.MalException
;
13 static function READ(str
:String
):MalType
{
14 return Reader
.read_str(str
);
18 static function is_pair(ast
:MalType
) {
20 case MalList(l
) |
MalVector(l
): l
.length
> 0;
25 static function quasiquote(ast
:MalType
) {
27 return MalList([MalSymbol("quote"), ast
]);
30 if (_equal_Q(a0
, MalSymbol("unquote"))) {
32 } else if (is_pair(a0
)) {
34 if (_equal_Q(a00
, MalSymbol("splice-unquote"))) {
35 return MalList([MalSymbol("concat"),
37 quasiquote(rest(ast
))]);
40 return MalList([MalSymbol("cons"),
42 quasiquote(rest(ast
))]);
46 static function is_macro(ast
:MalType
, env
:Env
) {
48 case MalList([]): false;
51 return symbol_Q(a0
) &&
52 env
.find(a0
) != null &&
53 _macro_Q(env
.get(a0
));
58 static function macroexpand(ast
:MalType
, env
:Env
) {
59 while (is_macro(ast
, env
)) {
60 var mac
= env
.get(first(ast
));
62 case MalFunc(f
,_
,_
,_
,_
,_
):
63 ast
= f(_list(ast
).slice(1));
70 static function eval_ast(ast
:MalType
, env
:Env
) {
72 case MalSymbol(s
): env
.get(ast
);
74 MalList(l
.map(function(x
) { return EVAL(x
, env
); }));
76 MalVector(l
.map(function(x
) { return EVAL(x
, env
); }));
78 var new_map
= new Map
<String
,MalType
>();
80 new_map
[k
] = EVAL(m
[k
], env
);
87 static function EVAL(ast
:MalType
, env
:Env
):MalType
{
89 if (!list_Q(ast
)) { return eval_ast(ast
, env
); }
92 ast
= macroexpand(ast
, env
);
93 if (!list_Q(ast
)) { return eval_ast(ast
, env
); }
95 var alst
= _list(ast
);
96 if (alst
.length
== 0) { return ast
; }
98 case MalSymbol("def!"):
99 return env
.set(alst
[1], EVAL(alst
[2], env
));
100 case MalSymbol("let*"):
101 var let_env
= new Env(env
);
103 case MalList(l
) |
MalVector(l
):
104 for (i
in 0...l
.length
) {
105 if ((i
%2) > 0) { continue; }
106 let_env
.set(l
[i
], EVAL(l
[i
+1], let_env
));
108 case _
: throw "Invalid let*";
113 case MalSymbol("quote"):
115 case MalSymbol("quasiquote"):
116 ast
= quasiquote(alst
[1]);
118 case MalSymbol("defmacro!"):
119 var func
= EVAL(alst
[2], env
);
120 return switch (func
) {
121 case MalFunc(f
,ast
,e
,params
,_
,_
):
122 env
.set(alst
[1], MalFunc(f
,ast
,e
,params
,true,nil
));
124 throw "Invalid defmacro! call";
126 case MalSymbol("macroexpand"):
127 return macroexpand(alst
[1], env
);
128 case MalSymbol("try*"):
130 return EVAL(alst
[1], env
);
131 } catch (err
:Dynamic) {
132 if (alst
.length
> 2) {
134 case MalList([MalSymbol("catch*"), a21
, a22
]):
136 if (Type
.getClass(err
) == MalException
) {
139 exc
= MalString(Std
.string(err
));
141 return EVAL(a22
, new Env(env
, [a21
], [exc
]));
149 case MalSymbol("do"):
150 var el
= eval_ast(MalList(alst
.slice(1, alst
.length
-1)), env
);
153 case MalSymbol("if"):
154 var cond
= EVAL(alst
[1], env
);
155 if (cond
!= MalFalse
&& cond
!= MalNil
) {
157 } else if (alst
.length
> 3) {
163 case MalSymbol("fn*"):
164 return MalFunc(function (args
) {
165 return EVAL(alst
[2], new Env(env
, _list(alst
[1]), args
));
166 },alst
[2],env
,alst
[1],false,nil
);
168 var el
= eval_ast(ast
, env
);
171 case MalFunc(f
,a
,e
,params
,_
,_
):
172 var args
= _list(el
).slice(1);
175 env
= new Env(e
, _list(params
), args
);
180 case _
: throw "Call of non-function";
187 static function PRINT(exp
:MalType
):String
{
188 return Printer
.pr_str(exp
, true);
192 static var repl_env
= new Env(null);
194 static function rep(line
:String
):String
{
195 return PRINT(EVAL(READ(line
), repl_env
));
198 public static function main() {
199 // core.EXT: defined using Haxe
200 for (k
in Core
.ns
.keys()) {
201 repl_env
.set(MalSymbol(k
), MalFunc(Core
.ns
[k
],null,null,null,false,nil
));
204 var evalfn
= MalFunc(function(args
) {
205 return EVAL(args
[0], repl_env
);
206 },null,null,null,false,nil
);
207 repl_env
.set(MalSymbol("eval"), evalfn
);
209 var cmdargs
= Compat
.cmdline_args();
210 var argarray
= cmdargs
.map(function(a
) { return MalString(a
); });
211 repl_env
.set(MalSymbol("*ARGV*"), MalList(argarray
.slice(1)));
213 // core.mal: defined using the language itself
214 rep("(def! *host-language* \"haxe\")");
215 rep("(def! not (fn* (a) (if a false true)))");
216 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
217 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)))))))");
220 if (cmdargs
.length
> 0) {
221 rep('(load-file "${cmdargs[0]}")');
225 rep("(println (str \"Mal [\" *host-language* \"]\"))");
228 var line
= Compat
.readline("user> ");
229 if (line
== "") { continue; }
230 Compat
.println(rep(line
));
231 } catch (exc
:BlankLine
) {
233 } catch (exc
:haxe
.io
.Eof
) {
235 } catch (exc
:Dynamic) {
236 if (Type
.getClass(exc
) == MalException
) {
237 Compat
.println("Error: " + Printer
.pr_str(exc
.obj
, true));
239 Compat
.println("Error: " + exc
);