diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-21 21:13:04 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-21 21:13:04 -0500 |
| commit | fd637e0385f4b39a0d9e109c8b4b8afe20874fa7 (patch) | |
| tree | c5bec673165e467a827a2a69d314e5fc3a3386b7 | |
| parent | 074cd7482bd5e6b0f5170f0128d417c1560ca00e (diff) | |
| download | mal-fd637e0385f4b39a0d9e109c8b4b8afe20874fa7.tar.gz mal-fd637e0385f4b39a0d9e109c8b4b8afe20874fa7.zip | |
Perl: add step7_quote
| -rw-r--r-- | perl/core.pm | 20 | ||||
| -rw-r--r-- | perl/reader.pm | 22 | ||||
| -rw-r--r-- | perl/step7_quote.pl | 160 |
3 files changed, 195 insertions, 7 deletions
diff --git a/perl/core.pm b/perl/core.pm index a529d21..a243dcb 100644 --- a/perl/core.pm +++ b/perl/core.pm @@ -38,6 +38,23 @@ sub slurp { } +# List functions + +sub cons { + my ($a, $b) = @_; + my @new_arr = @{[$a]}; + push @new_arr, @$b; + List->new(\@new_arr); +} + +sub concat { + my ($a, $b) = @_; + my @new_arr = @{$a}; + push @new_arr, @$b; + List->new(\@new_arr); +} + + our $core_ns = { '=' => sub { _equal_Q($_[0][0], $_[0][1]) ? $true : $false }, @@ -58,6 +75,9 @@ our $core_ns = { 'list' => sub { $_[0] }, 'list?' => sub { _list_Q($_[0][0]) ? $true : $false }, + + 'cons' => sub { cons($_[0][0], $_[0][1]) }, + 'concat' => sub { concat($_[0][0], $_[0][1]) }, 'empty?' => sub { scalar(@{$_[0][0]}) == 0 ? $true : $false }, 'count' => sub { Integer->new(scalar(@{$_[0][0]})) }, }; diff --git a/perl/reader.pm b/perl/reader.pm index 6fac066..e173910 100644 --- a/perl/reader.pm +++ b/perl/reader.pm @@ -78,13 +78,21 @@ sub read_form { my($rdr) = @_; my $token = $rdr->peek(); given ($token) { - when(')') { die "unexpected ')'"; } - when('(') { return read_list($rdr, 'List'); } - when(']') { die "unexpected ']'"; } - when('[') { return read_list($rdr, 'Vector', '[', ']'); } - when('}') { die "unexpected '}'"; } - when('{') { return read_list($rdr, 'HashMap', '{', '}'); } - default { return read_atom($rdr); } + when("'") { $rdr->next(); List->new([Symbol->new('quote'), + read_form($rdr)]) } + when('`') { $rdr->next(); List->new([Symbol->new('quasiquote'), + read_form($rdr)]) } + when('~') { $rdr->next(); List->new([Symbol->new('unquote'), + read_form($rdr)]) } + when('~@') { $rdr->next(); List->new([Symbol->new('splice-unquote'), + read_form($rdr)]) } + when(')') { die "unexpected ')'" } + when('(') { return read_list($rdr, 'List') } + when(']') { die "unexpected ']'" } + when('[') { return read_list($rdr, 'Vector', '[', ']') } + when('}') { die "unexpected '}'" } + when('{') { return read_list($rdr, 'HashMap', '{', '}') } + default { return read_atom($rdr) } } } diff --git a/perl/step7_quote.pl b/perl/step7_quote.pl new file mode 100644 index 0000000..7b9f92f --- /dev/null +++ b/perl/step7_quote.pl @@ -0,0 +1,160 @@ +use strict; +use warnings FATAL => qw(all); +use readline qw(readline); +use feature qw(switch); +use Data::Dumper; + +use types qw($nil $true $false _sequential_Q); +use reader; +use printer; +use env; +use core qw($core_ns); + +# read +sub READ { + my $str = shift; + return reader::read_str($str); +} + +# eval +sub is_pair { + my ($x) = @_; + return _sequential_Q($x) && scalar(@$x) > 0; +} + +sub quasiquote { + my ($ast) = @_; + if (!is_pair($ast)) { + return List->new([Symbol->new("quote"), $ast]); + } elsif (((ref $ast->[0]) =~ /^Symbol/) && + ${$ast->[0]} eq 'unquote') { + return $ast->[1]; + } elsif (is_pair($ast->[0]) && + ((ref $ast->[0][0]) =~ /^Symbol/) && + ${$ast->[0][0]} eq 'splice-unquote') { + return List->new([Symbol->new("concat"), + $ast->[0][1], + quasiquote($ast->rest())]); + } else { + return List->new([Symbol->new("cons"), + quasiquote($ast->[0]), + quasiquote($ast->rest())]); + } +} + +sub eval_ast { + my($ast, $env) = @_; + given (ref $ast) { + when (/^Symbol/) { + $env->get($$ast); + } + when (/^List/) { + my @lst = map {EVAL($_, $env)} @$ast; + return List->new(\@lst); + } + default { + return $ast; + } + } +} + +sub EVAL { + my($ast, $env) = @_; + + while (1) { + + #print "EVAL: " . printer::_pr_str($ast) . "\n"; + if (! ((ref $ast) =~ /^List/)) { + return eval_ast($ast, $env); + } + + # apply list + my ($a0, $a1, $a2, $a3) = @$ast; + given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { + when (/^def!$/) { + my $res = EVAL($a2, $env); + 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)); + } + return EVAL($a2, $let_env); + } + when (/^quote$/) { + return $a1; + } + when (/^quasiquote$/) { + return EVAL(quasiquote($a1), $env); + } + when (/^do$/) { + eval_ast($ast->slice(1, $#{$ast}-1), $env); + $ast = $ast->[$#{$ast}]; + } + when (/^if$/) { + my $cond = EVAL($a1, $env); + if ($cond eq $nil || $cond eq $false) { + $ast = $a3 ? $a3 : $nil; + } else { + $ast = $a2; + } + } + when (/^fn\*$/) { + return Function->new(\&EVAL, $a2, $env, $a1); + } + default { + my $el = eval_ast($ast, $env); + my $f = $el->[0]; + if ((ref $f) =~ /^Function/) { + $ast = $f->{ast}; + $env = $f->gen_env($el->rest()); + } else { + return &{ $f }($el->rest()); + } + } + } + + } # TCO while loop +} + +# print +sub PRINT { + my $exp = shift; + return printer::_pr_str($exp); +} + +# repl +my $repl_env = Env->new(); +sub REP { + my $str = shift; + return PRINT(EVAL(READ($str), $repl_env)); +} + +# 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); }); +my @_argv = map {String->new($_)} @ARGV[1..$#ARGV]; +$repl_env->set('*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) { + REP("(load-file \"" . $ARGV[0] . "\")"); + exit 0; +} +while (1) { + my $line = 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"; + } +} |
