8 import core
.stdc
.stdlib
;
16 bool is_pair(MalType ast
)
18 auto lst
= cast(MalSequential
) ast
;
19 if (lst
is null) return false;
20 return lst
.elements
.length
> 0;
23 MalType
quasiquote(MalType ast
)
27 return new MalList([sym_quote
, ast
]);
29 auto ast_seq
= verify_cast
!MalSequential(ast
);
30 auto aste
= ast_seq
.elements
;
31 if (aste
[0] == sym_unquote
)
38 auto ast0_seq
= verify_cast
!MalSequential(aste
[0]);
39 if (ast0_seq
.elements
[0] == sym_splice_unquote
)
41 return new MalList([new MalSymbol("concat"), ast0_seq
.elements
[1], quasiquote(new MalList(aste
[1..$]))]);
45 return new MalList([new MalSymbol("cons"), quasiquote(aste
[0]), quasiquote(new MalList(aste
[1..$]))]);
48 bool is_macro_call(MalType ast
, Env env
)
50 auto lst
= cast(MalList
) ast
;
51 if (lst
is null) return false;
52 if (lst
.elements
.length
== 0) return false;
53 auto sym0
= cast(MalSymbol
) lst
.elements
[0];
54 if (sym0
is null) return false;
55 if (env
.find(sym0
) is null) return false;
56 auto val
= env
.get(sym0
);
57 auto val_func
= cast(MalFunc
) val
;
58 if (val_func
is null) return false;
59 return val_func
.is_macro
;
62 MalType
macroexpand(MalType ast
, Env env
)
64 while (is_macro_call(ast
, env
))
66 auto ast_list
= verify_cast
!MalList(ast
);
67 auto sym0
= verify_cast
!MalSymbol(ast_list
.elements
[0]);
68 auto macrofunc
= verify_cast
!MalFunc(env
.get(sym0
));
69 auto rest
= ast_list
.elements
[1..$];
70 auto callenv
= new Env(macrofunc
.def_env
, macrofunc
.arg_names
, rest
);
71 ast
= EVAL(macrofunc
.func_body
, callenv
);
76 MalType
READ(string
str)
81 MalType
eval_ast(MalType ast
, Env env
)
83 if (auto sym
= cast(MalSymbol
)ast
)
87 else if (auto lst
= cast(MalList
)ast
)
89 auto el
= array(lst
.elements
.map
!(e
=> EVAL(e
, env
)));
90 return new MalList(el
);
92 else if (auto lst
= cast(MalVector
)ast
)
94 auto el
= array(lst
.elements
.map
!(e
=> EVAL(e
, env
)));
95 return new MalVector(el
);
97 else if (auto hm
= cast(MalHashmap
)ast
)
99 typeof(hm
.data
) new_data
;
100 foreach (string k
, MalType v
; hm
.data
)
102 new_data
[k
] = EVAL(v
, env
);
104 return new MalHashmap(new_data
);
112 MalType
EVAL(MalType ast
, Env env
)
116 MalList ast_list
= cast(MalList
) ast
;
117 if (ast_list
is null)
119 return eval_ast(ast
, env
);
122 ast
= macroexpand(ast
, env
);
123 ast_list
= cast(MalList
) ast
;
124 if (ast_list
is null)
126 return eval_ast(ast
, env
);
129 auto aste
= ast_list
.elements
;
130 if (aste
.length
== 0)
134 auto a0_sym
= cast(MalSymbol
) aste
[0];
135 auto sym_name
= a0_sym
is null ?
"" : a0_sym
.name
;
139 auto a1
= verify_cast
!MalSymbol(aste
[1]);
140 return env
.set(a1
, EVAL(aste
[2], env
));
143 auto a1
= verify_cast
!MalSequential(aste
[1]);
144 auto let_env
= new Env(env
);
145 foreach (kv
; chunks(a1
.elements
, 2))
147 if (kv
.length
< 2) throw new Exception("let* requires even number of elements");
148 auto var_name
= verify_cast
!MalSymbol(kv
[0]);
149 let_env
.set(var_name
, EVAL(kv
[1], let_env
));
159 ast
= quasiquote(aste
[1]);
163 auto a1
= verify_cast
!MalSymbol(aste
[1]);
164 auto mac
= verify_cast
!MalFunc(EVAL(aste
[2], env
));
166 return env
.set(a1
, mac
);
169 return macroexpand(aste
[1], env
);
172 auto all_but_last
= new MalList(aste
[1..$-1]);
173 eval_ast(all_but_last
, env
);
178 auto cond
= EVAL(aste
[1], env
);
179 if (cond
.is_truthy())
196 auto args_list
= verify_cast
!MalSequential(aste
[1]);
197 return new MalFunc(args_list
.elements
, aste
[2], env
);
200 auto el
= verify_cast
!MalList(eval_ast(ast
, env
));
201 if (el
.elements
.length
== 0)
203 throw new Exception("Expected a non-empty list");
205 auto first
= el
.elements
[0];
206 auto rest
= el
.elements
[1..$];
207 if (auto funcobj
= cast(MalFunc
)first
)
209 auto callenv
= new Env(funcobj
.def_env
, funcobj
.arg_names
, rest
);
210 ast
= funcobj
.func_body
;
214 else if (auto builtinfuncobj
= cast(MalBuiltinFunc
)first
)
216 return builtinfuncobj
.fn(rest
);
220 throw new Exception("Expected a function");
226 string
PRINT(MalType ast
)
231 MalType
re(string
str, Env env
)
233 return EVAL(READ(str), env
);
236 string
rep(string
str, Env env
)
238 return PRINT(re(str, env
));
241 static MalList
create_argv_list(string
[] args
)
243 if (args
.length
<= 2) return new MalList([]);
244 return new MalList(array(args
[2..$].map
!(s
=> cast(MalType
)(new MalString(s
)))));
247 void main(string
[] args
)
249 Env repl_env
= new Env(null);
250 foreach (string sym_name
, BuiltinStaticFuncType f
; core_ns
)
252 repl_env
.set(new MalSymbol(sym_name
), new MalBuiltinFunc(f
, sym_name
));
255 BuiltinFuncType eval_func
= (a
...) {
256 verify_args_count(a
, 1);
257 return EVAL(a
[0], repl_env
);
259 repl_env
.set(new MalSymbol("eval"), new MalBuiltinFunc(eval_func
, "eval"));
260 repl_env
.set(new MalSymbol("*ARGV*"), create_argv_list(args
));
262 // core.mal: defined using the language itself
263 re("(def! not (fn* (a) (if a false true)))", repl_env
);
264 re("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env
);
265 re("(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)))))))", repl_env
);
271 rep("(load-file \"" ~ args
[1] ~ "\")", repl_env
);
276 writeln("Error: ", e
.msg
);
283 string line
= _readline("user> ");
284 if (line
is null) break;
285 if (line
.length
== 0) continue;
288 writeln(rep(line
, repl_env
));
292 writeln("Error: ", e
.msg
);