perl: Abolish _assoc_BANG.
[jackhill/mal.git] / perl / types.pm
CommitLineData
b5dedee0
JM
1package types;
2use strict;
60f2b363 3use warnings FATAL => qw(all);
b5dedee0 4use Exporter 'import';
89bd4de1 5our @EXPORT_OK = qw(_sequential_Q _equal_Q _clone
b8ee29b2 6 $nil $true $false _nil_Q _true_Q _false_Q
9e1b1752 7 _number_Q _symbol _symbol_Q _string_Q _keyword _keyword_Q _list_Q _vector_Q _sub_Q _function_Q
19a341f0
BH
8 _hash_map _hash_map_Q _atom_Q);
9use List::Util qw(pairs pairmap);
a5a66058
JM
10
11use Data::Dumper;
12
13# General functions
14
15sub _sequential_Q {
16 return _list_Q($_[0]) || _vector_Q($_[0])
17}
18
19sub _equal_Q {
20 my ($a, $b) = @_;
21 my ($ota, $otb) = (ref $a, ref $b);
a5a66058
JM
22 if (!(($ota eq $otb) || (_sequential_Q($a) && _sequential_Q($b)))) {
23 return 0;
24 }
7b341cf0
BH
25 if ($a->isa('Symbol')) {
26 return $$a eq $$b;
27 } elsif ($a->isa('Sequence')) {
28 if (! (scalar(@$a) == scalar(@$b))) {
29 return 0;
30 }
31 for (my $i=0; $i<scalar(@$a); $i++) {
32 if (! _equal_Q($a->[$i], $b->[$i])) {
33 return 0;
34 }
35 }
36 return 1;
37 } elsif ($a->isa('HashMap')) {
90865c5b 38 if (! (scalar(keys %$a) == scalar(keys %$b))) {
7b341cf0
BH
39 return 0;
40 }
90865c5b
BH
41 foreach my $k (keys %$a) {
42 if (!_equal_Q($a->{$k}, $b->{$k})) {
7b341cf0
BH
43 return 0;
44 }
45 }
46 return 1;
47 } else {
48 return $$a eq $$b;
a5a66058
JM
49 }
50 return 0;
51}
52
89bd4de1 53sub _clone {
90865c5b 54 no overloading '%{}';
89bd4de1 55 my ($obj) = @_;
b7ad769a
BH
56 if ($obj->isa('CoreFunction')) {
57 return FunctionRef->new( $obj );
58 } else {
59 return bless {%{$obj}}, ref $obj;
89bd4de1
JM
60 }
61}
62
63# Errors/Exceptions
64
65{
66 package BlankException;
67 sub new { my $class = shift; bless String->new("Blank Line") => $class }
68}
69
a5a66058 70# Scalars
b5dedee0
JM
71
72{
73 package Nil;
e3c3ea02
BH
74 # Allow nil to be treated as an empty list or hash-map.
75 use overload '@{}' => sub { [] }, '%{}' => sub { {} }, fallback => 1;
b5dedee0 76 sub new { my $class = shift; my $s = 'nil'; bless \$s => $class }
e3c3ea02 77 sub rest { List->new([]) }
b5dedee0
JM
78}
79{
80 package True;
81 sub new { my $class = shift; my $s = 'true'; bless \$s => $class }
82}
83{
84 package False;
85 sub new { my $class = shift; my $s = 'false'; bless \$s => $class }
86}
87
88our $nil = Nil->new();
89our $true = True->new();
90our $false = False->new();
91
16354bb4
JM
92sub _nil_Q { return $_[0] eq $nil }
93sub _true_Q { return $_[0] eq $true }
94sub _false_Q { return $_[0] eq $false }
95
96
b5dedee0
JM
97{
98 package Integer;
c9de2e82 99 sub new { my $class = shift; bless \do { my $x=$_[0] }, $class }
b5dedee0 100}
7b341cf0 101sub _number_Q { $_[0]->isa('Integer') }
b5dedee0 102
a3b0621d 103
b5dedee0
JM
104{
105 package Symbol;
c9de2e82 106 sub new { my $class = shift; bless \do { my $x=$_[0] }, $class }
b5dedee0 107}
7b341cf0 108sub _symbol_Q { $_[0]->isa('Symbol') }
a3b0621d
JM
109
110
7b341cf0 111sub _string_Q { $_[0]->isa('String') && ${$_[0]} !~ /^\x{029e}/; }
2176357c
DM
112
113
b8ee29b2 114sub _keyword { return String->new(("\x{029e}".$_[0])); }
7b341cf0 115sub _keyword_Q { $_[0]->isa('String') && ${$_[0]} =~ /^\x{029e}/; }
b8ee29b2
JM
116
117
b5dedee0
JM
118{
119 package String;
120 sub new { my $class = shift; bless \$_[0] => $class }
121}
122
a3b0621d 123
20601f5f 124# Sequences
a5a66058 125
b5dedee0 126{
20601f5f 127 package Sequence;
ea7a2d2f 128 use overload '@{}' => sub { $_[0]->{val} }, fallback => 1;
89bd4de1 129 sub new { my $class = shift; bless {'meta'=>$nil, 'val'=>$_[0]}, $class }
333646bb 130 sub meta { $_[0]->{meta} }
89bd4de1
JM
131 #sub _val { $_[0]->{val}->[$_[1]]->{val}; } # return value of nth item
132 sub rest { my @arr = @{$_[0]->{val}}; List->new([@arr[1..$#arr]]); }
133 sub slice { my @arr = @{$_[0]->{val}}; List->new([@arr[$_[1]..$_[2]]]); }
b5dedee0
JM
134}
135
20601f5f
BH
136# Lists
137
138{
139 package List;
140 use parent -norequire, 'Sequence';
141}
142
7b341cf0 143sub _list_Q { $_[0]->isa('List') }
a5a66058 144
a3b0621d 145
a5a66058 146# Vectors
a3b0621d 147
b5dedee0
JM
148{
149 package Vector;
20601f5f 150 use parent -norequire, 'Sequence';
b5dedee0
JM
151}
152
7b341cf0 153sub _vector_Q { $_[0]->isa('Vector') }
a5a66058
JM
154
155
156# Hash Maps
a3b0621d 157
b5dedee0
JM
158{
159 package HashMap;
90865c5b
BH
160 use overload '%{}' => sub { no overloading '%{}'; $_[0]->{val} },
161 fallback => 1;
89bd4de1 162 sub new { my $class = shift; bless {'meta'=>$nil, 'val'=>$_[0]}, $class }
90865c5b
BH
163 sub meta { no overloading '%{}'; $_[0]->{meta} }
164 sub get { no overloading '%{}'; $_[0]->{val}->{$_[1]}; }
b5dedee0
JM
165}
166
19a341f0 167sub _hash_map { HashMap->new( { pairmap { $$a => $b } @_ } ) }
16354bb4 168
7b341cf0 169sub _hash_map_Q { $_[0]->isa('HashMap') }
16354bb4 170
60f2b363
JM
171
172# Functions
173
174{
175 package Function;
78f0b085 176 use overload '&{}' => sub { my $f = shift; sub { $f->apply(\@_) } },
90865c5b 177 fallback => 1;
60f2b363
JM
178 sub new {
179 my $class = shift;
180 my ($eval, $ast, $env, $params) = @_;
89bd4de1
JM
181 bless {'meta'=>$nil,
182 'eval'=>$eval,
60f2b363
JM
183 'ast'=>$ast,
184 'env'=>$env,
b50cb97c
JM
185 'params'=>$params,
186 'ismacro'=>0}, $class
60f2b363 187 }
333646bb 188 sub meta { $_[0]->{meta} }
60f2b363 189 sub gen_env {
b50cb97c
JM
190 my $self = $_[0];
191 return Env->new($self->{env}, $self->{params}, $_[1]);
60f2b363
JM
192 }
193 sub apply {
b50cb97c
JM
194 my $self = $_[0];
195 return &{ $self->{eval} }($self->{ast}, gen_env($self, $_[1]));
60f2b363
JM
196 }
197}
198
2a8ad7c8
BH
199sub _sub_Q { $_[0]->isa('CoreFunction') || $_[0]->isa('FunctionRef') }
200sub _function_Q { $_[0]->isa('Function') }
9e1b1752 201
89bd4de1 202
14b035f4
MK
203# FunctionRef
204
205{
206 package FunctionRef;
42e58bce 207 use overload '&{}' => sub { $_[0]->{code} }, fallback => 1;
14b035f4
MK
208 sub new {
209 my ($class, $code) = @_;
210 bless {'meta'=>$nil,
211 'code'=>$code}, $class
212 }
333646bb 213 sub meta { $_[0]->{meta} }
14b035f4
MK
214}
215
b7ad769a
BH
216# Core Functions
217
218{
219 package CoreFunction;
220 sub meta { $nil }
221}
222
14b035f4 223
89bd4de1
JM
224# Atoms
225
226{
227 package Atom;
90865c5b 228 use overload '${}' => sub { \($_[0]->{val}) }, fallback => 1;
89bd4de1 229 sub new { my $class = shift; bless {'meta'=>$nil, 'val'=>$_[0]}, $class }
333646bb 230 sub meta { $_[0]->{meta} }
89bd4de1
JM
231}
232
7b341cf0 233sub _atom_Q { $_[0]->isa('Atom') }
89bd4de1 234
b5dedee0 2351;