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 is copy
, $env is copy
) {
25 return eval_ast
($ast, $env) if $ast !~~ MalList
;
26 return $ast if !$ast.elems
;
28 my ($a0, $a1, $a2, $a3) = $ast.val
;
31 return $env.set
($a1.val
, eval($a2, $env));
34 my $new_env = MalEnv
.new
($env);
35 for |$a1.val
-> $key, $value {
36 $new_env.set
($key.val
, eval($value, $new_env));
42 eval_ast
(MalList
([$ast[1..*-2]]), $env);
46 if eval($a1, $env) ~~ MalNil
|MalFalse
{
47 return $NIL if $a3 ~~ $NIL;
55 my @binds = $a1 ??
$a1.map(*.val
) !! ();
57 eval($a2, MalEnv
.new
($env, @binds, @args));
59 return MalFunction
($a2, $env, @binds, &fn
);
62 my ($func, @args) = eval_ast
($ast, $env).val
;
63 return $func.apply
(|@args) if $func !~~ MalFunction
;
65 $env = MalEnv
.new
($func.env
, $func.params
, @args);
72 return pr_str
($exp, True
);
75 my $repl_env = MalEnv
.new
;
78 return print(eval(read($str), $repl_env));
81 sub MAIN
($source_file?
, *@args) {
82 $repl_env.set
(.key
, .value
) for %core::ns
;
83 $repl_env.set
('eval', MalCode
({ eval($^a
, $repl_env) }));
84 $repl_env.set
('*ARGV*', MalList
([@args.map({ MalString
($_) })]));
85 rep
(q{(def! not (fn* (a) (if a false true)))});
86 rep
(q{(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))});
88 if ($source_file.defined) {
89 rep
("(load-file \"$source_file\")");
93 while (my $line = prompt
'user> ').defined {
96 when X
::MalException
{ .Str
.say }