aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--docs/TODO3
-rw-r--r--perl/printer.pm33
-rw-r--r--perl/reader.pm102
-rw-r--r--perl/step1_read_print.pl45
-rw-r--r--perl/types.pm55
6 files changed, 241 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 2a99452..394c288 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ PYTHON = python
# Settings
#
-IMPLS = bash c clojure cs java js make mal php ps python ruby
+IMPLS = bash c clojure cs java js make mal perl php ps python ruby
step0 = step0_repl
step1 = step1_read_print
@@ -41,6 +41,7 @@ java_STEP_TO_PROG = java/src/main/java/mal/$($(1)).java
js_STEP_TO_PROG = js/$($(1)).js
make_STEP_TO_PROG = make/$($(1)).mk
mal_STEP_TO_PROG = mal/$($(1)).mal
+perl_STEP_TO_PROG = perl/$($(1)).pl
php_STEP_TO_PROG = php/$($(1)).php
ps_STEP_TO_PROG = ps/$($(1)).ps
python_STEP_TO_PROG = python/$($(1)).py
@@ -55,6 +56,7 @@ java_RUNSTEP = mvn -quiet exec:java -Dexec.mainClass="mal.$($(1))" -Dexec.arg
js_RUNSTEP = node ../$(2) $(3)
make_RUNSTEP = make -f ../$(2) $(3)
mal_RUNSTEP = $(call $(MAL_IMPL)_RUNSTEP,$(1),$(call $(MAL_IMPL)_STEP_TO_PROG,stepA),../$(2),") #"
+perl_RUNSTEP = perl ../$(2) $(3)
php_RUNSTEP = php ../$(2) $(3)
ps_RUNSTEP = $(4)gs -q -dNODISPLAY -- ../$(2) $(3)$(4)
python_RUNSTEP = $(PYTHON) ../$(2) $(3)
diff --git a/docs/TODO b/docs/TODO
index e5926e0..46f7881 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -41,6 +41,9 @@ Mal:
- step5_tco
- step9_interop
+Perl:
+ - object exceptions: http://perldoc.perl.org/functions/die.html
+
PHP:
Postscript:
diff --git a/perl/printer.pm b/perl/printer.pm
new file mode 100644
index 0000000..9741eda
--- /dev/null
+++ b/perl/printer.pm
@@ -0,0 +1,33 @@
+package printer;
+use feature qw(switch);
+use strict;
+use warnings;
+use Exporter 'import';
+our @EXPORT_OK = qw( _pr_str );
+
+use types qw($nil $true $false);
+
+sub _pr_str {
+ my($obj) = @_;
+ given (ref $obj) {
+ when(/^List/) {
+ return '(' . join(' ', map {_pr_str($_)} @$obj) . ')';
+ }
+ when(/^Vector/) {
+ return '[' . join(' ', map {_pr_str($_)} @$obj) . ']';
+ }
+ when(/^HashMap/) {
+ my @elems = ();
+ foreach my $key (keys %$obj) {
+ push(@elems, _pr_str(String->new($key)));
+ push(@elems, _pr_str($obj->{$key}));
+ }
+
+ return '{' . join(' ', @elems) . '}';
+ }
+ when(/^String/) { return '"' . $$obj . '"'; }
+ default { return $$obj; }
+ }
+}
+
+1;
diff --git a/perl/reader.pm b/perl/reader.pm
new file mode 100644
index 0000000..52b1a76
--- /dev/null
+++ b/perl/reader.pm
@@ -0,0 +1,102 @@
+package reader;
+use feature qw(switch);
+use strict;
+use warnings;
+use Exporter 'import';
+our @EXPORT_OK = qw( read_str );
+
+use types qw($nil $true $false);
+
+use Data::Dumper;
+
+{
+ package Reader;
+ sub new {
+ my $class = shift;
+ bless { position => 0, tokens => shift } => $class
+ }
+ sub next { my $self = shift; return $self->{tokens}[$self->{position}++] }
+ sub peek { my $self = shift; return $self->{tokens}[$self->{position}] }
+}
+
+sub tokenize {
+ my($str) = @_;
+ my @tokens = $str =~ /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/g;
+ return grep {not /^;|^$/} @tokens;
+}
+
+sub read_atom {
+ my($rdr) = @_;
+ my $token = $rdr->next();
+ given ($token) {
+ when(/^-?[0-9]+$/) { return Integer->new($token) }
+ when(/^"/) { return String->new(substr $token, 1, -1) }
+ when(/^nil$/) { return $nil }
+ when(/^true$/) { return $true }
+ when(/^false$/) { return $false }
+ default { return Symbol->new($token) }
+ }
+}
+
+sub read_list {
+ my($rdr,$class,$start,$end) = @_;
+ $start = $start || '(';
+ $end = $end || ')';
+
+ my $token = $rdr->next();
+ my @lst = ();
+ if ($token ne $start) {
+ die "expected '$start'";
+ }
+ while (($token = $rdr->peek()) ne $end) {
+ if (! defined $token) {
+ die "expected '$end', got EOF";
+ }
+ push(@lst, read_form($rdr));
+ }
+ $rdr->next();
+ if ($class eq 'List') {
+ return List->new(\@lst);
+ } elsif ($class eq 'Vector') {
+ return Vector->new(\@lst);
+ } else {
+ my $hsh = {};
+ for(my $i=0; $i<$#lst; $i+=2) {
+ my $str = $lst[$i];
+ $hsh->{$$str} = $lst[$i+1];
+ }
+ return HashMap->new($hsh);
+ }
+}
+
+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); }
+ }
+}
+
+sub read_str {
+ my($str) = @_;
+ my @tokens = tokenize($str);
+ #print join(" / ", @tokens) . "\n";
+ return read_form(Reader->new(\@tokens));
+}
+
+#print Dumper(read_str("123"));
+#print Dumper(read_str("+"));
+#print Dumper(read_str("\"abc\""));
+#print Dumper(read_str("nil"));
+#print Dumper(read_str("true"));
+#print Dumper(read_str("false"));
+#print Dumper(read_str("(+ 2 3)"));
+#print Dumper(read_str("(foo 2 (3 4))"));
+
+1;
diff --git a/perl/step1_read_print.pl b/perl/step1_read_print.pl
new file mode 100644
index 0000000..8d96ab5
--- /dev/null
+++ b/perl/step1_read_print.pl
@@ -0,0 +1,45 @@
+use strict;
+use warnings;
+use readline qw(readline);
+use feature qw(switch);
+
+use reader qw(read_str);
+use printer qw(_pr_str);
+
+# read
+sub READ {
+ my $str = shift;
+ return read_str($str);
+}
+
+# eval
+sub EVAL {
+ my($ast, $env) = @_;
+ return $ast;
+}
+
+# print
+sub PRINT {
+ my $exp = shift;
+ return _pr_str($exp);
+}
+
+# repl
+sub REP {
+ my $str = shift;
+ return PRINT(EVAL(READ($str), {}));
+}
+
+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";
+ }
+}
diff --git a/perl/types.pm b/perl/types.pm
new file mode 100644
index 0000000..500a1d3
--- /dev/null
+++ b/perl/types.pm
@@ -0,0 +1,55 @@
+package types;
+use strict;
+use warnings;
+use Exporter 'import';
+our @EXPORT_OK = qw( $nil $true $false);
+
+{
+ package Nil;
+ #sub new { my $class = shift; bless {type=>'nil'} => $class }
+ sub new { my $class = shift; my $s = 'nil'; bless \$s => $class }
+}
+{
+ package True;
+ sub new { my $class = shift; my $s = 'true'; bless \$s => $class }
+}
+{
+ package False;
+ sub new { my $class = shift; my $s = 'false'; bless \$s => $class }
+}
+
+our $nil = Nil->new();
+our $true = True->new();
+our $false = False->new();
+
+{
+ package Integer;
+ sub new { my $class = shift; bless \$_[0] => $class }
+}
+
+{
+ package Symbol;
+ sub new { my $class = shift; bless \$_[0] => $class }
+}
+
+{
+ package String;
+ sub new { my $class = shift; bless \$_[0] => $class }
+}
+
+{
+ package List;
+ sub new { my $class = shift; bless $_[0], $class }
+}
+
+{
+ package Vector;
+ sub new { my $class = shift; bless $_[0], $class }
+}
+
+{
+ package HashMap;
+ sub new { my $class = shift; bless $_[0], $class }
+}
+
+1;