2 use lib IO
::Path
.new
($?FILE
).dirname
;
10 return read_str
($str);
13 sub eval_ast
($ast, $env) {
15 when MalSymbol
{ $env.get
($ast.val
) || die X
::MalNotFound
.new
(name
=> $ast.val
) }
16 when MalList
{ MalList
([$ast.map({ eval($_, $env) })]) }
17 when MalVector
{ MalVector
([$ast.map({ eval($_, $env) })]) }
18 when MalHashMap
{ MalHashMap
($ast.kv
.map({ $^a
=> eval($^b
, $env) }).Hash
) }
19 default { $ast // $NIL }
23 sub eval ($ast, $env) {
24 return eval_ast
($ast, $env) if $ast !~~ MalList
;
25 return $ast if !$ast.elems
;
27 my ($a0, $a1, $a2, $a3) = $ast.val
;
30 return $env.set
($a1.val
, eval($a2, $env));
33 my $new_env = MalEnv
.new
($env);
34 for |$a1.val
-> $key, $value {
35 $new_env.set
($key.val
, eval($value, $new_env));
37 return eval($a2, $new_env);
40 return eval_ast
(MalList
([$ast[1..*]]), $env)[*-1];
43 return eval($a1, $env) !~~ MalNil
|MalFalse
44 ??
return eval($a2, $env)
45 !! return $a3 ??
eval($a3, $env) !! $NIL;
48 return MalCode
(-> *@args {
49 my @binds = $a1 ??
$a1.map(*.val
) !! ();
50 eval($a2, MalEnv
.new
($env, @binds, @args));
54 my ($func, @args) = eval_ast
($ast, $env).val
;
55 return $func.apply
(|@args);
61 return pr_str
($exp, True
);
64 my $repl_env = MalEnv
.new
;
67 return print(eval(read($str), $repl_env));
71 $repl_env.set
(.key
, .value
) for %core::ns
;
72 rep
(q{(def! not (fn* (a) (if a false true)))});
74 while (my $line = prompt
'user> ').defined {
77 when X
::MalException
{ .Str
.say }