2 import types
.Types
.MalType
;
11 static function READ(str
:String
):MalType
{
12 return Reader
.read_str(str
);
16 static function is_pair(ast
:MalType
) {
18 case MalList(l
) |
MalVector(l
): l
.length
> 0;
23 static function quasiquote(ast
:MalType
) {
25 return MalList([MalSymbol("quote"), ast
]);
28 if (_equal_Q(a0
, MalSymbol("unquote"))) {
30 } else if (is_pair(a0
)) {
32 if (_equal_Q(a00
, MalSymbol("splice-unquote"))) {
33 return MalList([MalSymbol("concat"),
35 quasiquote(rest(ast
))]);
38 return MalList([MalSymbol("cons"),
40 quasiquote(rest(ast
))]);
44 static function is_macro(ast
:MalType
, env
:Env
) {
48 return symbol_Q(a0
) &&
49 env
.find(a0
) != null &&
50 _macro_Q(env
.get(a0
));
55 static function macroexpand(ast
:MalType
, env
:Env
) {
56 while (is_macro(ast
, env
)) {
57 var mac
= env
.get(first(ast
));
59 case MalFunc(f
,_
,_
,_
,_
,_
):
60 ast
= f(_list(ast
).slice(1));
67 static function eval_ast(ast
:MalType
, env
:Env
) {
69 case MalSymbol(s
): env
.get(ast
);
71 MalList(l
.map(function(x
) { return EVAL(x
, env
); }));
73 MalVector(l
.map(function(x
) { return EVAL(x
, env
); }));
75 var new_map
= new Map
<String
,MalType
>();
77 new_map
[k
] = EVAL(m
[k
], env
);
84 static function EVAL(ast
:MalType
, env
:Env
):MalType
{
86 if (!list_Q(ast
)) { return eval_ast(ast
, env
); }
89 ast
= macroexpand(ast
, env
);
90 if (!list_Q(ast
)) { return eval_ast(ast
, env
); }
92 var alst
= _list(ast
);
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
);
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
));
104 case _
: throw "Invalid let*";
109 case MalSymbol("quote"):
111 case MalSymbol("quasiquote"):
112 ast
= quasiquote(alst
[1]);
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
));
120 throw "Invalid defmacro! call";
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
);
128 case MalSymbol("if"):
129 var cond
= EVAL(alst
[1], env
);
130 if (cond
!= MalFalse
&& cond
!= MalNil
) {
132 } else if (alst
.length
> 3) {
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
);
143 var el
= eval_ast(ast
, env
);
146 case MalFunc(f
,a
,e
,params
,_
,_
):
147 var args
= _list(el
).slice(1);
150 env
= new Env(e
, _list(params
), args
);
155 case _
: throw "Call of non-function";
162 static function PRINT(exp
:MalType
):String
{
163 return Printer
.pr_str(exp
, true);
167 static var repl_env
= new Env(null);
169 static function rep(line
:String
):String
{
170 return PRINT(EVAL(READ(line
), repl_env
));
173 public static function main() {
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
));
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
);
184 var cmdargs
= Compat
.cmdline_args();
185 var argarray
= cmdargs
.map(function(a
) { return MalString(a
); });
186 repl_env
.set(MalSymbol("*ARGV*"), MalList(argarray
.slice(1)));
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))))))))");
195 if (cmdargs
.length
> 0) {
196 rep('(load-file "${cmdargs[0]}")');
202 var line
= Compat
.readline("user> ");
203 if (line
== "") { continue; }
204 Compat
.println(rep(line
));
205 } catch (exc
:BlankLine
) {
207 } catch (exc
:haxe
.io
.Eof
) {
209 } catch (exc
:Dynamic) {