2 import types
.Types
.MalType
;
4 import types
.MalException
;
12 static function READ(str
:String
):MalType
{
13 return Reader
.read_str(str
);
17 static function qq_loop(elt
:MalType
, acc
:MalType
) {
19 case MalList([MalSymbol("splice-unquote"), arg
]):
20 return MalList([MalSymbol("concat"), arg
, acc
]);
22 return MalList([MalSymbol("cons"), quasiquote(elt
), acc
]);
25 static function qq_foldr(xs
:Array
<MalType
>) {
26 var acc
= MalList([]);
27 for (i
in 1 ... xs
.length
+1) {
28 acc
= qq_loop (xs
[xs
.length
-i
], acc
);
32 static function quasiquote(ast
:MalType
) {
34 case MalList([MalSymbol("unquote"), arg
]): arg
;
35 case MalList(l
): qq_foldr(l
);
36 case MalVector(l
): MalList([MalSymbol("vec"), qq_foldr(l
)]);
37 case MalSymbol(_
) |
MalHashMap(_
): MalList([MalSymbol("quote"), ast
]);
42 static function is_macro(ast
:MalType
, env
:Env
) {
44 case MalList([]): false;
47 return symbol_Q(a0
) &&
48 env
.find(a0
) != null &&
49 _macro_Q(env
.get(a0
));
54 static function macroexpand(ast
:MalType
, env
:Env
) {
55 while (is_macro(ast
, env
)) {
56 var mac
= env
.get(first(ast
));
58 case MalFunc(f
,_
,_
,_
,_
,_
):
59 ast
= f(_list(ast
).slice(1));
66 static function eval_ast(ast
:MalType
, env
:Env
) {
68 case MalSymbol(s
): env
.get(ast
);
70 MalList(l
.map(function(x
) { return EVAL(x
, env
); }));
72 MalVector(l
.map(function(x
) { return EVAL(x
, env
); }));
74 var new_map
= new Map
<String
,MalType
>();
76 new_map
[k
] = EVAL(m
[k
], env
);
83 static function EVAL(ast
:MalType
, env
:Env
):MalType
{
85 if (!list_Q(ast
)) { return eval_ast(ast
, env
); }
88 ast
= macroexpand(ast
, env
);
89 if (!list_Q(ast
)) { return eval_ast(ast
, env
); }
91 var alst
= _list(ast
);
92 if (alst
.length
== 0) { return 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("quasiquoteexpand"):
112 return quasiquote(alst
[1]);
113 case MalSymbol("quasiquote"):
114 ast
= quasiquote(alst
[1]);
116 case MalSymbol("defmacro!"):
117 var func
= EVAL(alst
[2], env
);
118 return switch (func
) {
119 case MalFunc(f
,ast
,e
,params
,_
,_
):
120 env
.set(alst
[1], MalFunc(f
,ast
,e
,params
,true,nil
));
122 throw "Invalid defmacro! call";
124 case MalSymbol("macroexpand"):
125 return macroexpand(alst
[1], env
);
126 case MalSymbol("do"):
127 var el
= eval_ast(MalList(alst
.slice(1, alst
.length
-1)), env
);
130 case MalSymbol("if"):
131 var cond
= EVAL(alst
[1], env
);
132 if (cond
!= MalFalse
&& cond
!= MalNil
) {
134 } else if (alst
.length
> 3) {
140 case MalSymbol("fn*"):
141 return MalFunc(function (args
) {
142 return EVAL(alst
[2], new Env(env
, _list(alst
[1]), args
));
143 },alst
[2],env
,alst
[1],false,nil
);
145 var el
= eval_ast(ast
, env
);
148 case MalFunc(f
,a
,e
,params
,_
,_
):
149 var args
= _list(el
).slice(1);
152 env
= new Env(e
, _list(params
), args
);
157 case _
: throw "Call of non-function";
164 static function PRINT(exp
:MalType
):String
{
165 return Printer
.pr_str(exp
, true);
169 static var repl_env
= new Env(null);
171 static function rep(line
:String
):String
{
172 return PRINT(EVAL(READ(line
), repl_env
));
175 public static function main() {
176 // core.EXT: defined using Haxe
177 for (k
in Core
.ns
.keys()) {
178 repl_env
.set(MalSymbol(k
), MalFunc(Core
.ns
[k
],null,null,null,false,nil
));
181 var evalfn
= MalFunc(function(args
) {
182 return EVAL(args
[0], repl_env
);
183 },null,null,null,false,nil
);
184 repl_env
.set(MalSymbol("eval"), evalfn
);
186 var cmdargs
= Compat
.cmdline_args();
187 var argarray
= cmdargs
.map(function(a
) { return MalString(a
); });
188 repl_env
.set(MalSymbol("*ARGV*"), MalList(argarray
.slice(1)));
190 // core.mal: defined using the language itself
191 rep("(def! not (fn* (a) (if a false true)))");
192 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
193 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)))))))");
196 if (cmdargs
.length
> 0) {
197 rep('(load-file "${cmdargs[0]}")');
203 var line
= Compat
.readline("user> ");
204 if (line
== "") { continue; }
205 Compat
.println(rep(line
));
206 } catch (exc
:BlankLine
) {
208 } catch (exc
:haxe
.io
.Eof
) {
210 } catch (exc
:Dynamic) {
211 if (Type
.getClass(exc
) == MalException
) {
212 Compat
.println("Error: " + Printer
.pr_str(exc
.obj
, true));
214 Compat
.println("Error: " + exc
);