Runtest should fail on bad test data. Fix interop tests.
[jackhill/mal.git] / perl6 / step4_if_fn_do.pl
1 use v6;
2 use lib IO::Path.new($?FILE).dirname;
3 use reader;
4 use printer;
5 use types;
6 use env;
7 use core;
8
9 sub read ($str) {
10 return read_str($str);
11 }
12
13 sub eval_ast ($ast, $env) {
14 given $ast {
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 }
20 }
21 }
22
23 sub eval ($ast, $env) {
24 return eval_ast($ast, $env) if $ast !~~ MalList;
25 return $ast if !$ast.elems;
26
27 my ($a0, $a1, $a2, $a3) = $ast.val;
28 given $a0.val {
29 when 'def!' {
30 return $env.set($a1.val, eval($a2, $env));
31 }
32 when 'let*' {
33 my $new_env = MalEnv.new($env);
34 for |$a1.val -> $key, $value {
35 $new_env.set($key.val, eval($value, $new_env));
36 }
37 return eval($a2, $new_env);
38 }
39 when 'do' {
40 return eval_ast(MalList([$ast[1..*]]), $env)[*-1];
41 }
42 when 'if' {
43 return eval($a1, $env) !~~ MalNil|MalFalse
44 ?? return eval($a2, $env)
45 !! return $a3 ?? eval($a3, $env) !! $NIL;
46 }
47 when 'fn*' {
48 return MalCode(-> *@args {
49 my @binds = $a1 ?? $a1.map(*.val) !! ();
50 eval($a2, MalEnv.new($env, @binds, @args));
51 });
52 }
53 default {
54 my ($func, @args) = eval_ast($ast, $env).val;
55 return $func.apply(|@args);
56 }
57 }
58 }
59
60 sub print ($exp) {
61 return pr_str($exp, True);
62 }
63
64 my $repl_env = MalEnv.new;
65
66 sub rep ($str) {
67 return print(eval(read($str), $repl_env));
68 }
69
70 sub MAIN {
71 $repl_env.set(.key, .value) for %core::ns;
72 rep(q{(def! not (fn* (a) (if a false true)))});
73
74 while (my $line = prompt 'user> ').defined {
75 say rep($line);
76 CATCH {
77 when X::MalException { .Str.say }
78 }
79 }
80 }