Perl: add step9_interop test
[jackhill/mal.git] / perl / step4_if_fn_do.pl
CommitLineData
a5a66058 1use strict;
60f2b363 2use warnings FATAL => qw(all);
a5a66058
JM
3use readline qw(readline);
4use feature qw(switch);
5use Data::Dumper;
6
7use types qw($nil $true $false);
8use reader;
9use printer;
10use env;
11use core qw($core_ns);
12
13# read
14sub READ {
15 my $str = shift;
16 return reader::read_str($str);
17}
18
19# eval
20sub eval_ast {
21 my($ast, $env) = @_;
22 given (ref $ast) {
23 when (/^Symbol/) {
24 $env->get($$ast);
25 }
26 when (/^List/) {
27 my @lst = map {EVAL($_, $env)} @$ast;
28 return List->new(\@lst);
29 }
30 default {
31 return $ast;
32 }
33 }
34}
35
36sub EVAL {
37 my($ast, $env) = @_;
38 #print "EVAL: " . printer::_pr_str($ast) . "\n";
39 if (! ((ref $ast) =~ /^List/)) {
40 return eval_ast($ast, $env);
41 }
42
43 # apply list
44 my ($a0, $a1, $a2, $a3) = @$ast;
45 given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) {
46 when (/^def!$/) {
47 my $res = EVAL($a2, $env);
48 return $env->set($$a1, $res);
49 }
50 when (/^let\*$/) {
51 my $let_env = Env->new($env);
52 for(my $i=0; $i < scalar(@{$a1}); $i+=2) {
53 $let_env->set(${$a1->[$i]}, EVAL($a1->[$i+1], $let_env));
54 }
55 return EVAL($a2, $let_env);
56 }
57 when (/^do$/) {
58 my $el = eval_ast($ast->rest(), $env);
59 return $el->[$#{$el}];
60 }
61 when (/^if$/) {
62 my $cond = EVAL($a1, $env);
63 if ($cond eq $nil || $cond eq $false) {
64 return $a3 ? EVAL($a3, $env) : $nil;
65 } else {
66 return EVAL($a2, $env);
67 }
68 }
69 when (/^fn\*$/) {
70 return sub {
71 #print "running fn*\n";
72 my $args = $_[0];
73 return EVAL($a2, Env->new($env, $a1, $args));
74 };
75 }
76 default {
77 my $el = eval_ast($ast, $env);
78 my $f = $el->[0];
79 return &{ $f }($el->rest());
80 }
81 }
82}
83
84# print
85sub PRINT {
86 my $exp = shift;
87 return printer::_pr_str($exp);
88}
89
90# repl
91my $repl_env = Env->new();
92sub REP {
93 my $str = shift;
94 return PRINT(EVAL(READ($str), $repl_env));
95}
96
97# core.pl: defined using perl
98foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); }
99
100# core.mal: defined using the language itself
101REP("(def! not (fn* (a) (if a false true)))");
102
103while (1) {
104 my $line = readline("user> ");
105 if (! defined $line) { last; }
106 eval {
107 use autodie; # always "throw" errors
108 print(REP($line), "\n");
109 1;
110 };
111 if (my $err = $@) {
112 chomp $err;
113 print "Error: $err\n";
114 }
115}