File Coverage

blib/lib/Language/LispPerl/Evaler.pm
Criterion Covered Total %
statement 361 502 71.9
branch 125 232 53.8
condition 67 120 55.8
subroutine 47 49 95.9
pod 13 34 38.2
total 613 937 65.4


line stmt bran cond sub pod time code
1             package Language::LispPerl::Evaler;
2             $Language::LispPerl::Evaler::VERSION = '0.006';
3 6     6   2952 use Moose;
  6         1937827  
  6         38  
4              
5 6     6   32676 use File::ShareDir;
  6         28828  
  6         308  
6 6     6   33 use File::Spec;
  6         7  
  6         95  
7 6     6   19 use File::Basename;
  6         7  
  6         332  
8              
9 6     6   2546 use Language::LispPerl::Reader;
  6         17  
  6         262  
10 6     6   2803 use Language::LispPerl::Var;
  6         17  
  6         221  
11 6     6   38 use Language::LispPerl::Printer;
  6         8  
  6         93  
12 6     6   3384 use Language::LispPerl::BuiltIns;
  6         170  
  6         222  
13              
14 6     6   32 use Log::Any qw/$log/;
  6         6  
  6         43  
15              
16             BEGIN{
17             # The test compatible File::Share
18 6     6   1276 eval{ require File::Share; File::Share->import('dist_dir'); };
  6         2923  
  6         3168  
19 6 50       24471 if( $@ ){
20             # The production only File::ShareDir
21 0         0 require File::ShareDir;
22 0         0 File::ShareDir->import('dist_dir');
23             }
24             };
25              
26              
27             our $namespace_key = "0namespace0";
28              
29             has 'scopes' => ( is => 'ro', default => sub{
30             return [ { $namespace_key => [] } ]
31             });
32             has 'loaded_files' => ( is => 'ro', default => sub{ {}; } );
33              
34             has 'file_stack' => ( is => 'ro', default => sub{ []; } );
35             has 'caller' => ( is => 'ro' , default => sub{ []; } );
36              
37             has 'quotation_scope' => ( is => 'ro', default => 0 );
38             has 'syntaxquotation_scope' => ( is => 'ro', default => 0 );
39              
40             has 'exception' => ( is => 'rw' );
41              
42             # The container for the builtin functions.
43             has 'builtins' => ( is => 'ro', lazy_build => 1 );
44              
45             sub _build_builtins{
46 8     8   13 my ($self) = @_;
47 8         263 return Language::LispPerl::BuiltIns->new({ evaler => $self });
48             }
49              
50             sub to_hash{
51 4     4 0 21 my ($self) = @_;
52             return {
53 4         105 'scopes' => Language::LispPerl::Printer::to_perl( $self->scopes() ),
54             'loaded_files' => Language::LispPerl::Printer::to_perl( $self->loaded_files() ),
55             'file_stack' => Language::LispPerl::Printer::to_perl( $self->file_stack() ),
56             'caller' => Language::LispPerl::Printer::to_perl( $self->caller() ),
57             'quotation_scope' => Language::LispPerl::Printer::to_perl( $self->quotation_scope() ),
58             'syntaxquotation_scope' => Language::LispPerl::Printer::to_perl( $self->syntaxquotation_scope() ),
59             'exception' => Language::LispPerl::Printer::to_perl( $self->exception() ),
60             __class => $self->blessed(),
61             };
62             }
63              
64             sub from_hash{
65 2     2 0 1314 my ($class, $hash) = @_;
66             return $class->new({
67 2         8 map { $_ => Language::LispPerl::Reader::from_perl( $hash->{$_} ) } keys %$hash
  16         32  
68             });
69             }
70              
71             =head2 new_instance
72              
73             Returns a new instance of this with the same builtins and everything else reset.
74              
75             Usage:
76              
77             my $other_evaler = $this->new_instance();
78              
79             =cut
80              
81             sub new_instance{
82 0     0 1 0 my ($self) = @_;
83 0         0 return ref($self)->new( { builtins => $self->builtins() } );
84             }
85              
86             sub clear_exception{
87 3     3 0 5 my ($self) = @_;
88 3         6 $self->{exception} = undef;
89             }
90              
91             sub push_scope {
92 57     57 0 63 my $self = shift;
93 57   33     104 my $context = shift // confess("Cannot push undef context");
94 57         87 my %c = %{$context};
  57         262  
95 57         65 my @ns = @{ $c{$namespace_key} };
  57         107  
96 57         78 $c{$namespace_key} = \@ns;
97 57         51 unshift @{ $self->scopes() }, \%c;
  57         1376  
98             }
99              
100             sub pop_scope {
101 57     57 0 63 my $self = shift;
102 57         44 shift @{ $self->scopes() };
  57         1347  
103             }
104              
105             sub current_scope {
106 1202     1202 0 819 my $self = shift;
107 1202         736 my $scope = @{ $self->scopes() }[0];
  1202         26642  
108 1202         1529 return $scope;
109             }
110              
111             sub push_caller {
112 57     57 0 64 my $self = shift;
113 57         59 my $ast = shift;
114 57         52 unshift @{ $self->caller() }, $ast;
  57         1281  
115             }
116              
117             sub pop_caller {
118 57     57 0 56 my $self = shift;
119 57         50 shift @{ $self->caller() };
  57         1350  
120             }
121              
122             sub caller_size {
123 6     6 0 7 my $self = shift;
124 6         4 scalar @{ $self->caller() };
  6         135  
125             }
126              
127             =head2 copy_caller
128              
129             Returns a shallow copy of this caller's stack.
130              
131             Usage:
132              
133             my $caller_stack = $this->copy_caller();
134              
135             =cut
136              
137             sub copy_caller{
138 4     4 1 9 my ($self) = @_;
139 4         4 return [ @{ $self->caller() } ];
  4         90  
140             }
141              
142             =head2 copy_current_scope
143              
144             Take a shallow copy of the current scope that
145             is adequate for function and macro contexts
146              
147             =cut
148              
149             sub copy_current_scope{
150 66     66 1 83 my ($self) = @_;
151             # Take a shallow copy of the current_scope
152 66         76 my %c = %{ $self->current_scope() };
  66         108  
153              
154             # Take a shallow copy of the namespace (keyed by namespace_key)
155 66         88 my @ns = @{ $c{$namespace_key} };
  66         127  
156 66         91 $c{$namespace_key} = \@ns;
157              
158 66         603 return \%c;
159             }
160              
161             sub push_namespace {
162 2     2 0 4 my $self = shift;
163 2         3 my $namespace = shift;
164 2         31 my $scope = $self->current_scope();
165 2         5 unshift @{ $scope->{$namespace_key} }, $namespace;
  2         8  
166             }
167              
168             sub pop_namespace {
169 2     2 0 3 my $self = shift;
170 2         5 my $scope = $self->current_scope();
171 2         3 shift @{ $scope->{$namespace_key} };
  2         7  
172             }
173              
174             sub current_namespace {
175 688     688 0 499 my $self = shift;
176 688         728 my $scope = $self->current_scope();
177 688         494 my $namespace = @{ $scope->{$namespace_key} }[0];
  688         780  
178 688 100       2900 return "" if ( !defined $namespace );
179 42         208 return $namespace;
180             }
181              
182             =head2 new_var
183              
184             From a name and a value, creates a new L<Language::LispPerl::Var> under the
185             key 'name' in $this->current_scope();
186              
187             Usage:
188              
189             $this->new_var( 'bla' , 1 );
190              
191             =cut
192              
193             sub new_var {
194 181     181 1 150 my $self = shift;
195 181         164 my $name = shift;
196 181         138 my $value = shift;
197 181         224 my $scope = $self->current_scope();
198 181         270 $name = $self->current_namespace() . "#" . $name;
199 181         4573 $scope->{$name} = Language::LispPerl::Var->new({ name => $name, value => $value });
200             }
201              
202             =head2 var
203              
204             Lookup the L<Language::LispPerl::Var> by name in the current scope or in the current namespace.
205             Returns undef if no such variable is found.
206              
207             Usage:
208              
209             if( my $var = $this->var( 'blabla' ) ){
210             ...
211             }
212              
213             =cut
214              
215             sub var {
216 259     259 1 219 my $self = shift;
217 259         217 my $name = shift;
218 259         362 my $scope = $self->current_scope();
219 259 100       566 if ( exists $scope->{$name} ) {
    100          
    50          
220 4         7 return $scope->{$name};
221             }
222             elsif ( exists $scope->{ $self->current_namespace() . "#" . $name } ) {
223 250         314 return $scope->{ $self->current_namespace() . "#" . $name };
224             }
225             elsif ( exists $scope->{ "#" . $name } ) {
226 5         9 return $scope->{ "#" . $name };
227             }
228 0         0 return undef;
229             }
230              
231             =head2 current_file
232              
233             Returns the current file on the file_stack or '.' if no such thing
234             exists.
235              
236             =cut
237              
238             sub current_file {
239 0     0 1 0 my $self = shift;
240 0         0 my $sd = scalar @{ $self->{file_stack} };
  0         0  
241 0 0       0 if ( $sd == 0 ) {
242 0         0 return ".";
243             }
244             else {
245 0         0 return ${ $self->{file_stack} }[ $sd - 1 ];
  0         0  
246             }
247             }
248              
249             =head2 search_file
250              
251             Looks up the given file name (fully qualified, with clp extension or not)
252             in this package's share directory or in @INC.
253              
254             dies with an error if no file can be found.
255              
256             Usage:
257              
258             $this->search_file('core');
259              
260             =cut
261              
262             sub search_file {
263 6     6 1 9 my $self = shift;
264 6         7 my $file = shift;
265              
266 6         28 my $dist_dir = dist_dir( 'Language-LispPerl' );
267 6         1265 $log->debug("Using dist dir = $dist_dir");
268              
269 6         42 foreach my $ext ( '', '.clp' ) {
270 9 100       235 if ( -f "$file$ext" ) {
    100          
271 1         35 return "$file$ext";
272             }
273             elsif ( -f $dist_dir. "/lisp/$file$ext" ) {
274 5         98 return $dist_dir . "/lisp/$file$ext";
275             }
276 3         7 foreach my $p (@INC) {
277 33 50       452 if ( -f "$p/$file$ext" ) {
278 0         0 return "$p/$file$ext";
279             }
280             }
281             }
282 0         0 Language::LispPerl::Logger::error( "cannot find " . $file );
283             }
284              
285             =head2 load
286              
287             Reads a file once if it hasn't been read before, for loading
288             libraries in the global scope.
289              
290             Usage:
291              
292             $this->load(/path/to/file.clp');
293              
294             =cut
295              
296             sub load {
297 6     6 1 19 my $self = shift;
298 6         7 my $file = shift;
299              
300             Language::LispPerl::Logger::error(
301             "cannot require file " . $file . " in non-global scope" )
302 6 50       11 if scalar @{ $self->scopes() } > 1;
  6         156  
303              
304 6         22 $file = File::Spec->rel2abs( $self->search_file($file) );
305              
306 6 100       29 return 1 if exists $self->{loaded_files}->{$file};
307 5         14 $self->{loaded_files}->{$file} = 1;
308 5         49 push @{ $self->{file_stack} }, $file;
  5         11  
309 5         16 my $res = $self->read($file);
310 5         6 pop @{ $self->{file_stack} };
  5         15  
311 5         34 return $res;
312             }
313              
314             =head2 read
315              
316             Reads and evaluates in this evaler
317             all the expressions in the given filename
318             and returns the last evaluation result.
319              
320             Usage:
321              
322             $this->read('/path/to/file.clp');
323              
324             =cut
325              
326             sub read {
327 6     6 1 9 my $self = shift;
328 6         7 my $file = shift;
329 6         47 my $reader = Language::LispPerl::Reader->new();
330 6         26 $reader->read_file($file);
331 6         13 my $res = undef;
332 6     45   26 $reader->ast()->each( sub { $res = $self->_eval( $_[0] ) } );
  45         121  
333 6         240 return $res;
334             }
335              
336             our $empty_list = Language::LispPerl::Seq->new({ type => "list" });
337             our $true = Language::LispPerl::Atom->new({type => "bool", value => "true" });
338             our $false = Language::LispPerl::Atom->new({type => "bool", value => "false"});
339             our $nil = Language::LispPerl::Atom->new({type => "nil", value => "nil"});
340              
341 21     21 0 252 sub true{ return $true; }
342 11     11 0 113 sub false{ return $false; }
343 12     12 0 39 sub nil{ return $nil; }
344 1     1 0 2 sub empty_list{ return $empty_list; }
345              
346             =head2 eval
347              
348             Evaluates a string and returns the result of the latest expression (or dies
349             with an error).
350              
351             Return the nil/nil atom when the given string is empty.
352              
353             Usage:
354              
355             my $res = $this->eval(q|( - 1 1 ) ( + 1 2 )|);
356             # $res->value() is 3
357              
358             =cut
359              
360             sub eval {
361 103     103 1 7474 my ($self, $str) = @_;
362 103 100       426 unless( length( defined( $str ) ? $str : '' ) ){
    100          
363 1         34 return $nil;
364             }
365              
366 102         669 my $reader = Language::LispPerl::Reader->new();
367 102         284 $reader->read_string($str);
368 102         152 my $res = undef;
369 102     112   248 $reader->ast()->each( sub { $res = $self->_eval( $_[0] ) } );
  112         306  
370 100         3297 return $res;
371             }
372              
373              
374             =head2 bind
375              
376             Associate the current L<Language::LispPerl::Atom> or L<Language::LispPerl::Seq>
377             with the correct Perl/Lisp space values.
378              
379             =cut
380              
381             sub bind {
382 669     669 1 573 my $self = shift;
383 669         461 my $ast = shift;
384 669         14414 my $class = $ast->class();
385 669         14087 my $type = $ast->type();
386 669         14120 my $value = $ast->value();
387 669 100 100     9151 if ( $type eq "symbol" and $value eq "true" ) {
    100 100        
    100 100        
    50 66        
    50 100        
    100 66        
    100 66        
    50 66        
    100 66        
388 12         31 return $true;
389             }
390             elsif ( $type eq "symbol" and $value eq "false" ) {
391 7         16 return $false;
392             }
393             elsif ( $type eq "symbol" and $value eq "nil" ) {
394 1         4 return $nil;
395             }
396             elsif ( $type eq "accessor" ) {
397 0         0 return Language::LispPerl::Atom->new({ type => "accessor", value => $self->bind($value) } );
398             }
399             elsif ( $type eq "sender" ) {
400 0         0 return Language::LispPerl::Atom->new({ type => "sender", value => $self->bind($value) });
401             }
402             elsif ( $type eq "syntaxquotation" or $type eq "quotation" ) {
403 41 50       91 $self->{syntaxquotation_scope} += 1 if $type eq "syntaxquotation";
404 41 50       76 $self->{quotation_scope} += 1 if $type eq "quotation";
405 41         88 my $r = $self->bind($value);
406 41 50       101 $self->{syntaxquotation_scope} -= 1 if $type eq "syntaxquotation";
407 41 50       80 $self->{quotation_scope} -= 1 if $type eq "quotation";
408 41         88 return $r;
409             }
410             elsif (
411             (
412             $type eq "symbol" and $self->{syntaxquotation_scope} == 0
413             and $self->{quotation_scope} == 0
414             )
415             or ( $type eq "dequotation" and $self->{syntaxquotation_scope} > 0 )
416             )
417             {
418             $ast->error("dequotation should be in syntax quotation scope")
419 455 50 66     908 if ( $type eq "dequotation" and $self->{syntaxquotation_scope} == 0 );
420 455         429 my $name = $value;
421 455 100 100     976 if ( $type eq "dequotation" and $value =~ /^@(\S+)$/ ) {
422 33         54 $name = $1;
423             }
424 455 100       747 return $ast
425             if $self->word_is_reserved( $name );
426 195         323 my $var = $self->var($name);
427 195 50       285 $ast->error("unbound symbol '$name'") if !defined $var;
428 195         4320 return $var->value();
429             }
430             elsif ( $type eq "symbol"
431             and $self->{quotation_scope} > 0 )
432             {
433 0         0 my $q = Language::LispPerl::Atom->new({ type => "quotation", value => $value });
434 0         0 return $q;
435             }
436             elsif ( $class eq "Seq" ) {
437 70 50 66     218 return $empty_list if $type eq "list" and $ast->size() == 0;
438 70         1648 my $list = Language::LispPerl::Seq->new({ type => "list" });
439 70         1589 $list->type($type);
440 70         61 foreach my $i ( @{$value} ) {
  70         98  
441 206 100 100     4559 if ( $i->type() eq "dequotation" and $i->value() =~ /^@/ ) {
442 33         72 my $dl = $self->bind($i);
443 33 50       734 $i->error( "~@ should be given a list but got " . $dl->type() )
444             if $dl->type() ne "list";
445 33         32 foreach my $di ( @{ $dl->value() } ) {
  33         704  
446 49         90 $list->append($di);
447             }
448             }
449             else {
450 173         270 $list->append( $self->bind($i) );
451             }
452             }
453 70         121 return $list;
454             }
455 83         199 return $ast;
456             }
457              
458             sub _eval {
459 930     930   694 my $self = shift;
460 930         594 my $ast = shift;
461 930         21453 my $class = $ast->class();
462 930         20103 my $type = $ast->type();
463 930         19909 my $value = $ast->value();
464 930 100 100     3447 if ( $type eq "list" ) {
    50 100        
    50 66        
    100 66        
    100          
    50          
    100          
    100          
    100          
465 313         647 my $size = $ast->size();
466 313 50       547 if ( $size == 0 ) {
467 0         0 return $empty_list;
468             }
469 313         555 my $f = $self->_eval( $ast->first() );
470 313         6836 my $ftype = $f->type();
471 313         6765 my $fvalue = $f->value();
472 313 100       704 if ( $ftype eq "symbol" ) {
    50          
    50          
    100          
    50          
    50          
473 259         528 return $self->builtin( $f, $ast );
474             }
475             elsif ( $ftype eq "key accessor" ) {
476 0 0       0 $ast->error("key accessor expects >= 1 arguments") if $size == 1;
477 0         0 my $m = $self->_eval( $ast->second() );
478 0         0 my $mtype = $m->type();
479 0         0 my $mvalue = $m->value();
480 0 0 0     0 $ast->error(
481             "key accessor expects a map or meta as the first arguments but got "
482             . $mtype )
483             if $mtype ne "map" and $mtype ne "meta";
484 0 0       0 if ( $size == 2 ) {
    0          
485              
486             #$ast->error("key " . $fvalue . " does not exist")
487 0 0       0 return $nil if !exists $mvalue->{$fvalue};
488 0         0 return $mvalue->{$fvalue};
489             }
490             elsif ( $size == 3 ) {
491 0         0 my $v = $self->_eval( $ast->third() );
492 0 0       0 if ( $v->type() eq "nil" ) {
493 0         0 delete $mvalue->{$fvalue};
494 0         0 return $nil;
495             }
496             else {
497 0         0 $mvalue->{$fvalue} = $v;
498 0         0 return $mvalue->{$fvalue};
499             }
500             }
501             else {
502 0         0 $ast->error("key accessor expects <= 2 arguments");
503             }
504             }
505             elsif ( $ftype eq "index accessor" ) {
506 0 0       0 $ast->error("index accessor expects >= 1 arguments") if $size == 1;
507 0         0 my $v = $self->_eval( $ast->second() );
508 0         0 my $vtype = $v->type();
509 0         0 my $vvalue = $v->value();
510 0 0 0     0 $ast->error(
      0        
511             "index accessor expects a vector or list or xml as the first arguments but got "
512             . $vtype )
513             if $vtype ne "vector"
514             and $vtype ne "list"
515             and $vtype ne "xml";
516             $ast->error("index is bigger than size")
517 0 0       0 if $fvalue >= scalar @{$vvalue};
  0         0  
518 0 0       0 if ( $size == 2 ) {
    0          
519 0         0 return $vvalue->[$fvalue];
520             }
521             elsif ( $size == 3 ) {
522 0         0 $vvalue->[$fvalue] = $self->_eval( $ast->third() );
523 0         0 return $vvalue->[$fvalue];
524             }
525             else {
526 0         0 $ast->error("index accessor expects <= 2 arguments");
527             }
528             }
529             elsif ( $ftype eq "function" ) {
530             # Fallback to current scope if the function
531             # definition didnt shallow copy its current scope at the time of definition.
532             # This is the case when the evaler is persisted and then defrosted.
533 19   66     432 my $scope = $f->context() // $self->copy_current_scope();
534              
535 19         19 my $fn = $fvalue;
536 19         46 my $fargs = $fn->second();
537 19         69 my @rargs = $ast->slice( 1 .. $size - 1 );
538 19         27 my @rrargs = ();
539 19         32 foreach my $arg (@rargs) {
540 21         38 push @rrargs, $self->_eval($arg);
541             }
542 19         39 $self->push_scope($scope);
543 19         41 $self->push_caller($fn);
544 19         25 my $rest_args = undef;
545 19         17 my $i = 0;
546 19         410 my $fargsvalue = $fargs->value();
547 19         18 my $fargsn = scalar @{$fargsvalue};
  19         23  
548 19         19 my $rrargsn = scalar @rrargs;
549              
550 19         54 for ( $i = 0 ; $i < $fargsn ; $i++ ) {
551 21         499 my $name = $fargsvalue->[$i]->value();
552 21 50       46 if ( $name eq "&" ) {
553 0         0 $i++;
554 0         0 $name = $fargsvalue->[$i]->value();
555 0         0 $rest_args = Language::LispPerl::Seq->new({ type => "list" });
556 0         0 $self->new_var( $name, $rest_args );
557             }
558             else {
559 21 50       42 $ast->error("real arguments < formal arguments")
560             if $i >= $rrargsn;
561 21         49 $self->new_var( $name, $rrargs[$i] );
562             }
563             }
564 19 50       37 if ( defined $rest_args ) {
565 0         0 $i -= 2;
566 0         0 for ( ; $i < $rrargsn ; $i++ ) {
567 0         0 $rest_args->append( $rrargs[$i] );
568             }
569             }
570             else {
571 19 50       38 $ast->error("real arguments > formal arguments")
572             if $i < $rrargsn;
573             }
574 19         47 my @body = $fn->slice( 2 .. $fn->size() - 1 );
575 19         19 my $res;
576 19         28 foreach my $b (@body) {
577 19         46 $res = $self->_eval($b);
578             }
579 19         52 $self->pop_scope();
580 19         409 $self->pop_caller();
581 19         257 return $res;
582             }
583             elsif ( $ftype eq "perlfunction" ) {
584 0         0 my $meta = undef;
585 0 0 0     0 $meta = $self->_eval( $ast->second() )
586             if defined $ast->second()
587             and $ast->second()->type() eq "meta";
588 0         0 my $perl_func = \&{ $f->value() };
  0         0  
589 0 0       0 my @args = $ast->slice( ( defined $meta ? 2 : 1 ) .. $size - 1 );
590 0         0 return $self->perlfunc_call( $perl_func, $meta, \@args, $ast );
591             }
592             elsif ( $ftype eq "macro" ) {
593 35   66     778 my $scope = $f->context() // $self->copy_current_scope();
594 35         42 my $fn = $fvalue;
595 35         98 my $fargs = $fn->third();
596 35         91 my @rargs = $ast->slice( 1 .. $ast->size() - 1 );
597 35         85 $self->push_scope($scope);
598 35         74 $self->push_caller($fn);
599 35         46 my $rest_args = undef;
600 35         31 my $i = 0;
601 35         774 my $fargsvalue = $fargs->value();
602 35         33 my $fargsn = scalar @{$fargsvalue};
  35         41  
603 35         43 my $rargsn = scalar @rargs;
604              
605 35         85 for ( $i = 0 ; $i < $fargsn ; $i++ ) {
606 98         2368 my $name = $fargsvalue->[$i]->value();
607 98 100       155 if ( $name eq "&" ) {
608 33         30 $i++;
609 33         734 $name = $fargsvalue->[$i]->value();
610 33         841 $rest_args = Language::LispPerl::Seq->new({ type => "list" });
611 33         86 $self->new_var( $name, $rest_args );
612             }
613             else {
614 65 50       102 $ast->error("real arguments < formal arguments")
615             if $i >= $rargsn;
616 65         113 $self->new_var( $name, $rargs[$i] );
617             }
618             }
619 35 100       58 if ( defined $rest_args ) {
620 33         37 $i -= 2;
621 33         69 for ( ; $i < $rargsn ; $i++ ) {
622 49         108 $rest_args->append( $rargs[$i] );
623             }
624             }
625             else {
626 2 50       7 $ast->error("real arguments > formal arguments")
627             if $i < $rargsn;
628             }
629 35         88 my @body = $fn->slice( 3 .. $fn->size() - 1 );
630 35         34 my $res;
631 35         56 foreach my $b (@body) {
632 35         69 $res = $self->_eval($b);
633             }
634 35         75 $self->pop_scope();
635 35         891 $self->pop_caller();
636 35         72 return $self->_eval($res);
637             }
638             else {
639 0         0 $ast->error("expect a function or function name or index/key accessor");
640             }
641             }
642             elsif ( $type eq "accessor" ) {
643 0         0 my $av = $self->_eval($value);
644 0         0 my $a = Language::LispPerl::Atom->new({ type => "unknown", value => $av->value() });
645 0         0 my $at = $av->type();
646 0 0 0     0 if ( $at eq "number" ) {
    0          
647 0         0 $a->type("index accessor");
648             }
649             elsif ( $at eq "string" or $at eq "keyword" ) {
650 0         0 $a->type("key accessor");
651             }
652             else {
653 0         0 $ast->error(
654             "unsupport type " . $at . " for accessor but got " . $at );
655             }
656 0         0 return $a;
657             }
658             elsif ( $type eq "sender" ) {
659 0         0 my $sn = $self->_eval($value);
660 0 0 0     0 $ast->error( "sender expects a string or keyword but got " . $type )
661             if $sn->type() ne "string"
662             and $sn->type() ne "keyword";
663 0         0 my $s = Language::LispPerl::Atom->new({ type => "symbol", value => $sn->value() });
664 0         0 return $self->bind($s);
665             }
666             elsif ( $type eq "symbol" ) {
667 377         664 return $self->bind($ast);
668             }
669             elsif ( $type eq "syntaxquotation" ) {
670 41         73 return $self->bind($ast);
671             }
672             elsif ( $type eq "quotation" ) {
673 0         0 return $self->bind($ast);
674             }
675             elsif ( $class eq "Seq" and $type eq "vector" ) {
676 5         120 my $v = Language::LispPerl::Atom->new({ type => "vector" });
677 5         11 my @vv = ();
678 5         6 foreach my $i ( @{$value} ) {
  5         11  
679 12         19 push @vv, $self->_eval($i);
680             }
681 5         117 $v->value( \@vv );
682 5         15 return $v;
683             }
684             elsif ( $class eq "Seq" and ( $type eq "map" or $type eq "meta" ) ) {
685 13         323 my $m = Language::LispPerl::Atom->new({ type => "map" });
686 13         31 my %mv = ();
687 13         11 my $n = scalar @{$value};
  13         20  
688 13 50       41 $ast->error( $type . " should have even number of items" )
689             if ( $n % 2 ) != 0;
690 13         38 for ( my $i = 0 ; $i < $n ; $i += 2 ) {
691 14         44 my $k = $self->_eval( $value->[$i] );
692 14 50 33     325 $ast->error( $type
693             . " expects keyword or string as key but got "
694             . $k->type() )
695             if ( $k->type() ne "keyword"
696             and $k->type() ne "string" );
697 14         35 my $v = $self->_eval( $value->[ $i + 1 ] );
698 14         303 $mv{ $k->value() } = $v;
699             }
700 13         280 $m->value( \%mv );
701 13 100       222 $m->type("meta") if $type eq "meta";
702 13         31 return $m;
703             }
704             elsif ( $class eq "Seq" and $type eq "xml" ) {
705 10         23 my $size = $ast->size();
706 10 50       20 $ast->error("xml expects >= 1 arguments") if $size == 0;
707 10         20 my $first = $ast->first();
708 10         218 my $firsttype = $first->type();
709 10 50       24 if ( $firsttype ne "symbol" ) {
710 0         0 $first = $self->_eval($first);
711 0         0 $firsttype = $first->type();
712             }
713             $ast->error(
714 10 0 33     20 "xml expects a symbol or string or keyword as name but got "
      33        
715             . $firsttype )
716             if $firsttype ne "symbol"
717             and $firsttype ne "string"
718             and $firsttype ne "keyword";
719 10         12 my @items = ();
720 10         244 my $xml = Language::LispPerl::Atom->new({ type => "xml", value => \@items });
721 10         226 $xml->{name} = $first->value();
722 10         36 my @rest = $ast->slice( 1 .. $size - 1 );
723 10         16 foreach my $i (@rest) {
724 13         24 my $iv = $self->_eval($i);
725 13         276 my $it = $iv->type();
726 13 50 100     54 $ast->error(
      66        
      33        
727             "xml expects string or xml or meta or list as items but got "
728             . $it )
729             if $it ne "string"
730             and $it ne "xml"
731             and $it ne "meta"
732             and $it ne "list";
733 13 100       26 if ( $it eq "meta" ) {
    50          
734 2         50 $xml->meta_data($iv);
735             }
736             elsif ( $it eq "list" ) {
737 0         0 foreach my $i ( @{ $iv->value() } ) {
  0         0  
738 0         0 push @items, $i;
739             }
740             }
741             else {
742             ;
743 11         19 push @items, $iv;
744             }
745             }
746 10         24 return $xml;
747             }
748 171         304 return $ast;
749             }
750              
751             =head2 word_is_reserved
752              
753             Is the given word reserved?
754             Usage:
755              
756             if( $this->word_is_reserved('bla') ){
757             ...
758             }
759              
760             =cut
761              
762             sub word_is_reserved{
763 491     491 1 516 my ($self, $word ) = @_;
764 491         11598 return $self->builtins()->has_function( $word );
765             }
766              
767             sub builtin {
768 259     259 0 283 my ($self, $f , $ast) = @_;
769              
770 259         5574 my $fn = $f->value();
771              
772 259 50       5756 if( my $function = $self->builtins()->has_function( $fn ) ){
773 259         5691 return $self->builtins()->call_function( $function , $ast , $f );
774             }
775              
776 0         0 confess "Builtin function '$fn' is not implemented";
777             }
778              
779             sub perlfunc_call {
780 12     12 0 16 my $self = shift;
781 12         12 my $perl_func = shift;
782 12         22 my $meta = shift;
783 12         11 my $rargs = shift;
784 12         11 my $ast = shift;
785              
786 12         17 my $ret_type = "scalar";
787 12         14 my @fargtypes = ();
788 12 100       24 if ( defined $meta ) {
789 6 50       132 if ( exists $meta->value()->{"return"} ) {
790 6         125 my $rt = $meta->value()->{"return"};
791 6 50 33     126 $ast->error(
792             "return expects a string or keyword but got " . $rt->type() )
793             if $rt->type() ne "string"
794             and $rt->type() ne "keyword";
795 6         125 $ret_type = $rt->value();
796             }
797 6 50       124 if ( exists $meta->value()->{"arguments"} ) {
798 0         0 my $ats = $meta->value()->{"arguments"};
799 0 0       0 $ast->error( "arguments expect a vector but got " . $ats->type() )
800             if $ats->type() ne "vector";
801 0         0 foreach my $arg ( @{ $ats->value() } ) {
  0         0  
802 0 0 0     0 $ast->error(
803             "arguments expect a vector of string or keyword but got "
804             . $arg->type() )
805             if $arg->type() ne "string"
806             and $arg->type() ne "keyword";
807 0         0 push @fargtypes, $arg->value();
808             }
809             }
810             }
811 12         17 my @args = ();
812 12         13 my $i = 0;
813 12         11 foreach my $arg ( @{$rargs} ) {
  12         19  
814 14         30 my $pobj = $self->clj2perl( $self->_eval($arg) );
815 14 50       47 if ( $i < scalar @fargtypes ) {
816 0         0 my $ft = $fargtypes[$i];
817 0 0       0 if ( $ft eq "scalar" ) {
    0          
    0          
    0          
818 0         0 push @args, $pobj;
819             }
820             elsif ( $ft eq "array" ) {
821 0         0 push @args, @{$pobj};
  0         0  
822             }
823             elsif ( $ft eq "hash" ) {
824 0         0 push @args, %{$pobj};
  0         0  
825             }
826             elsif ( $ft eq "ref" ) {
827 0         0 push @args, \$pobj;
828             }
829             else {
830 0         0 push @args, $pobj;
831             }
832             }
833             else {
834 14 50       33 if ( ref($pobj) eq "ARRAY" ) {
    50          
835 0         0 push @args, @{$pobj};
  0         0  
836             }
837             elsif ( ref($pobj) eq "HASH" ) {
838 0         0 push @args, %{$pobj};
  0         0  
839             }
840             else {
841 14         23 push @args, $pobj;
842             }
843             }
844 14         16 $i++;
845             }
846              
847 12 100       50 if ( $ret_type eq "scalar" ) {
    50          
    50          
    50          
    50          
    50          
    50          
    50          
848 11         57 my $r = $perl_func->(@args);
849 11         44 return &wrap_perlobj($r);
850             }
851             elsif ( $ret_type eq "ref-scalar" ) {
852 0         0 my $r = $perl_func->(@args);
853 0         0 return &wrap_perlobj( \$r );
854             }
855             elsif ( $ret_type eq "array" ) {
856 0         0 my @r = $perl_func->(@args);
857 0         0 return &wrap_perlobj(@r);
858             }
859             elsif ( $ret_type eq "ref-array" ) {
860 0         0 my @r = $perl_func->(@args);
861 0         0 return &wrap_perlobj( \@r );
862             }
863             elsif ( $ret_type eq "hash" ) {
864 0         0 my %r = $perl_func->(@args);
865 0         0 return &wrap_perlobj(%r);
866             }
867             elsif ( $ret_type eq "ref-hash" ) {
868 0         0 my %r = $perl_func->(@args);
869 0         0 return &wrap_perlobj( \%r );
870             }
871             elsif ( $ret_type eq "nil" ) {
872 0         0 $perl_func->(@args);
873 0         0 return $nil;
874             }
875             elsif ( $ret_type eq 'raw' ) {
876              
877             # The perl function is expected to return a raw Language::LispPerl::Atom
878 1         3 return $perl_func->(@args);
879             }
880             else {
881 0         0 my $r = \$perl_func->(@args);
882 0         0 return &wrap_perlobj($r);
883             }
884              
885             }
886              
887             sub clj2perl {
888 16     16 0 18 my $self = shift;
889 16         14 my $ast = shift;
890 16         337 my $type = $ast->type();
891 16         333 my $value = $ast->value();
892 16 100 66     149 if ( $type eq "string"
    50 66        
    100 33        
    50 66        
    50 33        
    50          
893             or $type eq "number"
894             or $type eq "quotation"
895             or $type eq "keyword"
896             or $type eq "perlobject" )
897             {
898 13         46 return $value;
899             }
900             elsif ( $type eq "bool" ) {
901 0 0       0 if ( $value eq "true" ) {
902 0         0 return 1;
903             }
904             else {
905 0         0 return 0;
906             }
907             }
908             elsif ( $type eq "nil" ) {
909 1         28 return undef;
910             }
911             elsif ( $type eq "list" or $type eq "vector" ) {
912 0         0 my @r = ();
913 0         0 foreach my $i ( @{$value} ) {
  0         0  
914 0         0 push @r, $self->clj2perl($i);
915             }
916 0         0 return \@r;
917             }
918             elsif ( $type eq "map" ) {
919 0         0 my %r = ();
920 0         0 foreach my $k ( keys %{$value} ) {
  0         0  
921 0         0 $r{$k} = $self->clj2perl( $value->{$k} );
922             }
923 0         0 return \%r;
924             }
925             elsif ( $type eq "function" ) {
926             my $f = sub {
927 2     2   5 my @args = @_;
928 2         70 my $cljf = Language::LispPerl::Seq->new({ type => "list" });
929 2         8 $cljf->append($ast);
930 2         4 foreach my $arg (@args) {
931 2         6 $cljf->append( $self->perl2clj($arg) );
932             }
933 2         5 return $self->clj2perl( $self->_eval($cljf) );
934 2         9 };
935 2         5 return $f;
936             }
937             else {
938 0         0 $ast->error(
939             "unsupported type '" . $type . "' for clj2perl object conversion" );
940             }
941             }
942              
943             sub wrap_perlobj {
944 11     11 0 14 my $v = shift;
945 11         28 while ( ref($v) eq "REF" ) {
946 0         0 $v = ${$v};
  0         0  
947             }
948 11         300 return Language::LispPerl::Atom->new({ type => "perlobject", value => $v });
949             }
950              
951             =head2 perl2clj
952              
953             Turn a native perl Object into a new L<Language::LispPerl::Atom>
954              
955             Usage:
956              
957             my $new_atom = $evaler->perl2clj( .. perl object .. );
958              
959             =cut
960              
961             sub perl2clj {
962 3     3 1 4 my ($self, $v) = @_;
963 3 100 66     28 if ( !defined ref($v) or ref($v) eq "" ) {
    50          
    50          
    50          
    50          
964 1         25 return Language::LispPerl::Atom->new({ type => "string", value => $v });
965             }
966             elsif ( ref($v) eq "SCALAR" ) {
967 0         0 return Language::LispPerl::Atom->new({ type => "string", value => ${$v} });
  0         0  
968             }
969             elsif ( ref($v) eq "HASH" ) {
970 0         0 my %m = ();
971 0         0 foreach my $k ( keys %{$v} ) {
  0         0  
972 0         0 $m{$k} = $self->perl2clj( $v->{$k} );
973             }
974 0         0 return Language::LispPerl::Atom->new({ type => "map", value => \%m });
975             }
976             elsif ( ref($v) eq "ARRAY" ) {
977 0         0 my @a = ();
978 0         0 foreach my $i ( @{$v} ) {
  0         0  
979 0         0 push @a, $self->perl2clj($i);
980             }
981 0         0 return Language::LispPerl::Atom->new({ type => "vector", value => \@a });
982             }
983             elsif ( ref($v) eq "CODE" ) {
984 0         0 return Language::LispPerl::Atom->new({ type => "perlfunction", value => $v });
985             }
986             else {
987 2         56 return Language::LispPerl::Atom->new({ type => "perlobject", value => $v });
988              
989             #$ast->error("expect a reference of scalar or hash or array");
990             }
991             }
992              
993             sub trace_vars {
994 1     1 0 1 my $self = shift;
995 1         2 print @{ $self->scopes() } . "\n";
  1         26  
996 1         2 foreach my $vn ( keys %{ $self->current_scope() } ) {
  1         3  
997 17         24 print
998             "$vn\n" # . Language::LispPerl::Printer::to_string(${$self->current_scope()}{$vn}->value()) . "\n";
999             }
1000             }
1001              
1002             __PACKAGE__->meta->make_immutable();
1003             1;