2 use feature
qw(switch);
4 use warnings FATAL
=> qw(all);
5 no if $] >= 5.018, warnings
=> "experimental::smartmatch";
7 our @EXPORT_OK = qw( read_str );
9 use types
qw($nil $true $false _keyword _hash_map);
17 bless { position => 0, tokens => shift } => $class
19 sub next { my $self = shift; return $self->{tokens}[$self->{position}++] }
20 sub peek { my $self = shift; return $self->{tokens}[$self->{position}] }
25 my @tokens = $str =~ /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)/g;
26 return grep {! /^;|^$/} @tokens;
31 my $token = $rdr->next();
33 when(/^-?[0-9]+$/) { return Integer->new($token) }
35 my %escaped_chars = ( "\\\\" => "\\", "\\\"" => "\"", "\\n" => "\n" );
36 my $str = substr $token, 1, -1;
37 $str =~ s/\\./$escaped_chars{$&}/ge;
38 return String->new($str)
41 die "expected '\"', got EOF";
43 when(/^:/) { return _keyword(substr($token,1)) }
44 when(/^nil$/) { return $nil }
45 when(/^true$/) { return $true }
46 when(/^false$/) { return $false }
47 default { return Symbol->new($token) }
52 my($rdr,$class,$start,$end) = @_;
53 $start = $start || '(';
56 my $token = $rdr->next();
58 if ($token ne $start) {
59 die "expected '$start'";
62 $token = $rdr->peek();
63 if (! defined($token)) {
64 die "expected '$end', got EOF";
66 last if ($token eq $end);
67 push(@lst, read_form($rdr));
70 if ($class eq 'List') {
71 return List->new(\@lst);
72 } elsif ($class eq 'Vector') {
73 return Vector->new(\@lst);
75 return _hash_map(@lst);
81 my $token = $rdr->peek();
83 when("'") { $rdr->next(); List->new([Symbol->new('quote'),
85 when('`') { $rdr->next(); List->new([Symbol->new('quasiquote'),
87 when('~') { $rdr->next(); List->new([Symbol->new('unquote'),
89 when('~@') { $rdr->next(); List->new([Symbol->new('splice-unquote'),
91 when('^') { $rdr->next(); my $meta = read_form($rdr);
92 List->new([Symbol->new('with-meta'),
93 read_form($rdr), $meta]) }
94 when('@') { $rdr->next(); List->new([Symbol->new('deref'),
97 when(')') { die "unexpected ')'" }
98 when('(') { return read_list($rdr, 'List') }
99 when(']') { die "unexpected ']'" }
100 when('[') { return read_list($rdr, 'Vector', '[', ']') }
101 when('}') { die "unexpected '}'" }
102 when('{') { return read_list($rdr, 'HashMap', '{', '}') }
103 default { return read_atom($rdr) }
109 my @tokens = tokenize($str);
110 #print "tokens: " . Dumper(\@tokens);
111 if (scalar(@tokens) == 0) { die BlankException->new(); }
112 return read_form(Reader->new(\@tokens));
115 #print Dumper(read_str("123"));
116 #print Dumper(read_str("+"));
117 #print Dumper(read_str("\"abc\""));
118 #print Dumper(read_str("nil"));
119 #print Dumper(read_str("true"));
120 #print Dumper(read_str("false"));
121 #print Dumper(read_str("(+ 2 3)"));
122 #print Dumper(read_str("(foo 2 (3 4))"));