line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CljPerl::Evaler;
|
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# use strict;
|
4
|
2
|
|
|
2
|
|
11
|
use warnings;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
53
|
|
5
|
2
|
|
|
2
|
|
1070
|
use CljPerl::Reader;
|
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
54
|
|
6
|
2
|
|
|
2
|
|
949
|
use CljPerl::Var;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
87
|
|
7
|
2
|
|
|
2
|
|
9
|
use CljPerl::Printer;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
33
|
|
8
|
2
|
|
|
2
|
|
8
|
use File::Spec;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
37
|
|
9
|
2
|
|
|
2
|
|
10
|
use File::Basename;
|
|
2
|
|
|
|
|
21
|
|
|
2
|
|
|
|
|
138
|
|
10
|
2
|
|
|
2
|
|
899
|
use Coro;
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.10';
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $namespace_key = "0namespace0";
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub new {
|
17
|
|
|
|
|
|
|
my $class = shift;
|
18
|
|
|
|
|
|
|
my @default_namespace = ();
|
19
|
|
|
|
|
|
|
my @scopes = ({$namespace_key=>\@default_namespace});
|
20
|
|
|
|
|
|
|
my @file_stack = ();
|
21
|
|
|
|
|
|
|
my @caller = ();
|
22
|
|
|
|
|
|
|
my $self = {class=>$class,
|
23
|
|
|
|
|
|
|
scopes=>\@scopes,
|
24
|
|
|
|
|
|
|
loaded_files=>{},
|
25
|
|
|
|
|
|
|
file_stack=>\@file_stack,
|
26
|
|
|
|
|
|
|
caller=>\@caller,
|
27
|
|
|
|
|
|
|
exception=>undef,
|
28
|
|
|
|
|
|
|
quotation_scope=>0,
|
29
|
|
|
|
|
|
|
syntaxquotation_scope=>0};
|
30
|
|
|
|
|
|
|
bless $self;
|
31
|
|
|
|
|
|
|
return $self;
|
32
|
|
|
|
|
|
|
}
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub scopes {
|
35
|
|
|
|
|
|
|
my $self = shift;
|
36
|
|
|
|
|
|
|
return $self->{scopes};
|
37
|
|
|
|
|
|
|
}
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub push_scope {
|
40
|
|
|
|
|
|
|
my $self = shift;
|
41
|
|
|
|
|
|
|
my $context = shift;
|
42
|
|
|
|
|
|
|
my %c = %{$context};
|
43
|
|
|
|
|
|
|
my @ns = @{$c{$namespace_key}};
|
44
|
|
|
|
|
|
|
$c{$namespace_key} = \@ns;
|
45
|
|
|
|
|
|
|
unshift @{$self->scopes()}, \%c;
|
46
|
|
|
|
|
|
|
}
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub pop_scope {
|
49
|
|
|
|
|
|
|
my $self = shift;
|
50
|
|
|
|
|
|
|
shift @{$self->scopes()};
|
51
|
|
|
|
|
|
|
}
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub current_scope {
|
54
|
|
|
|
|
|
|
my $self = shift;
|
55
|
|
|
|
|
|
|
my $scope = @{$self->scopes()}[0];
|
56
|
|
|
|
|
|
|
return $scope;
|
57
|
|
|
|
|
|
|
}
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub push_caller {
|
60
|
|
|
|
|
|
|
my $self = shift;
|
61
|
|
|
|
|
|
|
my $ast = shift;
|
62
|
|
|
|
|
|
|
unshift @{$self->{caller}}, $ast;
|
63
|
|
|
|
|
|
|
}
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub pop_caller {
|
66
|
|
|
|
|
|
|
my $self = shift;
|
67
|
|
|
|
|
|
|
shift @{$self->{caller}};
|
68
|
|
|
|
|
|
|
}
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub caller_size {
|
71
|
|
|
|
|
|
|
my $self = shift;
|
72
|
|
|
|
|
|
|
scalar @{$self->{caller}};
|
73
|
|
|
|
|
|
|
}
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub push_namespace {
|
76
|
|
|
|
|
|
|
my $self = shift;
|
77
|
|
|
|
|
|
|
my $namespace = shift;
|
78
|
|
|
|
|
|
|
my $scope = $self->current_scope();
|
79
|
|
|
|
|
|
|
unshift @{$scope->{$namespace_key}}, $namespace;
|
80
|
|
|
|
|
|
|
}
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub pop_namespace {
|
83
|
|
|
|
|
|
|
my $self = shift;
|
84
|
|
|
|
|
|
|
my $scope = $self->current_scope();
|
85
|
|
|
|
|
|
|
shift @{$scope->{$namespace_key}};
|
86
|
|
|
|
|
|
|
}
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub current_namespace {
|
89
|
|
|
|
|
|
|
my $self = shift;
|
90
|
|
|
|
|
|
|
my $scope = $self->current_scope();
|
91
|
|
|
|
|
|
|
my $namespace = @{$scope->{$namespace_key}}[0];
|
92
|
|
|
|
|
|
|
return "" if(!defined $namespace);
|
93
|
|
|
|
|
|
|
return $namespace;
|
94
|
|
|
|
|
|
|
}
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub new_var {
|
97
|
|
|
|
|
|
|
my $self = shift;
|
98
|
|
|
|
|
|
|
my $name = shift;
|
99
|
|
|
|
|
|
|
my $value = shift;
|
100
|
|
|
|
|
|
|
my $scope = $self->current_scope();
|
101
|
|
|
|
|
|
|
$name = $self->current_namespace() . "#" . $name;
|
102
|
|
|
|
|
|
|
$scope->{$name} = CljPerl::Var->new($name, $value);
|
103
|
|
|
|
|
|
|
}
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub var {
|
106
|
|
|
|
|
|
|
my $self = shift;
|
107
|
|
|
|
|
|
|
my $name = shift;
|
108
|
|
|
|
|
|
|
my $scope = $self->current_scope();
|
109
|
|
|
|
|
|
|
if(exists $scope->{$name}) {
|
110
|
|
|
|
|
|
|
return $scope->{$name};
|
111
|
|
|
|
|
|
|
} elsif(exists $scope->{$self->current_namespace() . "#" . $name}){
|
112
|
|
|
|
|
|
|
return $scope->{$self->current_namespace() . "#" . $name};
|
113
|
|
|
|
|
|
|
} elsif(exists $scope->{"#" . $name}) {
|
114
|
|
|
|
|
|
|
return $scope->{"#" . $name};
|
115
|
|
|
|
|
|
|
};
|
116
|
|
|
|
|
|
|
return undef;
|
117
|
|
|
|
|
|
|
}
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub current_file {
|
120
|
|
|
|
|
|
|
my $self = shift;
|
121
|
|
|
|
|
|
|
my $sd = scalar @{$self->{file_stack}};
|
122
|
|
|
|
|
|
|
if($sd == 0) {
|
123
|
|
|
|
|
|
|
return ".";
|
124
|
|
|
|
|
|
|
} else {
|
125
|
|
|
|
|
|
|
return ${$self->{file_stack}}[$sd-1];
|
126
|
|
|
|
|
|
|
}
|
127
|
|
|
|
|
|
|
}
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub search_file {
|
130
|
|
|
|
|
|
|
my $self = shift;
|
131
|
|
|
|
|
|
|
my $file = shift;
|
132
|
|
|
|
|
|
|
foreach my $ext ("", ".clp") {
|
133
|
|
|
|
|
|
|
if(-f "$file$ext") {
|
134
|
|
|
|
|
|
|
return "$file$ext";
|
135
|
|
|
|
|
|
|
} elsif(-f dirname($self->current_file()) . "/$file$ext") {
|
136
|
|
|
|
|
|
|
return dirname($self->current_file()) . "/$file$ext";
|
137
|
|
|
|
|
|
|
} elsif(-f $file . $ext) {
|
138
|
|
|
|
|
|
|
return $file . $ext;
|
139
|
|
|
|
|
|
|
};
|
140
|
|
|
|
|
|
|
foreach my $p (@INC) {
|
141
|
|
|
|
|
|
|
if(-f "$p/$file$ext") {
|
142
|
|
|
|
|
|
|
return "$p/$file$ext";
|
143
|
|
|
|
|
|
|
};
|
144
|
|
|
|
|
|
|
}
|
145
|
|
|
|
|
|
|
}
|
146
|
|
|
|
|
|
|
CljPerl::Logger::error("cannot find " . $file);
|
147
|
|
|
|
|
|
|
}
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub load {
|
150
|
|
|
|
|
|
|
my $self = shift;
|
151
|
|
|
|
|
|
|
my $file = shift;
|
152
|
|
|
|
|
|
|
CljPerl::Logger::error("cannot require file " . $file . " in non-global scope")
|
153
|
|
|
|
|
|
|
if scalar @{$self->scopes()} > 1;
|
154
|
|
|
|
|
|
|
$file = File::Spec->rel2abs($self->search_file($file));
|
155
|
|
|
|
|
|
|
return 1 if exists $self->{loaded_files}->{$file};
|
156
|
|
|
|
|
|
|
$self->{loaded_files}->{$file} = 1;
|
157
|
|
|
|
|
|
|
push @{$self->{file_stack}}, $file;
|
158
|
|
|
|
|
|
|
my $res = $self->read($file);
|
159
|
|
|
|
|
|
|
pop @{$self->{file_stack}};
|
160
|
|
|
|
|
|
|
return $res;
|
161
|
|
|
|
|
|
|
}
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub read {
|
164
|
|
|
|
|
|
|
my $self = shift;
|
165
|
|
|
|
|
|
|
my $file = shift;
|
166
|
|
|
|
|
|
|
my $reader = CljPerl::Reader->new();
|
167
|
|
|
|
|
|
|
$reader->read_file($file);
|
168
|
|
|
|
|
|
|
my $res = undef;
|
169
|
|
|
|
|
|
|
$reader->ast()->each(sub {$res = $self->_eval($_[0])});
|
170
|
|
|
|
|
|
|
return $res;
|
171
|
|
|
|
|
|
|
}
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub eval {
|
174
|
|
|
|
|
|
|
my $self = shift;
|
175
|
|
|
|
|
|
|
my $str = shift;
|
176
|
|
|
|
|
|
|
my $reader = CljPerl::Reader->new();
|
177
|
|
|
|
|
|
|
$reader->read_string($str);
|
178
|
|
|
|
|
|
|
my $res = undef;
|
179
|
|
|
|
|
|
|
$reader->ast()->each(sub {$res = $self->_eval($_[0])});
|
180
|
|
|
|
|
|
|
return $res;
|
181
|
|
|
|
|
|
|
}
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
our $builtin_funcs = {
|
184
|
|
|
|
|
|
|
"eval"=>1,
|
185
|
|
|
|
|
|
|
"syntax"=>1,
|
186
|
|
|
|
|
|
|
"catch"=>1,
|
187
|
|
|
|
|
|
|
"exception-label"=>1,
|
188
|
|
|
|
|
|
|
"exception-message"=>1,
|
189
|
|
|
|
|
|
|
"throw"=>1,
|
190
|
|
|
|
|
|
|
"def"=>1,
|
191
|
|
|
|
|
|
|
"set!"=>1,
|
192
|
|
|
|
|
|
|
"let"=>1,
|
193
|
|
|
|
|
|
|
"fn"=>1,
|
194
|
|
|
|
|
|
|
"defmacro"=>1,
|
195
|
|
|
|
|
|
|
"gen-sym"=>1,
|
196
|
|
|
|
|
|
|
"list"=>1,
|
197
|
|
|
|
|
|
|
"car"=>1,
|
198
|
|
|
|
|
|
|
"cdr"=>1,
|
199
|
|
|
|
|
|
|
"cons"=>1,
|
200
|
|
|
|
|
|
|
"if"=>1,
|
201
|
|
|
|
|
|
|
"while"=>1,
|
202
|
|
|
|
|
|
|
"begin"=>1,
|
203
|
|
|
|
|
|
|
"length"=>1,
|
204
|
|
|
|
|
|
|
"reverse"=>1,
|
205
|
|
|
|
|
|
|
"object-id"=>1,
|
206
|
|
|
|
|
|
|
"type"=>1,
|
207
|
|
|
|
|
|
|
"perlobj-type"=>1,
|
208
|
|
|
|
|
|
|
"meta"=>1,
|
209
|
|
|
|
|
|
|
"apply"=>1,
|
210
|
|
|
|
|
|
|
"append"=>1,
|
211
|
|
|
|
|
|
|
"keys"=>1,
|
212
|
|
|
|
|
|
|
"namespace-begin"=>1,
|
213
|
|
|
|
|
|
|
"namespace-end"=>1,
|
214
|
|
|
|
|
|
|
"perl->clj"=>1,
|
215
|
|
|
|
|
|
|
"clj->string"=>1,
|
216
|
|
|
|
|
|
|
"!"=>1,
|
217
|
|
|
|
|
|
|
"not"=>1,
|
218
|
|
|
|
|
|
|
"+"=>1,
|
219
|
|
|
|
|
|
|
"-"=>1,
|
220
|
|
|
|
|
|
|
"*"=>1,
|
221
|
|
|
|
|
|
|
"/"=>1,
|
222
|
|
|
|
|
|
|
"%"=>1,
|
223
|
|
|
|
|
|
|
"=="=>1,
|
224
|
|
|
|
|
|
|
"!="=>1,
|
225
|
|
|
|
|
|
|
">"=>1,
|
226
|
|
|
|
|
|
|
">="=>1,
|
227
|
|
|
|
|
|
|
"<"=>1,
|
228
|
|
|
|
|
|
|
"<="=>1,
|
229
|
|
|
|
|
|
|
"."=>1,
|
230
|
|
|
|
|
|
|
"->"=>1,
|
231
|
|
|
|
|
|
|
"eq"=>1,
|
232
|
|
|
|
|
|
|
"ne"=>1,
|
233
|
|
|
|
|
|
|
"and"=>1,
|
234
|
|
|
|
|
|
|
"or"=>1,
|
235
|
|
|
|
|
|
|
"equal"=>1,
|
236
|
|
|
|
|
|
|
"require"=>1,
|
237
|
|
|
|
|
|
|
"read"=>1,
|
238
|
|
|
|
|
|
|
"println"=>1,
|
239
|
|
|
|
|
|
|
"coro"=>1,
|
240
|
|
|
|
|
|
|
"coro-suspend"=>1,
|
241
|
|
|
|
|
|
|
"coro-sleep"=>1,
|
242
|
|
|
|
|
|
|
"coro-yield"=>1,
|
243
|
|
|
|
|
|
|
"coro-resume"=>1,
|
244
|
|
|
|
|
|
|
"coro-wake"=>1,
|
245
|
|
|
|
|
|
|
"coro-join"=>1,
|
246
|
|
|
|
|
|
|
"coro-current"=>1,
|
247
|
|
|
|
|
|
|
"coro-main"=>1,
|
248
|
|
|
|
|
|
|
"xml-name"=>1,
|
249
|
|
|
|
|
|
|
"trace-vars"=>1};
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
our $empty_list = CljPerl::Seq->new("list");
|
252
|
|
|
|
|
|
|
our $true = CljPerl::Atom->new("bool", "true");
|
253
|
|
|
|
|
|
|
our $false = CljPerl::Atom->new("bool", "false");
|
254
|
|
|
|
|
|
|
our $nil = CljPerl::Atom->new("nil", "nil");
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub bind {
|
257
|
|
|
|
|
|
|
my $self = shift;
|
258
|
|
|
|
|
|
|
my $ast = shift;
|
259
|
|
|
|
|
|
|
my $class = $ast->class();
|
260
|
|
|
|
|
|
|
my $type = $ast->type();
|
261
|
|
|
|
|
|
|
my $value = $ast->value();
|
262
|
|
|
|
|
|
|
if($type eq "symbol" and $value eq "true") {
|
263
|
|
|
|
|
|
|
return $true;
|
264
|
|
|
|
|
|
|
} elsif($type eq "symbol" and $value eq "false") {
|
265
|
|
|
|
|
|
|
return $false;
|
266
|
|
|
|
|
|
|
} elsif($type eq "symbol" and $value eq "nil") {
|
267
|
|
|
|
|
|
|
return $nil;
|
268
|
|
|
|
|
|
|
} elsif($type eq "accessor") {
|
269
|
|
|
|
|
|
|
return CljPerl::Atom->new("accessor", $self->bind($value));
|
270
|
|
|
|
|
|
|
} elsif($type eq "sender") {
|
271
|
|
|
|
|
|
|
return CljPerl::Atom->new("sender", $self->bind($value));
|
272
|
|
|
|
|
|
|
} elsif($type eq "syntaxquotation" or $type eq "quotation") {
|
273
|
|
|
|
|
|
|
$self->{syntaxquotation_scope} += 1 if $type eq "syntaxquotation";
|
274
|
|
|
|
|
|
|
$self->{quotation_scope} += 1 if $type eq "quotation";
|
275
|
|
|
|
|
|
|
my $r = $self->bind($value);
|
276
|
|
|
|
|
|
|
$self->{syntaxquotation_scope} -= 1 if $type eq "syntaxquotation";
|
277
|
|
|
|
|
|
|
$self->{quotation_scope} -= 1 if $type eq "quotation";
|
278
|
|
|
|
|
|
|
return $r;
|
279
|
|
|
|
|
|
|
} elsif(($type eq "symbol" and $self->{syntaxquotation_scope} == 0
|
280
|
|
|
|
|
|
|
and $self->{quotation_scope} == 0) or
|
281
|
|
|
|
|
|
|
($type eq "dequotation" and $self->{syntaxquotation_scope} > 0)) {
|
282
|
|
|
|
|
|
|
$ast->error("dequotation should be in syntax quotation scope")
|
283
|
|
|
|
|
|
|
if ($type eq "dequotation" and $self->{syntaxquotation_scope} == 0);
|
284
|
|
|
|
|
|
|
my $name = $value;
|
285
|
|
|
|
|
|
|
if($type eq "dequotation" and $value =~ /^@(\S+)$/) {
|
286
|
|
|
|
|
|
|
$name = $1;
|
287
|
|
|
|
|
|
|
}
|
288
|
|
|
|
|
|
|
return $ast if exists $builtin_funcs->{$name} or $name =~ /^(\.|->)\S+$/;
|
289
|
|
|
|
|
|
|
my $var = $self->var($name);
|
290
|
|
|
|
|
|
|
$ast->error("unbound symbol") if !defined $var;
|
291
|
|
|
|
|
|
|
return $var->value();
|
292
|
|
|
|
|
|
|
} elsif($type eq "symbol"
|
293
|
|
|
|
|
|
|
and $self->{quotation_scope} > 0) {
|
294
|
|
|
|
|
|
|
my $q = CljPerl::Atom->new("quotation", $value);
|
295
|
|
|
|
|
|
|
return $q;
|
296
|
|
|
|
|
|
|
} elsif($class eq "Seq") {
|
297
|
|
|
|
|
|
|
return $empty_list if $type eq "list" and $ast->size() == 0;
|
298
|
|
|
|
|
|
|
my $list = CljPerl::Seq->new("list");
|
299
|
|
|
|
|
|
|
$list->type($type);
|
300
|
|
|
|
|
|
|
foreach my $i (@{$value}) {
|
301
|
|
|
|
|
|
|
if($i->type() eq "dequotation" and $i->value() =~ /^@/){
|
302
|
|
|
|
|
|
|
my $dl = $self->bind($i);
|
303
|
|
|
|
|
|
|
$i->error("~@ should be given a list but got " . $dl->type()) if $dl->type() ne "list";
|
304
|
|
|
|
|
|
|
foreach my $di (@{$dl->value()}){
|
305
|
|
|
|
|
|
|
$list->append($di);
|
306
|
|
|
|
|
|
|
};
|
307
|
|
|
|
|
|
|
} else {
|
308
|
|
|
|
|
|
|
$list->append($self->bind($i));
|
309
|
|
|
|
|
|
|
}
|
310
|
|
|
|
|
|
|
}
|
311
|
|
|
|
|
|
|
return $list;
|
312
|
|
|
|
|
|
|
};
|
313
|
|
|
|
|
|
|
return $ast;
|
314
|
|
|
|
|
|
|
}
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub _eval {
|
317
|
|
|
|
|
|
|
my $self = shift;
|
318
|
|
|
|
|
|
|
my $ast = shift;
|
319
|
|
|
|
|
|
|
my $class = $ast->class();
|
320
|
|
|
|
|
|
|
my $type = $ast->type();
|
321
|
|
|
|
|
|
|
my $value = $ast->value();
|
322
|
|
|
|
|
|
|
if($type eq "list") {
|
323
|
|
|
|
|
|
|
my $size = $ast->size();
|
324
|
|
|
|
|
|
|
if($size == 0) {
|
325
|
|
|
|
|
|
|
return $empty_list;
|
326
|
|
|
|
|
|
|
};
|
327
|
|
|
|
|
|
|
my $f = $self->_eval($ast->first());
|
328
|
|
|
|
|
|
|
my $ftype = $f->type();
|
329
|
|
|
|
|
|
|
my $fvalue = $f->value();
|
330
|
|
|
|
|
|
|
if($ftype eq "symbol") {
|
331
|
|
|
|
|
|
|
return $self->builtin($f, $ast);
|
332
|
|
|
|
|
|
|
} elsif($ftype eq "key accessor") {
|
333
|
|
|
|
|
|
|
$ast->error("key accessor expects >= 1 arguments") if $size == 1;
|
334
|
|
|
|
|
|
|
my $m = $self->_eval($ast->second());
|
335
|
|
|
|
|
|
|
my $mtype = $m->type();
|
336
|
|
|
|
|
|
|
my $mvalue = $m->value();
|
337
|
|
|
|
|
|
|
$ast->error("key accessor expects a map or meta as the first arguments but got " . $mtype)
|
338
|
|
|
|
|
|
|
if $mtype ne "map" and $mtype ne "meta";
|
339
|
|
|
|
|
|
|
if($size == 2) {
|
340
|
|
|
|
|
|
|
#$ast->error("key " . $fvalue . " does not exist")
|
341
|
|
|
|
|
|
|
return $nil if ! exists $mvalue->{$fvalue};
|
342
|
|
|
|
|
|
|
return $mvalue->{$fvalue};
|
343
|
|
|
|
|
|
|
} elsif($size == 3) {
|
344
|
|
|
|
|
|
|
my $v = $self->_eval($ast->third());
|
345
|
|
|
|
|
|
|
if($v->type() eq "nil"){
|
346
|
|
|
|
|
|
|
delete $mvalue->{$fvalue};
|
347
|
|
|
|
|
|
|
return $nil;
|
348
|
|
|
|
|
|
|
} else {
|
349
|
|
|
|
|
|
|
$mvalue->{$fvalue} = $v;
|
350
|
|
|
|
|
|
|
return $mvalue->{$fvalue};
|
351
|
|
|
|
|
|
|
};
|
352
|
|
|
|
|
|
|
} else {
|
353
|
|
|
|
|
|
|
$ast->error("key accessor expects <= 2 arguments");
|
354
|
|
|
|
|
|
|
}
|
355
|
|
|
|
|
|
|
} elsif($ftype eq "index accessor") {
|
356
|
|
|
|
|
|
|
$ast->error("index accessor expects >= 1 arguments") if $size == 1;
|
357
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
358
|
|
|
|
|
|
|
my $vtype = $v->type();
|
359
|
|
|
|
|
|
|
my $vvalue = $v->value();
|
360
|
|
|
|
|
|
|
$ast->error("index accessor expects a vector or list or xml as the first arguments but got " . $vtype)
|
361
|
|
|
|
|
|
|
if $vtype ne "vector" and $vtype ne "list"
|
362
|
|
|
|
|
|
|
and $vtype ne "xml";
|
363
|
|
|
|
|
|
|
$ast->error("index is bigger than size") if $fvalue >= scalar @{$vvalue};
|
364
|
|
|
|
|
|
|
if($size == 2) {
|
365
|
|
|
|
|
|
|
return $vvalue->[$fvalue];
|
366
|
|
|
|
|
|
|
} elsif($size == 3) {
|
367
|
|
|
|
|
|
|
$vvalue->[$fvalue] = $self->_eval($ast->third());
|
368
|
|
|
|
|
|
|
return $vvalue->[$fvalue];
|
369
|
|
|
|
|
|
|
} else {
|
370
|
|
|
|
|
|
|
$ast->error("index accessor expects <= 2 arguments");
|
371
|
|
|
|
|
|
|
}
|
372
|
|
|
|
|
|
|
} elsif($ftype eq "function") {
|
373
|
|
|
|
|
|
|
my $scope = $f->{context};
|
374
|
|
|
|
|
|
|
my $fn = $fvalue;
|
375
|
|
|
|
|
|
|
my $fargs = $fn->second();
|
376
|
|
|
|
|
|
|
my @rargs = $ast->slice(1 .. $size-1);
|
377
|
|
|
|
|
|
|
my @rrargs = ();
|
378
|
|
|
|
|
|
|
foreach my $arg (@rargs) {
|
379
|
|
|
|
|
|
|
push @rrargs, $self->_eval($arg);
|
380
|
|
|
|
|
|
|
};
|
381
|
|
|
|
|
|
|
$self->push_scope($scope);
|
382
|
|
|
|
|
|
|
$self->push_caller($fn);
|
383
|
|
|
|
|
|
|
my $rest_args = undef;
|
384
|
|
|
|
|
|
|
my $i = 0;
|
385
|
|
|
|
|
|
|
my $fargsvalue = $fargs->value();
|
386
|
|
|
|
|
|
|
my $fargsn = scalar @{$fargsvalue};
|
387
|
|
|
|
|
|
|
my $rrargsn = scalar @rrargs;
|
388
|
|
|
|
|
|
|
for($i=0; $i < $fargsn; $i++) {
|
389
|
|
|
|
|
|
|
my $name = $fargsvalue->[$i]->value();
|
390
|
|
|
|
|
|
|
if($name eq "&"){
|
391
|
|
|
|
|
|
|
$i++;
|
392
|
|
|
|
|
|
|
$name = $fargsvalue->[$i]->value();
|
393
|
|
|
|
|
|
|
$rest_args = CljPerl::Seq->new("list");
|
394
|
|
|
|
|
|
|
$self->new_var($name, $rest_args);
|
395
|
|
|
|
|
|
|
} else {
|
396
|
|
|
|
|
|
|
$ast->error("real arguments < formal arguments") if $i >= $rrargsn;
|
397
|
|
|
|
|
|
|
$self->new_var($name, $rrargs[$i]);
|
398
|
|
|
|
|
|
|
}
|
399
|
|
|
|
|
|
|
};
|
400
|
|
|
|
|
|
|
if(defined $rest_args){
|
401
|
|
|
|
|
|
|
$i -= 2;
|
402
|
|
|
|
|
|
|
for(; $i < $rrargsn; $i ++) {
|
403
|
|
|
|
|
|
|
$rest_args->append($rrargs[$i]);
|
404
|
|
|
|
|
|
|
}
|
405
|
|
|
|
|
|
|
} else {
|
406
|
|
|
|
|
|
|
$ast->error("real arguments > formal arguments") if $i < $rrargsn;
|
407
|
|
|
|
|
|
|
};
|
408
|
|
|
|
|
|
|
my @body = $fn->slice(2 .. $fn->size()-1);
|
409
|
|
|
|
|
|
|
my $res;
|
410
|
|
|
|
|
|
|
foreach my $b (@body){
|
411
|
|
|
|
|
|
|
$res = $self->_eval($b);
|
412
|
|
|
|
|
|
|
};
|
413
|
|
|
|
|
|
|
$self->pop_scope();
|
414
|
|
|
|
|
|
|
$self->pop_caller();
|
415
|
|
|
|
|
|
|
return $res;
|
416
|
|
|
|
|
|
|
} elsif($ftype eq "perlfunction") {
|
417
|
|
|
|
|
|
|
my $meta = undef;
|
418
|
|
|
|
|
|
|
$meta = $self->_eval($ast->second()) if defined $ast->second() and $ast->second()->type() eq "meta";
|
419
|
|
|
|
|
|
|
my $perl_func = $f->value();
|
420
|
|
|
|
|
|
|
my @args = $ast->slice((defined $meta ? 2 : 1) .. $size-1);
|
421
|
|
|
|
|
|
|
return $self->perlfunc_call($perl_func, $meta, \@args);
|
422
|
|
|
|
|
|
|
} elsif($ftype eq "macro") {
|
423
|
|
|
|
|
|
|
my $scope = $f->{context};
|
424
|
|
|
|
|
|
|
my $fn = $fvalue;
|
425
|
|
|
|
|
|
|
my $fargs = $fn->third();
|
426
|
|
|
|
|
|
|
my @rargs = $ast->slice(1 .. $ast->size()-1);
|
427
|
|
|
|
|
|
|
$self->push_scope($scope);
|
428
|
|
|
|
|
|
|
$self->push_caller($fn);
|
429
|
|
|
|
|
|
|
my $rest_args = undef;
|
430
|
|
|
|
|
|
|
my $i = 0;
|
431
|
|
|
|
|
|
|
my $fargsvalue = $fargs->value();
|
432
|
|
|
|
|
|
|
my $fargsn = scalar @{$fargsvalue};
|
433
|
|
|
|
|
|
|
my $rargsn = scalar @rargs;
|
434
|
|
|
|
|
|
|
for($i=0; $i < $fargsn; $i++) {
|
435
|
|
|
|
|
|
|
my $name = $fargsvalue->[$i]->value();
|
436
|
|
|
|
|
|
|
if($name eq "&"){
|
437
|
|
|
|
|
|
|
$i++;
|
438
|
|
|
|
|
|
|
$name = $fargsvalue->[$i]->value();
|
439
|
|
|
|
|
|
|
$rest_args = CljPerl::Seq->new("list");
|
440
|
|
|
|
|
|
|
$self->new_var($name, $rest_args);
|
441
|
|
|
|
|
|
|
} else {
|
442
|
|
|
|
|
|
|
$ast->error("real arguments < formal arguments") if $i >= $rargsn;
|
443
|
|
|
|
|
|
|
$self->new_var($name, $rargs[$i]);
|
444
|
|
|
|
|
|
|
}
|
445
|
|
|
|
|
|
|
};
|
446
|
|
|
|
|
|
|
if(defined $rest_args){
|
447
|
|
|
|
|
|
|
$i -= 2;
|
448
|
|
|
|
|
|
|
for(; $i < $rargsn; $i ++) {
|
449
|
|
|
|
|
|
|
$rest_args->append($rargs[$i]);
|
450
|
|
|
|
|
|
|
}
|
451
|
|
|
|
|
|
|
} else {
|
452
|
|
|
|
|
|
|
$ast->error("real arguments > formal arguments") if $i < $rargsn;
|
453
|
|
|
|
|
|
|
};
|
454
|
|
|
|
|
|
|
my @body = $fn->slice(3 .. $fn->size()-1);
|
455
|
|
|
|
|
|
|
my $res;
|
456
|
|
|
|
|
|
|
foreach my $b (@body){
|
457
|
|
|
|
|
|
|
$res = $self->_eval($b);
|
458
|
|
|
|
|
|
|
};
|
459
|
|
|
|
|
|
|
$self->pop_scope();
|
460
|
|
|
|
|
|
|
$self->pop_caller();
|
461
|
|
|
|
|
|
|
return $self->_eval($res);
|
462
|
|
|
|
|
|
|
} else {
|
463
|
|
|
|
|
|
|
$ast->error("expect a function or function name or index/key accessor");
|
464
|
|
|
|
|
|
|
};
|
465
|
|
|
|
|
|
|
} elsif($type eq "accessor") {
|
466
|
|
|
|
|
|
|
my $av = $self->_eval($value);
|
467
|
|
|
|
|
|
|
my $a = CljPerl::Atom->new("unknown", $av->value());
|
468
|
|
|
|
|
|
|
my $at = $av->type();
|
469
|
|
|
|
|
|
|
if($at eq "number") {
|
470
|
|
|
|
|
|
|
$a->type("index accessor");
|
471
|
|
|
|
|
|
|
} elsif($at eq "string" or $at eq "keyword") {
|
472
|
|
|
|
|
|
|
$a->type("key accessor");
|
473
|
|
|
|
|
|
|
} else {
|
474
|
|
|
|
|
|
|
$ast->error("unsupport type " . $at . " for accessor but got " . $at);
|
475
|
|
|
|
|
|
|
}
|
476
|
|
|
|
|
|
|
return $a;
|
477
|
|
|
|
|
|
|
} elsif($type eq "sender") {
|
478
|
|
|
|
|
|
|
my $sn = $self->_eval($value);
|
479
|
|
|
|
|
|
|
$ast->error("sender expects a string or keyword but got " . $type)
|
480
|
|
|
|
|
|
|
if $sn->type() ne "string"
|
481
|
|
|
|
|
|
|
and $sn->type() ne "keyword";
|
482
|
|
|
|
|
|
|
my $s = CljPerl::Atom->new("symbol", $sn->value());
|
483
|
|
|
|
|
|
|
return $self->bind($s);
|
484
|
|
|
|
|
|
|
} elsif($type eq "symbol") {
|
485
|
|
|
|
|
|
|
return $self->bind($ast);
|
486
|
|
|
|
|
|
|
} elsif($type eq "syntaxquotation") {
|
487
|
|
|
|
|
|
|
return $self->bind($ast);
|
488
|
|
|
|
|
|
|
} elsif($type eq "quotation") {
|
489
|
|
|
|
|
|
|
return $self->bind($ast);
|
490
|
|
|
|
|
|
|
} elsif($class eq "Seq" and $type eq "vector") {
|
491
|
|
|
|
|
|
|
my $v = CljPerl::Atom->new("vector");
|
492
|
|
|
|
|
|
|
my @vv = ();
|
493
|
|
|
|
|
|
|
foreach my $i (@{$value}) {
|
494
|
|
|
|
|
|
|
push @vv, $self->_eval($i);
|
495
|
|
|
|
|
|
|
}
|
496
|
|
|
|
|
|
|
$v->value(\@vv);
|
497
|
|
|
|
|
|
|
return $v;
|
498
|
|
|
|
|
|
|
} elsif($class eq "Seq" and ($type eq "map" or $type eq "meta")) {
|
499
|
|
|
|
|
|
|
my $m = CljPerl::Atom->new("map");
|
500
|
|
|
|
|
|
|
my %mv = ();
|
501
|
|
|
|
|
|
|
my $n = scalar @{$value};
|
502
|
|
|
|
|
|
|
$ast->error($type . " should have even number of items") if ($n%2) != 0;
|
503
|
|
|
|
|
|
|
for(my $i=0; $i<$n; $i+=2) {
|
504
|
|
|
|
|
|
|
my $k = $self->_eval($value->[$i]);
|
505
|
|
|
|
|
|
|
$ast->error($type . " expects keyword or string as key but got " . $k->type())
|
506
|
|
|
|
|
|
|
if ($k->type() ne "keyword"
|
507
|
|
|
|
|
|
|
and $k->type() ne "string");
|
508
|
|
|
|
|
|
|
my $v = $self->_eval($value->[$i+1]);
|
509
|
|
|
|
|
|
|
$mv{$k->value()} = $v;
|
510
|
|
|
|
|
|
|
};
|
511
|
|
|
|
|
|
|
$m->value(\%mv);
|
512
|
|
|
|
|
|
|
$m->type("meta") if $type eq "meta";
|
513
|
|
|
|
|
|
|
return $m;
|
514
|
|
|
|
|
|
|
} elsif($class eq "Seq" and $type eq "xml") {
|
515
|
|
|
|
|
|
|
my $size = $ast->size();
|
516
|
|
|
|
|
|
|
$ast->error("xml expects >= 1 arguments") if $size == 0;
|
517
|
|
|
|
|
|
|
my $first = $ast->first();
|
518
|
|
|
|
|
|
|
my $firsttype = $first->type();
|
519
|
|
|
|
|
|
|
if($firsttype ne "symbol") {
|
520
|
|
|
|
|
|
|
$first = $self->_eval($first);
|
521
|
|
|
|
|
|
|
$firsttype = $first->type();
|
522
|
|
|
|
|
|
|
};
|
523
|
|
|
|
|
|
|
$ast->error("xml expects a symbol or string or keyword as name but got " . $firsttype)
|
524
|
|
|
|
|
|
|
if $firsttype ne "symbol"
|
525
|
|
|
|
|
|
|
and $firsttype ne "string"
|
526
|
|
|
|
|
|
|
and $firsttype ne "keyword";
|
527
|
|
|
|
|
|
|
my @items = ();
|
528
|
|
|
|
|
|
|
my $xml = CljPerl::Atom->new("xml", \@items);
|
529
|
|
|
|
|
|
|
$xml->{name} = $first->value();
|
530
|
|
|
|
|
|
|
my @rest = $ast->slice(1 .. $size-1);
|
531
|
|
|
|
|
|
|
foreach my $i (@rest) {
|
532
|
|
|
|
|
|
|
my $iv = $self->_eval($i);
|
533
|
|
|
|
|
|
|
my $it = $iv->type();
|
534
|
|
|
|
|
|
|
$ast->error("xml expects string or xml or meta or list as items but got " . $it)
|
535
|
|
|
|
|
|
|
if $it ne "string"
|
536
|
|
|
|
|
|
|
and $it ne "xml"
|
537
|
|
|
|
|
|
|
and $it ne "meta"
|
538
|
|
|
|
|
|
|
and $it ne "list";
|
539
|
|
|
|
|
|
|
if($it eq "meta") {
|
540
|
|
|
|
|
|
|
$xml->meta($iv);
|
541
|
|
|
|
|
|
|
} elsif($it eq "list") {
|
542
|
|
|
|
|
|
|
foreach my $i (@{$iv->value()}) {
|
543
|
|
|
|
|
|
|
push @items, $i;
|
544
|
|
|
|
|
|
|
};
|
545
|
|
|
|
|
|
|
} else {;
|
546
|
|
|
|
|
|
|
push @items, $iv;
|
547
|
|
|
|
|
|
|
};
|
548
|
|
|
|
|
|
|
};
|
549
|
|
|
|
|
|
|
return $xml;
|
550
|
|
|
|
|
|
|
};
|
551
|
|
|
|
|
|
|
return $ast;
|
552
|
|
|
|
|
|
|
}
|
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub builtin {
|
555
|
|
|
|
|
|
|
my $self = shift;
|
556
|
|
|
|
|
|
|
my $f = shift;
|
557
|
|
|
|
|
|
|
my $ast = shift;
|
558
|
|
|
|
|
|
|
my $size = $ast->size();
|
559
|
|
|
|
|
|
|
#my $f = $ast->first();
|
560
|
|
|
|
|
|
|
my $fn = $f->value();
|
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
# (eval "bla bla bla")
|
563
|
|
|
|
|
|
|
if($fn eq "eval") {
|
564
|
|
|
|
|
|
|
$ast->error("eval expects 1 argument") if $size != 2;
|
565
|
|
|
|
|
|
|
my $s = $ast->second();
|
566
|
|
|
|
|
|
|
$ast->error("eval expects 1 string as argument but got " . $s->type()) if $s->type() ne "string";
|
567
|
|
|
|
|
|
|
return $self->eval($s->value());
|
568
|
|
|
|
|
|
|
} elsif($fn eq "syntax") {
|
569
|
|
|
|
|
|
|
$ast->error("syntax expects 1 argument") if $size != 2;
|
570
|
|
|
|
|
|
|
return $self->bind($ast->second());
|
571
|
|
|
|
|
|
|
} elsif($fn eq "throw") {
|
572
|
|
|
|
|
|
|
$ast->error("throw expects 2 arguments") if $size != 3;
|
573
|
|
|
|
|
|
|
my $label = $ast->second();
|
574
|
|
|
|
|
|
|
$ast->error("throw expects a symbol as the first argument but got " . $label->type()) if $label->type() ne "symbol";
|
575
|
|
|
|
|
|
|
my $msg = $self->_eval($ast->third());
|
576
|
|
|
|
|
|
|
$ast->error("throw expects a string as the second argument but got " . $msg->type()) if $msg->type() ne "string";
|
577
|
|
|
|
|
|
|
my $e = CljPerl::Atom->new("exception", $msg->value());
|
578
|
|
|
|
|
|
|
$e->{label} = $label->value();
|
579
|
|
|
|
|
|
|
my @caller = @{$self->{caller}};
|
580
|
|
|
|
|
|
|
$e->{caller} = \@caller;
|
581
|
|
|
|
|
|
|
$self->{exception} = $e;
|
582
|
|
|
|
|
|
|
die $msg->value();
|
583
|
|
|
|
|
|
|
} elsif($fn eq "exception-label") {
|
584
|
|
|
|
|
|
|
$ast->error("exception-label expects 1 argument") if $size != 2;
|
585
|
|
|
|
|
|
|
my $e = $self->_eval($ast->second());
|
586
|
|
|
|
|
|
|
$ast->error("exception-label expects an exception as argument but got " . $e->type()) if $e->type() ne "exception";
|
587
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $e->{label});
|
588
|
|
|
|
|
|
|
} elsif($fn eq "exception-message") {
|
589
|
|
|
|
|
|
|
$ast->error("exception-message expects 1 argument") if $size != 2;
|
590
|
|
|
|
|
|
|
my $e = $self->_eval($ast->second());
|
591
|
|
|
|
|
|
|
$ast->error("exception-message expects an exception as argument but got " . $e->type()) if $e->type() ne "exception";
|
592
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $e->value());
|
593
|
|
|
|
|
|
|
} elsif($fn eq "catch") {
|
594
|
|
|
|
|
|
|
$ast->error("catch expects 2 arguments") if $size != 3;
|
595
|
|
|
|
|
|
|
my $handler = $self->_eval($ast->third());
|
596
|
|
|
|
|
|
|
$ast->error("catch expects a function/lambda as the second argument but got " . $handler->type()) if $handler->type() ne "function";
|
597
|
|
|
|
|
|
|
my $res;
|
598
|
|
|
|
|
|
|
my $saved_caller_depth = $self->caller_size();
|
599
|
|
|
|
|
|
|
eval {
|
600
|
|
|
|
|
|
|
$res = $self->_eval($ast->second());
|
601
|
|
|
|
|
|
|
};
|
602
|
|
|
|
|
|
|
if($@){
|
603
|
|
|
|
|
|
|
my $e = $self->{exception};
|
604
|
|
|
|
|
|
|
if(!defined $e) {
|
605
|
|
|
|
|
|
|
$e = CljPerl::Atom->new("exception", "unkown expection");
|
606
|
|
|
|
|
|
|
$e->{label} = "undef";
|
607
|
|
|
|
|
|
|
my @ec = ();
|
608
|
|
|
|
|
|
|
$e->{caller} = \@ec;
|
609
|
|
|
|
|
|
|
};
|
610
|
|
|
|
|
|
|
$ast->error("catch expects an exception for handler but got " . $e->type()) if $e->type() ne "exception";
|
611
|
|
|
|
|
|
|
my $i = $self->caller_size();
|
612
|
|
|
|
|
|
|
for(;$i > $saved_caller_depth; $i--){
|
613
|
|
|
|
|
|
|
$self->pop_caller();
|
614
|
|
|
|
|
|
|
};
|
615
|
|
|
|
|
|
|
my $call_handler = CljPerl::Seq->new("list");
|
616
|
|
|
|
|
|
|
$call_handler->append($handler);
|
617
|
|
|
|
|
|
|
$call_handler->append($e);
|
618
|
|
|
|
|
|
|
$self->{exception} = undef;
|
619
|
|
|
|
|
|
|
return $self->_eval($call_handler);
|
620
|
|
|
|
|
|
|
};
|
621
|
|
|
|
|
|
|
return $res;
|
622
|
|
|
|
|
|
|
# (def ^{} name value)
|
623
|
|
|
|
|
|
|
} elsif($fn eq "def") {
|
624
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size > 4 or $size < 3;
|
625
|
|
|
|
|
|
|
if($size == 3){
|
626
|
|
|
|
|
|
|
$ast->error($fn . " expects a symbol as the first argument but got " . $ast->second()->type()) if $ast->second()->type() ne "symbol";
|
627
|
|
|
|
|
|
|
my $name = $ast->second()->value();
|
628
|
|
|
|
|
|
|
$ast->error($name . " is a reserved word") if exists $builtin_funcs->{$name} or $name =~ /^(\.|->)\S+$/;
|
629
|
|
|
|
|
|
|
$self->new_var($name);
|
630
|
|
|
|
|
|
|
my $value = $self->_eval($ast->third());
|
631
|
|
|
|
|
|
|
$self->var($name)->value($value);
|
632
|
|
|
|
|
|
|
return $value;
|
633
|
|
|
|
|
|
|
} else {
|
634
|
|
|
|
|
|
|
my $meta = $self->_eval($ast->second());
|
635
|
|
|
|
|
|
|
$ast->error($fn . " expects a meta as the first argument but got " . $meta->type()) if $meta->type() ne "meta";
|
636
|
|
|
|
|
|
|
$ast->error($fn . " expects a symbol as the first argument but got " . $ast->third()->type()) if $ast->third()->type() ne "symbol";
|
637
|
|
|
|
|
|
|
my $name = $ast->third()->value();
|
638
|
|
|
|
|
|
|
$ast->error($name . " is a reserved word") if exists $builtin_funcs->{$name} or $name =~ /^(\.|->)\S+$/;
|
639
|
|
|
|
|
|
|
$self->new_var($name);
|
640
|
|
|
|
|
|
|
my $value = $self->_eval($ast->fourth());
|
641
|
|
|
|
|
|
|
$value->meta($meta);
|
642
|
|
|
|
|
|
|
$self->var($name)->value($value);
|
643
|
|
|
|
|
|
|
return $value;
|
644
|
|
|
|
|
|
|
}
|
645
|
|
|
|
|
|
|
# (set! name value)
|
646
|
|
|
|
|
|
|
} elsif($fn eq "set!") {
|
647
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
648
|
|
|
|
|
|
|
$ast->error($fn . " expects a symbol as the first argument but got " . $ast->second()->type()) if $ast->second()->type() ne "symbol";
|
649
|
|
|
|
|
|
|
my $name = $ast->second()->value();
|
650
|
|
|
|
|
|
|
$ast->error("undefine variable " . $name) if !defined $self->var($name);
|
651
|
|
|
|
|
|
|
my $value = $self->_eval($ast->third());
|
652
|
|
|
|
|
|
|
$self->var($name)->value($value);
|
653
|
|
|
|
|
|
|
return $value;
|
654
|
|
|
|
|
|
|
} elsif($fn eq "let") {
|
655
|
|
|
|
|
|
|
$ast->error($fn . " expects >=3 arguments") if $size < 3;
|
656
|
|
|
|
|
|
|
my $vars = $ast->second();
|
657
|
|
|
|
|
|
|
$ast->error($fn . " expects a list [name value ...] as the first argument") if $vars->type() ne "vector";
|
658
|
|
|
|
|
|
|
my $varssize = $vars->size();
|
659
|
|
|
|
|
|
|
$ast->error($fn . " expects [name value ...] pairs as the first argument") if $varssize%2 != 0;
|
660
|
|
|
|
|
|
|
my $varvs = $vars->value();
|
661
|
|
|
|
|
|
|
$self->push_scope($self->current_scope());
|
662
|
|
|
|
|
|
|
$self->push_caller($ast);
|
663
|
|
|
|
|
|
|
for(my $i=0; $i < $varssize; $i+=2) {
|
664
|
|
|
|
|
|
|
my $n = $varvs->[$i];
|
665
|
|
|
|
|
|
|
my $v = $varvs->[$i+1];
|
666
|
|
|
|
|
|
|
$ast->error($fn . " expects a symbol as name but got " . $n->type()) if $n->type() ne "symbol";
|
667
|
|
|
|
|
|
|
$self->new_var($n->value(), $self->_eval($v));
|
668
|
|
|
|
|
|
|
};
|
669
|
|
|
|
|
|
|
my @body = $ast->slice(2 .. $size-1);
|
670
|
|
|
|
|
|
|
my $res = $nil;
|
671
|
|
|
|
|
|
|
foreach my $b (@body){
|
672
|
|
|
|
|
|
|
$res = $self->_eval($b);
|
673
|
|
|
|
|
|
|
};
|
674
|
|
|
|
|
|
|
$self->pop_scope();
|
675
|
|
|
|
|
|
|
$self->pop_caller();
|
676
|
|
|
|
|
|
|
return $res;
|
677
|
|
|
|
|
|
|
# (fn [args ...] body)
|
678
|
|
|
|
|
|
|
} elsif($fn eq "fn") {
|
679
|
|
|
|
|
|
|
$ast->error("fn expects >= 3 arguments") if $size < 3;
|
680
|
|
|
|
|
|
|
my $args = $ast->second();
|
681
|
|
|
|
|
|
|
my $argstype = $args->type();
|
682
|
|
|
|
|
|
|
$ast->error("fn expects [arg ...] as formal argument list") if $argstype ne "vector";
|
683
|
|
|
|
|
|
|
my $argsvalue = $args->value();
|
684
|
|
|
|
|
|
|
my $argssize = $args->size();
|
685
|
|
|
|
|
|
|
my $i = 0;
|
686
|
|
|
|
|
|
|
foreach my $arg (@{$argsvalue}) {
|
687
|
|
|
|
|
|
|
$arg->error("formal argument should be a symbol but got " . $arg->type()) if $arg->type() ne "symbol";
|
688
|
|
|
|
|
|
|
if($arg->value() eq "&"
|
689
|
|
|
|
|
|
|
and ($argssize != $i + 2 or $argsvalue->[$i+1]->value() eq "&")) {
|
690
|
|
|
|
|
|
|
$arg->error("only 1 non-& should follow &");
|
691
|
|
|
|
|
|
|
};
|
692
|
|
|
|
|
|
|
$i ++;
|
693
|
|
|
|
|
|
|
}
|
694
|
|
|
|
|
|
|
my $nast = CljPerl::Atom->new("function", $ast);
|
695
|
|
|
|
|
|
|
my %c = %{$self->current_scope()};
|
696
|
|
|
|
|
|
|
my @ns = @{$c{$namespace_key}};
|
697
|
|
|
|
|
|
|
$c{$namespace_key} = \@ns;
|
698
|
|
|
|
|
|
|
$nast->{context} = \%c;
|
699
|
|
|
|
|
|
|
return $nast;
|
700
|
|
|
|
|
|
|
# (defmacro name [args ...] body)
|
701
|
|
|
|
|
|
|
} elsif($fn eq "defmacro") {
|
702
|
|
|
|
|
|
|
$ast->error("defmacro expects >= 4 arguments") if $size < 4;
|
703
|
|
|
|
|
|
|
my $name = $ast->second()->value();
|
704
|
|
|
|
|
|
|
my $args = $ast->third();
|
705
|
|
|
|
|
|
|
$ast->error("defmacro expect [arg ...] as formal argument list") if $args->type() ne "vector";
|
706
|
|
|
|
|
|
|
my $i = 0;
|
707
|
|
|
|
|
|
|
foreach my $arg (@{$args->value()}) {
|
708
|
|
|
|
|
|
|
$arg->error("formal argument should be a symbol but got " . $arg->type()) if $arg->type() ne "symbol";
|
709
|
|
|
|
|
|
|
if($arg->value() eq "&"
|
710
|
|
|
|
|
|
|
and ($args->size() != $i + 2 or $args->value()->[$i+1]->value() eq "&")) {
|
711
|
|
|
|
|
|
|
$arg->error("only 1 non-& should follow &");
|
712
|
|
|
|
|
|
|
};
|
713
|
|
|
|
|
|
|
$i ++;
|
714
|
|
|
|
|
|
|
}
|
715
|
|
|
|
|
|
|
my $nast = CljPerl::Atom->new("macro", $ast);
|
716
|
|
|
|
|
|
|
my %c = %{$self->current_scope()};
|
717
|
|
|
|
|
|
|
my @ns = @{$c{$namespace_key}};
|
718
|
|
|
|
|
|
|
$c{$namespace_key} = \@ns;
|
719
|
|
|
|
|
|
|
$nast->{context} = \%c;
|
720
|
|
|
|
|
|
|
$self->new_var($name, $nast);
|
721
|
|
|
|
|
|
|
return $nast;
|
722
|
|
|
|
|
|
|
# (gen-sym)
|
723
|
|
|
|
|
|
|
} elsif($fn eq "gen-sym") {
|
724
|
|
|
|
|
|
|
$ast->error("gen-sym expects 0/1 argument") if $size > 2;
|
725
|
|
|
|
|
|
|
my $s = CljPerl::Atom->new("symbol");
|
726
|
|
|
|
|
|
|
if($size == 2) {
|
727
|
|
|
|
|
|
|
my $pre = $self->_eval($ast->second());
|
728
|
|
|
|
|
|
|
$ast->("gen-sym expects string as argument") if $pre->type ne "string";
|
729
|
|
|
|
|
|
|
$s->value($pre->value() . $s->object_id());
|
730
|
|
|
|
|
|
|
} else {
|
731
|
|
|
|
|
|
|
$s->value($s->object_id());
|
732
|
|
|
|
|
|
|
};
|
733
|
|
|
|
|
|
|
return $s;
|
734
|
|
|
|
|
|
|
# (require "filename")
|
735
|
|
|
|
|
|
|
} elsif($fn eq "require") {
|
736
|
|
|
|
|
|
|
$ast->error("require expects 1 argument") if $size != 2;
|
737
|
|
|
|
|
|
|
my $m = $ast->second();
|
738
|
|
|
|
|
|
|
if($m->type() eq "symbol" or $m->type() eq "keyword") {
|
739
|
|
|
|
|
|
|
} else {
|
740
|
|
|
|
|
|
|
$m = $self->_eval($m);
|
741
|
|
|
|
|
|
|
$ast->error("require expects a string but got " . $m->type())
|
742
|
|
|
|
|
|
|
if $m->type() ne "string";
|
743
|
|
|
|
|
|
|
};
|
744
|
|
|
|
|
|
|
return $self->load($m->value());
|
745
|
|
|
|
|
|
|
} elsif($fn eq "read") {
|
746
|
|
|
|
|
|
|
$ast->error("read expects 1 argument") if $size != 2;
|
747
|
|
|
|
|
|
|
my $f = $self->_eval($ast->second());
|
748
|
|
|
|
|
|
|
$ast->error("read expects a string but got " . $f->type())
|
749
|
|
|
|
|
|
|
if $f->type() ne "string";
|
750
|
|
|
|
|
|
|
return $self->read($f->value());
|
751
|
|
|
|
|
|
|
# (list 'a 'b 'c)
|
752
|
|
|
|
|
|
|
} elsif($fn eq "list") {
|
753
|
|
|
|
|
|
|
return $emtpy_list if $size == 1;
|
754
|
|
|
|
|
|
|
my @vs = $ast->slice(1 .. $size-1);
|
755
|
|
|
|
|
|
|
my $r = CljPerl::Seq->new("list");
|
756
|
|
|
|
|
|
|
foreach my $i (@vs) {
|
757
|
|
|
|
|
|
|
$r->append($self->_eval($i));
|
758
|
|
|
|
|
|
|
};
|
759
|
|
|
|
|
|
|
return $r;
|
760
|
|
|
|
|
|
|
# (car list)
|
761
|
|
|
|
|
|
|
} elsif($fn eq "car") {
|
762
|
|
|
|
|
|
|
$ast->error("car expects 1 argument") if $size != 2;
|
763
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
764
|
|
|
|
|
|
|
$ast->error("car expects 1 list as argument but got " . $v->type()) if $v->type() ne "list";
|
765
|
|
|
|
|
|
|
my $fv = $v->first();
|
766
|
|
|
|
|
|
|
return $fv;
|
767
|
|
|
|
|
|
|
# (cdr list)
|
768
|
|
|
|
|
|
|
} elsif($fn eq "cdr") {
|
769
|
|
|
|
|
|
|
$ast->error("cdr expects 1 argument") if $size != 2;
|
770
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
771
|
|
|
|
|
|
|
$ast->error("cdr expects 1 list as argument but got " . $v->type()) if $v->type() ne "list";
|
772
|
|
|
|
|
|
|
return $empty_list if($v->size()==0);
|
773
|
|
|
|
|
|
|
my @vs = $v->slice(1 .. $v->size()-1);
|
774
|
|
|
|
|
|
|
my $r = CljPerl::Seq->new("list");
|
775
|
|
|
|
|
|
|
$r->value(\@vs);
|
776
|
|
|
|
|
|
|
return $r;
|
777
|
|
|
|
|
|
|
# (cons item list)
|
778
|
|
|
|
|
|
|
} elsif($fn eq "cons") {
|
779
|
|
|
|
|
|
|
$ast->error("cons expects 2 arguments") if $size != 3;
|
780
|
|
|
|
|
|
|
my $fv = $self->_eval($ast->second());
|
781
|
|
|
|
|
|
|
my $rvs = $self->_eval($ast->third());
|
782
|
|
|
|
|
|
|
$ast->error("cons expects 1 list as the second argument but got " . $rvs->type()) if $rvs->type() ne "list";
|
783
|
|
|
|
|
|
|
my @vs = ();
|
784
|
|
|
|
|
|
|
@vs = $rvs->slice(0 .. $rvs->size()-1) if $rvs->size() > 0;
|
785
|
|
|
|
|
|
|
unshift @vs, $fv;
|
786
|
|
|
|
|
|
|
my $r = CljPerl::Seq->new("list");
|
787
|
|
|
|
|
|
|
$r->value(\@vs);
|
788
|
|
|
|
|
|
|
return $r;
|
789
|
|
|
|
|
|
|
# (if cond true_clause false_clause)
|
790
|
|
|
|
|
|
|
} elsif($fn eq "if") {
|
791
|
|
|
|
|
|
|
$ast->error("if expects 2 or 3 arguments") if $size > 4 or $size < 3;
|
792
|
|
|
|
|
|
|
my $cond = $self->_eval($ast->second());
|
793
|
|
|
|
|
|
|
$ast->error("if expects a bool as the first argument but got " . $cond->type()) if $cond->type() ne "bool";
|
794
|
|
|
|
|
|
|
if($cond->value() eq "true") {
|
795
|
|
|
|
|
|
|
return $self->_eval($ast->third());
|
796
|
|
|
|
|
|
|
} elsif($ast->size() == 4) {
|
797
|
|
|
|
|
|
|
return $self->_eval($ast->fourth());
|
798
|
|
|
|
|
|
|
} else {
|
799
|
|
|
|
|
|
|
return $nil;
|
800
|
|
|
|
|
|
|
};
|
801
|
|
|
|
|
|
|
# (while cond body)
|
802
|
|
|
|
|
|
|
} elsif($fn eq "while") {
|
803
|
|
|
|
|
|
|
$ast->error("while expects >= 2 arguments") if $size < 3;
|
804
|
|
|
|
|
|
|
my $cond = $self->_eval($ast->second());
|
805
|
|
|
|
|
|
|
$ast->error("while expects a bool as the first argument but got " . $cond->type()) if $cond->type() ne "bool";
|
806
|
|
|
|
|
|
|
my $res = $nil;
|
807
|
|
|
|
|
|
|
my @body = $ast->slice(2 .. $size-1);
|
808
|
|
|
|
|
|
|
while ($cond->value() eq "true") {
|
809
|
|
|
|
|
|
|
foreach my $i (@body) {
|
810
|
|
|
|
|
|
|
$res = $self->_eval($i);
|
811
|
|
|
|
|
|
|
}
|
812
|
|
|
|
|
|
|
$cond = $self->_eval($ast->second());
|
813
|
|
|
|
|
|
|
}
|
814
|
|
|
|
|
|
|
return $res;
|
815
|
|
|
|
|
|
|
# (begin body)
|
816
|
|
|
|
|
|
|
} elsif($fn eq "begin") {
|
817
|
|
|
|
|
|
|
$ast->error("being expects >= 1 arguments") if $size < 2;
|
818
|
|
|
|
|
|
|
my $res = $nil;
|
819
|
|
|
|
|
|
|
my @body = $ast->slice(1 .. $size-1);
|
820
|
|
|
|
|
|
|
foreach my $i (@body) {
|
821
|
|
|
|
|
|
|
$res = $self->_eval($i);
|
822
|
|
|
|
|
|
|
}
|
823
|
|
|
|
|
|
|
return $res;
|
824
|
|
|
|
|
|
|
# + - & / % operations
|
825
|
|
|
|
|
|
|
} elsif($fn =~ /^(\+|\-|\*|\/|\%)$/) {
|
826
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
827
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
828
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
829
|
|
|
|
|
|
|
$ast->error($fn . " expects number as arguments but got " . $v1->type() . " and " . $v2->type())
|
830
|
|
|
|
|
|
|
if $v1->type() ne "number" or $v2->type() ne "number";
|
831
|
|
|
|
|
|
|
my $vv1 = $v1->value();
|
832
|
|
|
|
|
|
|
my $vv2 = $v2->value();
|
833
|
|
|
|
|
|
|
my $r = CljPerl::Atom->new("number", eval("$vv1 $fn $vv2"));
|
834
|
|
|
|
|
|
|
return $r;
|
835
|
|
|
|
|
|
|
# == > < >= <= != logic operations
|
836
|
|
|
|
|
|
|
} elsif($fn =~ /^(==|>|<|>=|<=|!=)$/) {
|
837
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
838
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
839
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
840
|
|
|
|
|
|
|
$ast->error($fn . " expects number as arguments but got " . $v1->type() . " and " . $v2->type())
|
841
|
|
|
|
|
|
|
if $v1->type() ne "number" or $v2->type() ne "number";
|
842
|
|
|
|
|
|
|
my $vv1 = $v1->value();
|
843
|
|
|
|
|
|
|
my $vv2 = $v2->value();
|
844
|
|
|
|
|
|
|
my $r = eval("$vv1 $fn $vv2");
|
845
|
|
|
|
|
|
|
if($r){
|
846
|
|
|
|
|
|
|
return $true;
|
847
|
|
|
|
|
|
|
} else {
|
848
|
|
|
|
|
|
|
return $false;
|
849
|
|
|
|
|
|
|
}
|
850
|
|
|
|
|
|
|
} elsif($fn eq "xml-name") {
|
851
|
|
|
|
|
|
|
$ast->error($fn . " expects 1 argument") if $size != 2;
|
852
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
853
|
|
|
|
|
|
|
$ast->error($fn . " expects xml as argument but got " . $v->type()) if $v->type() ne "xml";
|
854
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $v->{name});
|
855
|
|
|
|
|
|
|
# eq ne for string comparing
|
856
|
|
|
|
|
|
|
} elsif($fn =~ /^(eq|ne)$/) {
|
857
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
858
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
859
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
860
|
|
|
|
|
|
|
$ast->error($fn . " expects string as arguments but got " . $v1->type() . " and " . $v2->type())
|
861
|
|
|
|
|
|
|
if $v1->type() ne "string" or $v2->type() ne "string";
|
862
|
|
|
|
|
|
|
my $vv1 = $v1->value();
|
863
|
|
|
|
|
|
|
my $vv2 = $v2->value();
|
864
|
|
|
|
|
|
|
my $r = eval("'$vv1' $fn '$vv2'");
|
865
|
|
|
|
|
|
|
if($r){
|
866
|
|
|
|
|
|
|
return $true;
|
867
|
|
|
|
|
|
|
} else {
|
868
|
|
|
|
|
|
|
return $false;
|
869
|
|
|
|
|
|
|
}
|
870
|
|
|
|
|
|
|
# (equal a b)
|
871
|
|
|
|
|
|
|
} elsif($fn eq "equal") {
|
872
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
873
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
874
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
875
|
|
|
|
|
|
|
my $r = 0;
|
876
|
|
|
|
|
|
|
if($v1->type() ne $v2->type()) {
|
877
|
|
|
|
|
|
|
$r = 0;
|
878
|
|
|
|
|
|
|
} elsif($v1->type() eq "string"
|
879
|
|
|
|
|
|
|
or $v1->type() eq "keyword"
|
880
|
|
|
|
|
|
|
or $v1->type() eq "quotation"
|
881
|
|
|
|
|
|
|
or $v1->type() eq "bool"
|
882
|
|
|
|
|
|
|
or $v1->type() eq "nil"){
|
883
|
|
|
|
|
|
|
$r = $v1->value() eq $v2->value();
|
884
|
|
|
|
|
|
|
} elsif($v1->type() eq "number"){
|
885
|
|
|
|
|
|
|
$r = $v1->value() == $v2->value();
|
886
|
|
|
|
|
|
|
} else {
|
887
|
|
|
|
|
|
|
$r = $v1->value() eq $v2->value();
|
888
|
|
|
|
|
|
|
};
|
889
|
|
|
|
|
|
|
if($r){
|
890
|
|
|
|
|
|
|
return $true;
|
891
|
|
|
|
|
|
|
} else {
|
892
|
|
|
|
|
|
|
return $false;
|
893
|
|
|
|
|
|
|
};
|
894
|
|
|
|
|
|
|
# (! true_or_false)
|
895
|
|
|
|
|
|
|
} elsif($fn eq "!" or $fn eq "not") {
|
896
|
|
|
|
|
|
|
$ast->error("!/not expects 1 argument") if $size != 2;
|
897
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
898
|
|
|
|
|
|
|
$ast->error("!/not expects a bool as the first argument but got " . $v->type()) if $v->type() ne "bool";
|
899
|
|
|
|
|
|
|
if($v->value() eq "true") {
|
900
|
|
|
|
|
|
|
return $false;
|
901
|
|
|
|
|
|
|
} else {
|
902
|
|
|
|
|
|
|
return $true;
|
903
|
|
|
|
|
|
|
};
|
904
|
|
|
|
|
|
|
# (and/or true_or_false true_or_false)
|
905
|
|
|
|
|
|
|
} elsif($fn eq "and") {
|
906
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
907
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
908
|
|
|
|
|
|
|
$ast->error($fn . " expects bool as arguments but got " . $v1->type())
|
909
|
|
|
|
|
|
|
if $v1->type() ne "bool";
|
910
|
|
|
|
|
|
|
return $false if $v1->value() eq "false";
|
911
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
912
|
|
|
|
|
|
|
$ast->error($fn . " expects bool as arguments but got " . $v2->type())
|
913
|
|
|
|
|
|
|
if $v2->type() ne "bool";
|
914
|
|
|
|
|
|
|
if($v2->value() eq "true") {
|
915
|
|
|
|
|
|
|
return $true;
|
916
|
|
|
|
|
|
|
} else {
|
917
|
|
|
|
|
|
|
return $false;
|
918
|
|
|
|
|
|
|
};
|
919
|
|
|
|
|
|
|
} elsif($fn eq "or") {
|
920
|
|
|
|
|
|
|
$ast->error($fn . " expects 2 arguments") if $size != 3;
|
921
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
922
|
|
|
|
|
|
|
$ast->error($fn . " expects bool as arguments but got " . $v1->type())
|
923
|
|
|
|
|
|
|
if $v1->type() ne "bool";
|
924
|
|
|
|
|
|
|
return $true if $v1->value() eq "true";
|
925
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
926
|
|
|
|
|
|
|
$ast->error($fn . " expects bool as arguments but got " . $v2->type())
|
927
|
|
|
|
|
|
|
if $v2->type() ne "bool";
|
928
|
|
|
|
|
|
|
if($v2->value() eq "true") {
|
929
|
|
|
|
|
|
|
return $true;
|
930
|
|
|
|
|
|
|
} else {
|
931
|
|
|
|
|
|
|
return $false;
|
932
|
|
|
|
|
|
|
};
|
933
|
|
|
|
|
|
|
# (length list_or_vector_or_xml_or_map_or_string)
|
934
|
|
|
|
|
|
|
} elsif($fn eq "length") {
|
935
|
|
|
|
|
|
|
$ast->error("length expects 1 argument") if $size != 2;
|
936
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
937
|
|
|
|
|
|
|
my $r = CljPerl::Atom->new("number", 0);
|
938
|
|
|
|
|
|
|
if($v->type() eq "string"){
|
939
|
|
|
|
|
|
|
$r->value(length($v->value()));
|
940
|
|
|
|
|
|
|
} elsif($v->type() eq "list" or $v->type() eq "vector" or $v->type() eq "xml"){
|
941
|
|
|
|
|
|
|
$r->value(scalar @{$v->value()});
|
942
|
|
|
|
|
|
|
} elsif($v->type() eq "map") {
|
943
|
|
|
|
|
|
|
$r->value(scalar %{$v->value()});
|
944
|
|
|
|
|
|
|
} else {
|
945
|
|
|
|
|
|
|
$ast->error("unexpected type " . $v->type() . " of argument for length");
|
946
|
|
|
|
|
|
|
};
|
947
|
|
|
|
|
|
|
return $r;
|
948
|
|
|
|
|
|
|
# (reverse list_or_vector_or_xml_or_string)
|
949
|
|
|
|
|
|
|
} elsif($fn eq "reverse") {
|
950
|
|
|
|
|
|
|
$ast->error("length expects 1 argument") if $size != 2;
|
951
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
952
|
|
|
|
|
|
|
my $r;
|
953
|
|
|
|
|
|
|
if($v->type() eq "string"){
|
954
|
|
|
|
|
|
|
$r = CljPerl::Atom->new("string", 0);
|
955
|
|
|
|
|
|
|
$r->value(reverse($v->value()));
|
956
|
|
|
|
|
|
|
} elsif($v->type() eq "list") {
|
957
|
|
|
|
|
|
|
$r = CljPerl::Seq->new("list");
|
958
|
|
|
|
|
|
|
my @vv = reverse @{$v->value()};
|
959
|
|
|
|
|
|
|
$r->value(\@vv);
|
960
|
|
|
|
|
|
|
} elsif($v->type() eq "vector" or $v->type() eq "xml"){
|
961
|
|
|
|
|
|
|
$r = CljPerl::Atom->new($v->type());
|
962
|
|
|
|
|
|
|
my @vv = reverse @{$v->value()};
|
963
|
|
|
|
|
|
|
$r->value(\@vv);
|
964
|
|
|
|
|
|
|
} else {
|
965
|
|
|
|
|
|
|
$ast->error("unexpected type " . $v->type() . " of argument for reverse");
|
966
|
|
|
|
|
|
|
};
|
967
|
|
|
|
|
|
|
return $r;
|
968
|
|
|
|
|
|
|
# (append list1 list2)
|
969
|
|
|
|
|
|
|
} elsif($fn eq "append") {
|
970
|
|
|
|
|
|
|
$ast->error("append expects 2 arguments") if $size != 3;
|
971
|
|
|
|
|
|
|
my $v1 = $self->_eval($ast->second());
|
972
|
|
|
|
|
|
|
my $v2 = $self->_eval($ast->third());
|
973
|
|
|
|
|
|
|
my $v1type = $v1->type();
|
974
|
|
|
|
|
|
|
my $v2type = $v2->type();
|
975
|
|
|
|
|
|
|
$ast->error("append expects string or list or vector as arguments but got " . $v1type . " and " . $v2type)
|
976
|
|
|
|
|
|
|
if (($v1type ne $v2type)
|
977
|
|
|
|
|
|
|
or ($v1type ne "string"
|
978
|
|
|
|
|
|
|
and $v1type ne "list"
|
979
|
|
|
|
|
|
|
and $v1type ne "vector"
|
980
|
|
|
|
|
|
|
and $v1type ne "map"));
|
981
|
|
|
|
|
|
|
if($v1type eq "string") {
|
982
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $v1->value() . $v2->value());
|
983
|
|
|
|
|
|
|
} elsif($v1type eq "list" or $v1type eq "vector") {
|
984
|
|
|
|
|
|
|
my @r = ();
|
985
|
|
|
|
|
|
|
push @r, @{$v1->value()};
|
986
|
|
|
|
|
|
|
push @r, @{$v2->value()};
|
987
|
|
|
|
|
|
|
if($v1type eq "list"){
|
988
|
|
|
|
|
|
|
return CljPerl::Seq->new("list", \@r);
|
989
|
|
|
|
|
|
|
} else {
|
990
|
|
|
|
|
|
|
return CljPerl::Atom->new("vector", \@r);
|
991
|
|
|
|
|
|
|
};
|
992
|
|
|
|
|
|
|
} else {
|
993
|
|
|
|
|
|
|
my %r = (%{$v1->value()}, %{$v2->value()});
|
994
|
|
|
|
|
|
|
return CljPerl::Atom->new("map", \%r);
|
995
|
|
|
|
|
|
|
};
|
996
|
|
|
|
|
|
|
# (keys map)
|
997
|
|
|
|
|
|
|
} elsif($fn eq "keys") {
|
998
|
|
|
|
|
|
|
$ast->error("keys expects 1 argument") if $size != 2;
|
999
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
1000
|
|
|
|
|
|
|
$ast->error("keys expects map as arguments but got " . $v->type()) if $v->type() ne "map";
|
1001
|
|
|
|
|
|
|
my @r = ();
|
1002
|
|
|
|
|
|
|
foreach my $k (keys %{$v->value()}) {
|
1003
|
|
|
|
|
|
|
push @r, CljPerl::Atom->new("keyword", $k);
|
1004
|
|
|
|
|
|
|
};
|
1005
|
|
|
|
|
|
|
return CljPerl::Seq->new("list", \@r);
|
1006
|
|
|
|
|
|
|
# (namespace-begin "ns")
|
1007
|
|
|
|
|
|
|
} elsif($fn eq "namespace-begin") {
|
1008
|
|
|
|
|
|
|
$ast->error("namespace-begin expects 1 argument") if $size != 2;
|
1009
|
|
|
|
|
|
|
my $v = $ast->second();
|
1010
|
|
|
|
|
|
|
if($v->type() eq "symbol" or $v->type() eq "keyword") {
|
1011
|
|
|
|
|
|
|
} else {
|
1012
|
|
|
|
|
|
|
$v = $self->_eval($v);
|
1013
|
|
|
|
|
|
|
$ast->error("namespace-begin expects string as argument but got " . $v->type())
|
1014
|
|
|
|
|
|
|
if $v->type() ne "string";
|
1015
|
|
|
|
|
|
|
};
|
1016
|
|
|
|
|
|
|
$self->push_namespace($v->value());
|
1017
|
|
|
|
|
|
|
return $v;
|
1018
|
|
|
|
|
|
|
# (namespace-end)
|
1019
|
|
|
|
|
|
|
} elsif($fn eq "namespace-end") {
|
1020
|
|
|
|
|
|
|
$ast->error("namespace-end expects 0 argument") if $size != 1;
|
1021
|
|
|
|
|
|
|
$self->pop_namespace();
|
1022
|
|
|
|
|
|
|
return $nil;
|
1023
|
|
|
|
|
|
|
# (object-id obj)
|
1024
|
|
|
|
|
|
|
} elsif($fn eq "object-id") {
|
1025
|
|
|
|
|
|
|
$ast->error("object-id expects 1 argument") if $size != 2;
|
1026
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
1027
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $v->object_id());
|
1028
|
|
|
|
|
|
|
# (type obj)
|
1029
|
|
|
|
|
|
|
} elsif($fn eq "type") {
|
1030
|
|
|
|
|
|
|
$ast->error("type expects 1 argument") if $size != 2;
|
1031
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
1032
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $v->type());
|
1033
|
|
|
|
|
|
|
# (perlobj-type obj)
|
1034
|
|
|
|
|
|
|
} elsif($fn eq "perlobj-type") {
|
1035
|
|
|
|
|
|
|
$ast->error("perlobj-type expects 1 argument") if $size != 2;
|
1036
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
1037
|
|
|
|
|
|
|
$ast->error("perlobj-type expects perlobject as argument but got " . $v->type()) if($v->type() ne "perlobject");
|
1038
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", ref($v->value()));
|
1039
|
|
|
|
|
|
|
# (apply fn list)
|
1040
|
|
|
|
|
|
|
} elsif($fn eq "apply") {
|
1041
|
|
|
|
|
|
|
$ast->error("apply expects 2 arguments") if $size != 3;
|
1042
|
|
|
|
|
|
|
my $f = $self->_eval($ast->second());
|
1043
|
|
|
|
|
|
|
$ast->error("apply expects function as the first argument but got " . $f->type())
|
1044
|
|
|
|
|
|
|
if ($f->type() ne "function"
|
1045
|
|
|
|
|
|
|
and !($f->type() eq "symbol" and exists $builtin_funcs->{$f->value()}));
|
1046
|
|
|
|
|
|
|
my $l = $self->_eval($ast->third());
|
1047
|
|
|
|
|
|
|
$ast->error("apply expects list as the first argument but got " . $l->type()) if $l->type() ne "list";
|
1048
|
|
|
|
|
|
|
my $n = CljPerl::Seq->new("list");
|
1049
|
|
|
|
|
|
|
$n->append($f);
|
1050
|
|
|
|
|
|
|
foreach my $i (@{$l->value()}) {
|
1051
|
|
|
|
|
|
|
$n->append($i);
|
1052
|
|
|
|
|
|
|
}
|
1053
|
|
|
|
|
|
|
return $self->_eval($n);
|
1054
|
|
|
|
|
|
|
# (meta obj)
|
1055
|
|
|
|
|
|
|
} elsif($fn eq "meta") {
|
1056
|
|
|
|
|
|
|
$ast->error("meta expects 1 or 2 arguments") if $size < 2 or $size > 3;
|
1057
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
1058
|
|
|
|
|
|
|
if($size == 3){
|
1059
|
|
|
|
|
|
|
my $vm = $self->_eval($ast->third());
|
1060
|
|
|
|
|
|
|
$ast->error("meta expects 1 meta data as the second arguments but got " . $vm->type()) if $vm->type() ne "meta";
|
1061
|
|
|
|
|
|
|
$v->meta($vm);
|
1062
|
|
|
|
|
|
|
}
|
1063
|
|
|
|
|
|
|
my $m = $v->meta();
|
1064
|
|
|
|
|
|
|
$ast->error("no meta data in " . CljPerl::Printer::to_string($v)) if !defined $m;
|
1065
|
|
|
|
|
|
|
return $m;
|
1066
|
|
|
|
|
|
|
} elsif($fn eq "clj->string") {
|
1067
|
|
|
|
|
|
|
$ast->error("clj->string expects 1 argument") if $size != 2;
|
1068
|
|
|
|
|
|
|
my $v = $self->_eval($ast->second());
|
1069
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", CljPerl::Printer::to_string($v));
|
1070
|
|
|
|
|
|
|
# (.namespace function args...)
|
1071
|
|
|
|
|
|
|
} elsif($fn =~ /^(\.|->)(\S*)$/) {
|
1072
|
|
|
|
|
|
|
my $blessed = $1;
|
1073
|
|
|
|
|
|
|
my $ns = $2;
|
1074
|
|
|
|
|
|
|
$ast->error(". expects > 1 arguments") if $size < 2;
|
1075
|
|
|
|
|
|
|
$ast->error(". expects a symbol or keyword or stirng as the first argument but got " . $ast->second()->type())
|
1076
|
|
|
|
|
|
|
if ($ast->second()->type() ne "symbol"
|
1077
|
|
|
|
|
|
|
and $ast->second()->type() ne "keyword"
|
1078
|
|
|
|
|
|
|
and $ast->second()->type() ne "string");
|
1079
|
|
|
|
|
|
|
my $perl_func = $ast->second()->value();
|
1080
|
|
|
|
|
|
|
if($perl_func eq "require") {
|
1081
|
|
|
|
|
|
|
$ast->error(". require expects 1 argument") if $size != 3;
|
1082
|
|
|
|
|
|
|
my $m = $ast->third();
|
1083
|
|
|
|
|
|
|
if($m->type() eq "keyword" or $m->type() eq "symbol") {
|
1084
|
|
|
|
|
|
|
} elsif($m->type() eq "string") {
|
1085
|
|
|
|
|
|
|
$m = $self->_eval($ast->third());
|
1086
|
|
|
|
|
|
|
} else {
|
1087
|
|
|
|
|
|
|
$ast->error(". require expects a string but got " . $m->type());
|
1088
|
|
|
|
|
|
|
};
|
1089
|
|
|
|
|
|
|
my $mn = $m->value();
|
1090
|
|
|
|
|
|
|
$mn =~ s/::/\//g;
|
1091
|
|
|
|
|
|
|
foreach my $ext ("", ".pm") {
|
1092
|
|
|
|
|
|
|
if(-f $mn . $ext) {
|
1093
|
|
|
|
|
|
|
require $mn . $ext;
|
1094
|
|
|
|
|
|
|
return $true;
|
1095
|
|
|
|
|
|
|
};
|
1096
|
|
|
|
|
|
|
foreach my $p (@INC) {
|
1097
|
|
|
|
|
|
|
if(-f "$p/$mn$ext") {
|
1098
|
|
|
|
|
|
|
require "$p/$mn$ext";
|
1099
|
|
|
|
|
|
|
return $true;
|
1100
|
|
|
|
|
|
|
};
|
1101
|
|
|
|
|
|
|
}
|
1102
|
|
|
|
|
|
|
}
|
1103
|
|
|
|
|
|
|
$ast->error("cannot find $mn");
|
1104
|
|
|
|
|
|
|
} else {
|
1105
|
|
|
|
|
|
|
$ns = "CljPerl" if ! defined $ns or $ns eq "";
|
1106
|
|
|
|
|
|
|
my $meta = undef;
|
1107
|
|
|
|
|
|
|
$meta = $self->_eval($ast->third()) if defined $ast->third() and $ast->third()->type() eq "meta";
|
1108
|
|
|
|
|
|
|
$perl_func = $ns . "::" . $perl_func;
|
1109
|
|
|
|
|
|
|
my @rest = $ast->slice((defined $meta ? 3 : 2) .. $size-1);
|
1110
|
|
|
|
|
|
|
unshift @rest, CljPerl::Atom->new("string", $ns) if $blessed eq "->";
|
1111
|
|
|
|
|
|
|
return $self->perlfunc_call($perl_func, $meta, \@rest);
|
1112
|
|
|
|
|
|
|
}
|
1113
|
|
|
|
|
|
|
# (perl->clj o)
|
1114
|
|
|
|
|
|
|
} elsif($fn eq "perl->clj") {
|
1115
|
|
|
|
|
|
|
$ast->error("perl->clj expects 1 argument") if $size != 2;
|
1116
|
|
|
|
|
|
|
my $o = $self->_eval($ast->second());
|
1117
|
|
|
|
|
|
|
$ast->error("perl->clj expects perlobject as argument but got " . $o->type()) if $o->type() ne "perlobject";
|
1118
|
|
|
|
|
|
|
return &perl2clj($o->value());
|
1119
|
|
|
|
|
|
|
# (println obj)
|
1120
|
|
|
|
|
|
|
} elsif($fn eq "println") {
|
1121
|
|
|
|
|
|
|
$ast->error("println expects 1 argument") if $size != 2;
|
1122
|
|
|
|
|
|
|
print CljPerl::Printer::to_string($self->_eval($ast->second())) . "\n";
|
1123
|
|
|
|
|
|
|
return $nil;
|
1124
|
|
|
|
|
|
|
} elsif($fn eq "coro") {
|
1125
|
|
|
|
|
|
|
$ast->error("coro expects 1 argument") if $size != 2;
|
1126
|
|
|
|
|
|
|
my $b = $self->_eval($ast->second());
|
1127
|
|
|
|
|
|
|
$ast->error("core expects a function as argument but got " . $b->type()) if $b->type() ne "function";
|
1128
|
|
|
|
|
|
|
my $coro = new Coro sub {
|
1129
|
|
|
|
|
|
|
my $evaler = CljPerl::Evaler->new();
|
1130
|
|
|
|
|
|
|
my $fc = CljPerl::Seq->new("list");
|
1131
|
|
|
|
|
|
|
$fc->append($b);
|
1132
|
|
|
|
|
|
|
$evaler->_eval($fc);
|
1133
|
|
|
|
|
|
|
};
|
1134
|
|
|
|
|
|
|
$coro->ready();
|
1135
|
|
|
|
|
|
|
return CljPerl::Atom->new("coroutine", $coro);
|
1136
|
|
|
|
|
|
|
} elsif($fn eq "coro-suspend") {
|
1137
|
|
|
|
|
|
|
$ast->error("coro-suspend expects 1 argument") if $size != 2;
|
1138
|
|
|
|
|
|
|
my $coro = $self->_eval($ast->second());
|
1139
|
|
|
|
|
|
|
$ast->error("coro-suspend expects a coroutine as argument but got " . $coro->type()) if $coro->type() ne "coroutine";
|
1140
|
|
|
|
|
|
|
$coro->value()->suspend();
|
1141
|
|
|
|
|
|
|
return $coro;
|
1142
|
|
|
|
|
|
|
} elsif($fn eq "coro-sleep") {
|
1143
|
|
|
|
|
|
|
$ast->error("coro-sleep expects 0 argument") if $size != 1;
|
1144
|
|
|
|
|
|
|
$Coro::current->suspend();
|
1145
|
|
|
|
|
|
|
cede;
|
1146
|
|
|
|
|
|
|
return CljPerl::Atom->new("coroutine", $Coro::current);
|
1147
|
|
|
|
|
|
|
} elsif($fn eq "coro-yield") {
|
1148
|
|
|
|
|
|
|
$ast->error("coro-yield expects 0 argument") if $size != 1;
|
1149
|
|
|
|
|
|
|
cede;
|
1150
|
|
|
|
|
|
|
return CljPerl::Atom->new("coroutine", $Coro::current);
|
1151
|
|
|
|
|
|
|
} elsif($fn eq "coro-resume") {
|
1152
|
|
|
|
|
|
|
$ast->error("coro-resume expects 1 argument") if $size != 2;
|
1153
|
|
|
|
|
|
|
my $coro = $self->_eval($ast->second());
|
1154
|
|
|
|
|
|
|
$ast->error("coro-resume expects a coroutine as argument but got " . $coro->type()) if $coro->type() ne "coroutine";
|
1155
|
|
|
|
|
|
|
$coro->value()->resume();
|
1156
|
|
|
|
|
|
|
$coro->value()->cede_to();
|
1157
|
|
|
|
|
|
|
return $coro;
|
1158
|
|
|
|
|
|
|
} elsif($fn eq "coro-wake") {
|
1159
|
|
|
|
|
|
|
$ast->error("coro-wake expects 1 argument") if $size != 2;
|
1160
|
|
|
|
|
|
|
my $coro = $self->_eval($ast->second());
|
1161
|
|
|
|
|
|
|
$ast->error("coro-wake expects a coroutine as argument but got " . $coro->type()) if $coro->type() ne "coroutine";
|
1162
|
|
|
|
|
|
|
$coro->value()->resume();
|
1163
|
|
|
|
|
|
|
return $coro;
|
1164
|
|
|
|
|
|
|
} elsif($fn eq "join-coro") {
|
1165
|
|
|
|
|
|
|
$ast->error("join-coro expects 1 argument") if $size != 2;
|
1166
|
|
|
|
|
|
|
my $coro = $self->_eval($ast->second());
|
1167
|
|
|
|
|
|
|
$ast->error("join-coro expects a coroutine as argument but got " . $coro->type()) if $coro->type() ne "coroutine";
|
1168
|
|
|
|
|
|
|
$coro->value()->join();
|
1169
|
|
|
|
|
|
|
return $coro;
|
1170
|
|
|
|
|
|
|
} elsif($fn eq "coro-current") {
|
1171
|
|
|
|
|
|
|
$ast->error("coro-current expects 0 argument") if $size != 1;
|
1172
|
|
|
|
|
|
|
return CljPerl::Atom->new("coroutine", $Coro::current);
|
1173
|
|
|
|
|
|
|
} elsif($fn eq "coro-main") {
|
1174
|
|
|
|
|
|
|
$ast->error("coro-main expects 0 argument") if $size != 1;
|
1175
|
|
|
|
|
|
|
return CljPerl::Atom->new("coroutine", $Coro::main);
|
1176
|
|
|
|
|
|
|
} elsif($fn eq "trace-vars") {
|
1177
|
|
|
|
|
|
|
$ast->error("trace-vars expects 0 argument") if $size != 1;
|
1178
|
|
|
|
|
|
|
$self->trace_vars();
|
1179
|
|
|
|
|
|
|
return $nil;
|
1180
|
|
|
|
|
|
|
};
|
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
return $ast;
|
1183
|
|
|
|
|
|
|
}
|
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
sub perlfunc_call {
|
1186
|
|
|
|
|
|
|
my $self = shift;
|
1187
|
|
|
|
|
|
|
my $perl_func = shift;
|
1188
|
|
|
|
|
|
|
my $meta = shift;
|
1189
|
|
|
|
|
|
|
my $rargs = shift;
|
1190
|
|
|
|
|
|
|
my $ret_type = "scalar";
|
1191
|
|
|
|
|
|
|
my @fargtypes = ();
|
1192
|
|
|
|
|
|
|
if(defined $meta) {
|
1193
|
|
|
|
|
|
|
if(exists $meta->value()->{"return"}) {
|
1194
|
|
|
|
|
|
|
my $rt = $meta->value()->{"return"};
|
1195
|
|
|
|
|
|
|
$ast->error("return expects a string or keyword but got " . $rt->type())
|
1196
|
|
|
|
|
|
|
if $rt->type() ne "string"
|
1197
|
|
|
|
|
|
|
and $rt->type() ne "keyword";
|
1198
|
|
|
|
|
|
|
$ret_type = $rt->value();
|
1199
|
|
|
|
|
|
|
};
|
1200
|
|
|
|
|
|
|
if(exists $meta->value()->{"arguments"}) {
|
1201
|
|
|
|
|
|
|
my $ats = $meta->value()->{"arguments"};
|
1202
|
|
|
|
|
|
|
$ast->error("arguments expect a vector but got " . $ats->type()) if $ats->type() ne "vector";
|
1203
|
|
|
|
|
|
|
foreach my $arg (@{$ats->value()}) {
|
1204
|
|
|
|
|
|
|
$ast->error("arguments expect a vector of string or keyword but got " . $arg->type())
|
1205
|
|
|
|
|
|
|
if $arg->type() ne "string"
|
1206
|
|
|
|
|
|
|
and $arg->type() ne "keyword";
|
1207
|
|
|
|
|
|
|
push @fargtypes, $arg->value();
|
1208
|
|
|
|
|
|
|
};
|
1209
|
|
|
|
|
|
|
};
|
1210
|
|
|
|
|
|
|
};
|
1211
|
|
|
|
|
|
|
my @args = ();
|
1212
|
|
|
|
|
|
|
my $i = 0;
|
1213
|
|
|
|
|
|
|
foreach my $arg (@{$rargs}) {
|
1214
|
|
|
|
|
|
|
my $pobj = $self->clj2perl($self->_eval($arg));
|
1215
|
|
|
|
|
|
|
if($i < scalar @fargtypes) {
|
1216
|
|
|
|
|
|
|
my $ft = $fargtypes[$i];
|
1217
|
|
|
|
|
|
|
if($ft eq "scalar") {
|
1218
|
|
|
|
|
|
|
push @args, $pobj;
|
1219
|
|
|
|
|
|
|
} elsif($ft eq "array") {
|
1220
|
|
|
|
|
|
|
push @args, @{$pobj};
|
1221
|
|
|
|
|
|
|
} elsif($ft eq "hash") {
|
1222
|
|
|
|
|
|
|
push @args, %{$pobj};
|
1223
|
|
|
|
|
|
|
} elsif($ft eq "ref") {
|
1224
|
|
|
|
|
|
|
push @args, \$pobj;
|
1225
|
|
|
|
|
|
|
} else {
|
1226
|
|
|
|
|
|
|
push @args, $pobj;
|
1227
|
|
|
|
|
|
|
};
|
1228
|
|
|
|
|
|
|
} else {
|
1229
|
|
|
|
|
|
|
if(ref($pobj) eq "ARRAY") {
|
1230
|
|
|
|
|
|
|
push @args, @{$pobj};
|
1231
|
|
|
|
|
|
|
} elsif(ref($pobj) eq "HASH") {
|
1232
|
|
|
|
|
|
|
push @args, %{$pobj};
|
1233
|
|
|
|
|
|
|
} else {
|
1234
|
|
|
|
|
|
|
push @args, $pobj;
|
1235
|
|
|
|
|
|
|
};
|
1236
|
|
|
|
|
|
|
};
|
1237
|
|
|
|
|
|
|
$i ++;
|
1238
|
|
|
|
|
|
|
};
|
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
if($ret_type eq "scalar") {
|
1241
|
|
|
|
|
|
|
my $r = $perl_func->(@args);
|
1242
|
|
|
|
|
|
|
return &wrap_perlobj($r);
|
1243
|
|
|
|
|
|
|
} elsif($ret_type eq "ref-scalar") {
|
1244
|
|
|
|
|
|
|
my $r = $perl_func->(@args);
|
1245
|
|
|
|
|
|
|
return &wrap_perlobj(\$r);
|
1246
|
|
|
|
|
|
|
} elsif($ret_type eq "array") {
|
1247
|
|
|
|
|
|
|
my @r = $perl_func->(@args);
|
1248
|
|
|
|
|
|
|
return &wrap_perlobj(@r);
|
1249
|
|
|
|
|
|
|
} elsif($ret_type eq "ref-array") {
|
1250
|
|
|
|
|
|
|
my @r = $perl_func->(@args);
|
1251
|
|
|
|
|
|
|
return &wrap_perlobj(\@r);
|
1252
|
|
|
|
|
|
|
} elsif($ret_type eq "hash") {
|
1253
|
|
|
|
|
|
|
my %r = $perl_func->(@args);
|
1254
|
|
|
|
|
|
|
return &wrap_perlobj(%r);
|
1255
|
|
|
|
|
|
|
} elsif($ret_type eq "ref-hash") {
|
1256
|
|
|
|
|
|
|
my %r = $perl_func->(@args);
|
1257
|
|
|
|
|
|
|
return &wrap_perlobj(\%r);
|
1258
|
|
|
|
|
|
|
} elsif($ret_type eq "nil") {
|
1259
|
|
|
|
|
|
|
$perl_func->(@args);
|
1260
|
|
|
|
|
|
|
return $nil;
|
1261
|
|
|
|
|
|
|
} else {
|
1262
|
|
|
|
|
|
|
my $r = \$perl_func->(@args);
|
1263
|
|
|
|
|
|
|
return &wrap_perlobj($r);
|
1264
|
|
|
|
|
|
|
};
|
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
}
|
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
sub clj2perl {
|
1269
|
|
|
|
|
|
|
my $self = shift;
|
1270
|
|
|
|
|
|
|
my $ast = shift;
|
1271
|
|
|
|
|
|
|
my $type = $ast->type();
|
1272
|
|
|
|
|
|
|
my $value = $ast->value();
|
1273
|
|
|
|
|
|
|
if($type eq "string" or $type eq "number"
|
1274
|
|
|
|
|
|
|
or $type eq "quotation" or $type eq "keyword"
|
1275
|
|
|
|
|
|
|
or $type eq "perlobject") {
|
1276
|
|
|
|
|
|
|
return $value;
|
1277
|
|
|
|
|
|
|
} elsif($type eq "bool") {
|
1278
|
|
|
|
|
|
|
if($value eq "true") {
|
1279
|
|
|
|
|
|
|
return 1;
|
1280
|
|
|
|
|
|
|
} else {
|
1281
|
|
|
|
|
|
|
return 0;
|
1282
|
|
|
|
|
|
|
}
|
1283
|
|
|
|
|
|
|
} elsif($type eq "nil") {
|
1284
|
|
|
|
|
|
|
return undef;
|
1285
|
|
|
|
|
|
|
} elsif($type eq "list" or $type eq "vector") {
|
1286
|
|
|
|
|
|
|
my @r = ();
|
1287
|
|
|
|
|
|
|
foreach my $i (@{$value}) {
|
1288
|
|
|
|
|
|
|
push @r, $self->clj2perl($i);
|
1289
|
|
|
|
|
|
|
};
|
1290
|
|
|
|
|
|
|
return \@r;
|
1291
|
|
|
|
|
|
|
} elsif($type eq "map") {
|
1292
|
|
|
|
|
|
|
my %r = ();
|
1293
|
|
|
|
|
|
|
foreach my $k (keys %{$value}) {
|
1294
|
|
|
|
|
|
|
$r{$k} = $self->clj2perl($value->{$k});
|
1295
|
|
|
|
|
|
|
};
|
1296
|
|
|
|
|
|
|
return \%r;
|
1297
|
|
|
|
|
|
|
} elsif($type eq "function") {
|
1298
|
|
|
|
|
|
|
my $f = sub {
|
1299
|
|
|
|
|
|
|
my @args = @_;
|
1300
|
|
|
|
|
|
|
my $cljf = CljPerl::Seq->new("list");
|
1301
|
|
|
|
|
|
|
$cljf->append($ast);
|
1302
|
|
|
|
|
|
|
foreach my $arg (@args) {
|
1303
|
|
|
|
|
|
|
$cljf->append(&perl2clj($arg));
|
1304
|
|
|
|
|
|
|
};
|
1305
|
|
|
|
|
|
|
return $self->clj2perl($self->_eval($cljf));
|
1306
|
|
|
|
|
|
|
};
|
1307
|
|
|
|
|
|
|
return $f;
|
1308
|
|
|
|
|
|
|
} else {
|
1309
|
|
|
|
|
|
|
$ast->error("unsupported type " . $type . " for clj2perl object conversion");
|
1310
|
|
|
|
|
|
|
}
|
1311
|
|
|
|
|
|
|
}
|
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
sub wrap_perlobj {
|
1314
|
|
|
|
|
|
|
my $v = shift;
|
1315
|
|
|
|
|
|
|
while(ref($v) eq "REF") {
|
1316
|
|
|
|
|
|
|
$v = ${$v};
|
1317
|
|
|
|
|
|
|
}
|
1318
|
|
|
|
|
|
|
return CljPerl::Atom->new("perlobject", $v);
|
1319
|
|
|
|
|
|
|
}
|
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
sub perl2clj {
|
1322
|
|
|
|
|
|
|
my $v = shift; #$ast->value();
|
1323
|
|
|
|
|
|
|
if(! defined ref($v) or ref($v) eq ""){
|
1324
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", $v);
|
1325
|
|
|
|
|
|
|
} elsif(ref($v) eq "SCALAR") {
|
1326
|
|
|
|
|
|
|
return CljPerl::Atom->new("string", ${$v});
|
1327
|
|
|
|
|
|
|
} elsif(ref($v) eq "HASH") {
|
1328
|
|
|
|
|
|
|
my %m = ();
|
1329
|
|
|
|
|
|
|
foreach my $k (keys %{$v}) {
|
1330
|
|
|
|
|
|
|
$m{$k} = &perl2clj($v->{$k});
|
1331
|
|
|
|
|
|
|
};
|
1332
|
|
|
|
|
|
|
return CljPerl::Atom->new("map", \%m);
|
1333
|
|
|
|
|
|
|
} elsif(ref($v) eq "ARRAY") {
|
1334
|
|
|
|
|
|
|
my @a = ();
|
1335
|
|
|
|
|
|
|
foreach my $i (@{$v}) {
|
1336
|
|
|
|
|
|
|
push @a, &perl2clj($i);
|
1337
|
|
|
|
|
|
|
};
|
1338
|
|
|
|
|
|
|
return CljPerl::Atom->new("vector", \@a);
|
1339
|
|
|
|
|
|
|
} elsif(ref($v) eq "CODE") {
|
1340
|
|
|
|
|
|
|
return CljPerl::Atom->new("perlfunction", $v);
|
1341
|
|
|
|
|
|
|
} else {
|
1342
|
|
|
|
|
|
|
return CljPerl::Atom->new("perlobject", $v);
|
1343
|
|
|
|
|
|
|
#$ast->error("expect a reference of scalar or hash or array");
|
1344
|
|
|
|
|
|
|
};
|
1345
|
|
|
|
|
|
|
}
|
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
sub trace_vars {
|
1348
|
|
|
|
|
|
|
my $self = shift;
|
1349
|
|
|
|
|
|
|
print @{$self->scopes()} . "\n";
|
1350
|
|
|
|
|
|
|
foreach my $vn (keys %{$self->current_scope()}) {
|
1351
|
|
|
|
|
|
|
print "$vn\n" # . CljPerl::Printer::to_string(${$self->current_scope()}{$vn}->value()) . "\n";
|
1352
|
|
|
|
|
|
|
};
|
1353
|
|
|
|
|
|
|
}
|
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
1;
|