File Coverage

blib/lib/CljPerl/Evaler.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


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;