use strict;
use warnings FATAL => qw(all);
-use readline qw(readline);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
+use File::Basename;
+use lib dirname (__FILE__);
+use readline qw(mal_readline set_rl_mode);
use feature qw(switch);
use Data::Dumper;
-use types qw($nil $true $false);
+use types qw($nil $true $false _list_Q);
use reader;
use printer;
use env;
my($ast, $env) = @_;
given (ref $ast) {
when (/^Symbol/) {
- $env->get($$ast);
+ $env->get($ast);
}
when (/^List/) {
- my @lst = map {EVAL($_, $env)} @$ast;
+ my @lst = map {EVAL($_, $env)} @{$ast->{val}};
return List->new(\@lst);
}
+ when (/^Vector/) {
+ my @lst = map {EVAL($_, $env)} @{$ast->{val}};
+ return Vector->new(\@lst);
+ }
+ when (/^HashMap/) {
+ my $new_hm = {};
+ foreach my $k (keys( %{ $ast->{val} })) {
+ $new_hm->{$k} = EVAL($ast->get($k), $env);
+ }
+ return HashMap->new($new_hm);
+ }
default {
return $ast;
}
while (1) {
#print "EVAL: " . printer::_pr_str($ast) . "\n";
- if (! ((ref $ast) =~ /^List/)) {
+ if (! _list_Q($ast)) {
return eval_ast($ast, $env);
}
# apply list
- my ($a0, $a1, $a2, $a3) = @$ast;
+ my ($a0, $a1, $a2, $a3) = @{$ast->{val}};
given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) {
when (/^def!$/) {
my $res = EVAL($a2, $env);
- return $env->set($$a1, $res);
+ return $env->set($a1, $res);
}
when (/^let\*$/) {
my $let_env = Env->new($env);
- for(my $i=0; $i < scalar(@{$a1}); $i+=2) {
- $let_env->set(${$a1->[$i]}, EVAL($a1->[$i+1], $let_env));
+ for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) {
+ $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env));
}
- return EVAL($a2, $let_env);
+ $ast = $a2;
+ $env = $let_env;
+ # Continue loop (TCO)
}
when (/^do$/) {
- eval_ast($ast->slice(1, $#{$ast}-1), $env);
- $ast = $ast->[$#{$ast}];
+ eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
+ $ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
}
default {
my $el = eval_ast($ast, $env);
- my $f = $el->[0];
+ my $f = $el->nth(0);
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
}
# core.pl: defined using perl
-foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); }
-$repl_env->set('eval', sub { EVAL($_[0][0], $repl_env); });
+foreach my $n (%$core_ns) {
+ $repl_env->set(Symbol->new($n), $core_ns->{$n});
+}
+$repl_env->set(Symbol->new('eval'), sub { EVAL($_[0]->nth(0), $repl_env); });
my @_argv = map {String->new($_)} @ARGV[1..$#ARGV];
-$repl_env->set('*ARGV*', List->new(\@_argv));
+$repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv));
# core.mal: defined using the language itself
REP("(def! not (fn* (a) (if a false true)))");
REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
-if ($#ARGV > 0) {
+if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") {
+ set_rl_mode("raw");
+ shift @ARGV;
+}
+if (scalar(@ARGV) > 0) {
REP("(load-file \"" . $ARGV[0] . "\")");
exit 0;
}
while (1) {
- my $line = readline("user> ");
+ my $line = mal_readline("user> ");
if (! defined $line) { last; }
- eval {
- use autodie; # always "throw" errors
- print(REP($line), "\n");
- 1;
- };
- if (my $err = $@) {
- chomp $err;
- print "Error: $err\n";
- }
+ do {
+ local $@;
+ my $ret;
+ eval {
+ use autodie; # always "throw" errors
+ print(REP($line), "\n");
+ 1;
+ } or do {
+ my $err = $@;
+ given (ref $err) {
+ when (/^BlankException/) {
+ # ignore and continue
+ }
+ default {
+ chomp $err;
+ print "Error: $err\n";
+ }
+ }
+ };
+ };
}