perl: De-FATALise warnings.
[jackhill/mal.git] / perl / reader.pm
CommitLineData
b5dedee0 1package reader;
b5dedee0 2use strict;
1d834731 3use warnings;
01c97316 4no if $] >= 5.018, warnings => "experimental::smartmatch";
29702ab6
BH
5use feature qw(switch);
6
b5dedee0
JM
7use Exporter 'import';
8our @EXPORT_OK = qw( read_str );
9
b8ee29b2 10use types qw($nil $true $false _keyword _hash_map);
b5dedee0
JM
11
12use Data::Dumper;
13
14{
6708078b 15 package Mal::Reader;
b5dedee0
JM
16 sub new {
17 my $class = shift;
18 bless { position => 0, tokens => shift } => $class
19 }
20 sub next { my $self = shift; return $self->{tokens}[$self->{position}++] }
21 sub peek { my $self = shift; return $self->{tokens}[$self->{position}] }
22}
23
24sub tokenize {
25 my($str) = @_;
5f80c83f 26 my @tokens = $str =~ /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)/g;
a3b0621d 27 return grep {! /^;|^$/} @tokens;
b5dedee0
JM
28}
29
30sub read_atom {
31 my($rdr) = @_;
32 my $token = $rdr->next();
33 given ($token) {
6708078b 34 when(/^-?[0-9]+$/) { return Mal::Integer->new($token) }
16309256 35 when(/^"(?:\\.|[^\\"])*"$/) {
0794206d 36 my %escaped_chars = ( "\\\\" => "\\", "\\\"" => "\"", "\\n" => "\n" );
a5a66058 37 my $str = substr $token, 1, -1;
0794206d 38 $str =~ s/\\./$escaped_chars{$&}/ge;
6708078b 39 return Mal::String->new($str)
a5a66058 40 }
16309256 41 when(/^"/) {
5f80c83f
JM
42 die "expected '\"', got EOF";
43 }
b8ee29b2 44 when(/^:/) { return _keyword(substr($token,1)) }
1753f4a9
BH
45 when('nil') { return $nil }
46 when('true') { return $true }
47 when('false') { return $false }
6708078b 48 default { return Mal::Symbol->new($token) }
b5dedee0
JM
49 }
50}
51
52sub read_list {
53 my($rdr,$class,$start,$end) = @_;
74d12000
BH
54 $start = $start // '(';
55 $end = $end // ')';
b5dedee0
JM
56
57 my $token = $rdr->next();
58 my @lst = ();
59 if ($token ne $start) {
60 die "expected '$start'";
61 }
5f80c83f
JM
62 while (1) {
63 $token = $rdr->peek();
64 if (! defined($token)) {
b5dedee0
JM
65 die "expected '$end', got EOF";
66 }
5f80c83f 67 last if ($token eq $end);
b5dedee0
JM
68 push(@lst, read_form($rdr));
69 }
70 $rdr->next();
6708078b
BH
71 if ($class eq 'Mal::List') {
72 return Mal::List->new(\@lst);
73 } elsif ($class eq 'Mal::Vector') {
74 return Mal::Vector->new(\@lst);
b5dedee0 75 } else {
16354bb4 76 return _hash_map(@lst);
b5dedee0
JM
77 }
78}
79
80sub read_form {
81 my($rdr) = @_;
82 my $token = $rdr->peek();
83 given ($token) {
6708078b
BH
84 when("'") { $rdr->next(); Mal::List->new([Mal::Symbol->new('quote'),
85 read_form($rdr)]) }
86 when('`') { $rdr->next();
87 Mal::List->new([Mal::Symbol->new('quasiquote'),
88 read_form($rdr)]) }
89 when('~') { $rdr->next(); Mal::List->new([Mal::Symbol->new('unquote'),
90 read_form($rdr)]) }
91 when('~@') { $rdr->next();
92 Mal::List->new([Mal::Symbol->new('splice-unquote'),
93 read_form($rdr)]) }
89bd4de1 94 when('^') { $rdr->next(); my $meta = read_form($rdr);
6708078b
BH
95 Mal::List->new([Mal::Symbol->new('with-meta'),
96 read_form($rdr), $meta]) }
97 when('@') { $rdr->next(); Mal::List->new([Mal::Symbol->new('deref'),
98 read_form($rdr)]) }
89bd4de1 99
fd637e03 100 when(')') { die "unexpected ')'" }
6708078b 101 when('(') { return read_list($rdr, 'Mal::List') }
fd637e03 102 when(']') { die "unexpected ']'" }
6708078b 103 when('[') { return read_list($rdr, 'Mal::Vector', '[', ']') }
fd637e03 104 when('}') { die "unexpected '}'" }
6708078b 105 when('{') { return read_list($rdr, 'Mal::HashMap', '{', '}') }
fd637e03 106 default { return read_atom($rdr) }
b5dedee0
JM
107 }
108}
109
110sub read_str {
111 my($str) = @_;
112 my @tokens = tokenize($str);
89bd4de1 113 #print "tokens: " . Dumper(\@tokens);
6708078b
BH
114 if (scalar(@tokens) == 0) { die Mal::BlankException->new(); }
115 return read_form(Mal::Reader->new(\@tokens));
b5dedee0
JM
116}
117
118#print Dumper(read_str("123"));
119#print Dumper(read_str("+"));
120#print Dumper(read_str("\"abc\""));
121#print Dumper(read_str("nil"));
122#print Dumper(read_str("true"));
123#print Dumper(read_str("false"));
124#print Dumper(read_str("(+ 2 3)"));
125#print Dumper(read_str("(foo 2 (3 4))"));
126
1271;