Commit | Line | Data |
---|---|---|
b5dedee0 JM |
1 | package types; |
2 | use strict; | |
60f2b363 | 3 | use warnings FATAL => qw(all); |
b5dedee0 | 4 | use Exporter 'import'; |
89bd4de1 | 5 | our @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); |
9 | use List::Util qw(pairs pairmap); | |
a5a66058 JM |
10 | |
11 | use Data::Dumper; | |
12 | ||
13 | # General functions | |
14 | ||
15 | sub _sequential_Q { | |
16 | return _list_Q($_[0]) || _vector_Q($_[0]) | |
17 | } | |
18 | ||
19 | sub _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 | 53 | sub _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 | ||
88 | our $nil = Nil->new(); | |
89 | our $true = True->new(); | |
90 | our $false = False->new(); | |
91 | ||
16354bb4 JM |
92 | sub _nil_Q { return $_[0] eq $nil } |
93 | sub _true_Q { return $_[0] eq $true } | |
94 | sub _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 | 101 | sub _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 | 108 | sub _symbol_Q { $_[0]->isa('Symbol') } |
a3b0621d JM |
109 | |
110 | ||
7b341cf0 | 111 | sub _string_Q { $_[0]->isa('String') && ${$_[0]} !~ /^\x{029e}/; } |
2176357c DM |
112 | |
113 | ||
b8ee29b2 | 114 | sub _keyword { return String->new(("\x{029e}".$_[0])); } |
7b341cf0 | 115 | sub _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 | 143 | sub _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 | 153 | sub _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 | 167 | sub _hash_map { HashMap->new( { pairmap { $$a => $b } @_ } ) } |
16354bb4 | 168 | |
7b341cf0 | 169 | sub _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 |
199 | sub _sub_Q { $_[0]->isa('CoreFunction') || $_[0]->isa('FunctionRef') } |
200 | sub _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 | 233 | sub _atom_Q { $_[0]->isa('Atom') } |
89bd4de1 | 234 | |
b5dedee0 | 235 | 1; |