16 MalType
READ(string
str)
21 MalType
eval_ast(MalType ast
, Env env
)
23 if (typeid(ast
) == typeid(MalSymbol
))
25 auto sym
= verify_cast
!MalSymbol(ast
);
28 else if (typeid(ast
) == typeid(MalList
))
30 auto lst
= verify_cast
!MalList(ast
);
31 auto el
= array(lst
.elements
.map
!(e
=> EVAL(e
, env
)));
32 return new MalList(el
);
34 else if (typeid(ast
) == typeid(MalVector
))
36 auto lst
= verify_cast
!MalVector(ast
);
37 auto el
= array(lst
.elements
.map
!(e
=> EVAL(e
, env
)));
38 return new MalVector(el
);
40 else if (typeid(ast
) == typeid(MalHashmap
))
42 auto hm
= verify_cast
!MalHashmap(ast
);
43 typeof(hm
.data
) new_data
;
44 foreach (string k
, MalType v
; hm
.data
)
46 new_data
[k
] = EVAL(v
, env
);
48 return new MalHashmap(new_data
);
56 MalType
EVAL(MalType ast
, Env env
)
60 MalList ast_list
= cast(MalList
) ast
;
63 return eval_ast(ast
, env
);
66 auto aste
= ast_list
.elements
;
67 auto a0_sym
= cast(MalSymbol
) aste
[0];
68 auto sym_name
= a0_sym
is null ?
"" : a0_sym
.name
;
72 auto a1
= verify_cast
!MalSymbol(aste
[1]);
73 return env
.set(a1
, EVAL(aste
[2], env
));
76 auto a1
= verify_cast
!MalSequential(aste
[1]);
77 auto let_env
= new Env(env
);
78 foreach (kv
; chunks(a1
.elements
, 2))
80 if (kv
.length
< 2) throw new Exception("let* requires even number of elements");
81 auto var_name
= verify_cast
!MalSymbol(kv
[0]);
82 let_env
.set(var_name
, EVAL(kv
[1], let_env
));
89 auto all_but_last
= new MalList(aste
[1..$-1]);
90 eval_ast(all_but_last
, env
);
95 auto cond
= EVAL(aste
[1], env
);
113 auto args_list
= verify_cast
!MalSequential(aste
[1]);
114 return new MalFunc(args_list
.elements
, aste
[2], env
);
117 auto el
= verify_cast
!MalList(eval_ast(ast
, env
));
118 if (el
.elements
.length
== 0)
120 throw new Exception("Expected a non-empty list");
122 auto first
= el
.elements
[0];
123 auto rest
= el
.elements
[1..$];
124 if (typeid(first
) == typeid(MalFunc
))
126 auto funcobj
= verify_cast
!MalFunc(first
);
127 auto callenv
= new Env(funcobj
.def_env
, funcobj
.arg_names
, rest
);
128 ast
= funcobj
.func_body
;
132 else if (typeid(first
) == typeid(MalBuiltinFunc
))
134 auto builtinfuncobj
= verify_cast
!MalBuiltinFunc(first
);
135 return builtinfuncobj
.fn(rest
);
139 throw new Exception("Expected a function");
145 string
PRINT(MalType ast
)
150 MalType
re(string
str, Env env
)
152 return EVAL(READ(str), env
);
155 string
rep(string
str, Env env
)
157 return PRINT(re(str, env
));
160 static MalList
create_argv_list(string
[] args
)
162 if (args
.length
<= 2) return new MalList([]);
163 return new MalList(array(args
[2..$].map
!(s
=> cast(MalType
)(new MalString(s
)))));
166 void main(string
[] args
)
168 Env repl_env
= new Env(null);
169 foreach (string sym_name
, BuiltinStaticFuncType f
; core_ns
)
171 repl_env
.set(new MalSymbol(sym_name
), new MalBuiltinFunc(f
, sym_name
));
174 BuiltinFuncType eval_func
= (a
...) {
175 verify_args_count(a
, 1);
176 return EVAL(a
[0], repl_env
);
178 repl_env
.set(new MalSymbol("eval"), new MalBuiltinFunc(eval_func
, "eval"));
179 repl_env
.set(new MalSymbol("*ARGV*"), create_argv_list(args
));
181 // core.mal: defined using the language itself
182 re("(def! not (fn* (a) (if a false true)))", repl_env
);
183 re("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env
);
189 rep("(load-file \"" ~ args
[1] ~ "\")", repl_env
);
194 writeln("Error: ", e
.msg
);
195 std
.c
.process
.exit(1);
201 string line
= _readline("user> ");
202 if (line
is null) break;
203 if (line
.length
== 0) continue;
206 writeln(rep(line
, repl_env
));
210 writeln("Error: ", e
.msg
);