File Coverage

blib/lib/Language/LispPerl/Evaler.pm
Criterion Covered Total %
statement 361 502 71.9
branch 129 236 54.6
condition 63 114 55.2
subroutine 47 49 95.9
pod 13 34 38.2
total 613 935 65.5


line stmt bran cond sub pod time code
1             package Language::LispPerl::Evaler;
2             $Language::LispPerl::Evaler::VERSION = '0.007';
3 6     6   2657 use Moose;
  6         1919501  
  6         35  
4              
5 6     6   32753 use File::ShareDir;
  6         29120  
  6         323  
6 6     6   37 use File::Spec;
  6         10  
  6         105  
7 6     6   25 use File::Basename;
  6         7  
  6         390  
8              
9 6     6   2669 use Language::LispPerl::Reader;
  6         33  
  6         190  
10 6     6   2858 use Language::LispPerl::Var;
  6         16  
  6         190  
11 6     6   36 use Language::LispPerl::Printer;
  6         7  
  6         85  
12 6     6   3052 use Language::LispPerl::BuiltIns;
  6         177  
  6         235  
13              
14 6     6   34 use Log::Any qw/$log/;
  6         6  
  6         45  
15              
16             BEGIN{
17             # The test compatible File::Share
18 6     6   1261 eval{ require File::Share; File::Share->import('dist_dir'); };
  6         2632  
  6         2982  
19 6 50       23595 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   12 my ($self) = @_;
47 8         323 return Language::LispPerl::BuiltIns->new({ evaler => $self });
48             }
49              
50             sub to_hash{
51 4     4 0 23 my ($self) = @_;
52             return {
53 4         110 '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 1573 my ($class, $hash) = @_;
66             return $class->new({
67 2         9 map { $_ => Language::LispPerl::Reader::from_perl( $hash->{$_} ) } keys %$hash
  16         33  
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 9 my ($self) = @_;
88 3         5 $self->{exception} = undef;
89             }
90              
91             sub push_scope {
92 57     57 0 58 my $self = shift;
93 57   33     109 my $context = shift() || confess("Cannot push untrue context");
94 57         48 my %c = %{$context};
  57         287  
95 57         64 my @ns = @{ $c{$namespace_key} };
  57         93  
96 57         84 $c{$namespace_key} = \@ns;
97 57         53 unshift @{ $self->scopes() }, \%c;
  57         1421  
98             }
99              
100             sub pop_scope {
101 57     57 0 68 my $self = shift;
102 57         51 shift @{ $self->scopes() };
  57         1411  
103             }
104              
105             sub current_scope {
106 1213     1213 0 879 my $self = shift;
107 1213         837 my $scope = @{ $self->scopes() }[0];
  1213         27689  
108 1213         1543 return $scope;
109             }
110              
111             sub push_caller {
112 57     57 0 66 my $self = shift;
113 57         52 my $ast = shift;
114 57         58 unshift @{ $self->caller() }, $ast;
  57         1326  
115             }
116              
117             sub pop_caller {
118 57     57 0 64 my $self = shift;
119 57         45 shift @{ $self->caller() };
  57         1366  
120             }
121              
122             sub caller_size {
123 6     6 0 7 my $self = shift;
124 6         5 scalar @{ $self->caller() };
  6         144  
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 4 my ($self) = @_;
139 4         5 return [ @{ $self->caller() } ];
  4         89  
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 68 my ($self) = @_;
151             # Take a shallow copy of the current_scope
152 66         62 my %c = %{ $self->current_scope() };
  66         112  
153              
154             # Take a shallow copy of the namespace (keyed by namespace_key)
155 66         94 my @ns = @{ $c{$namespace_key} };
  66         138  
156 66         109 $c{$namespace_key} = \@ns;
157              
158 66         615 return \%c;
159             }
160              
161             sub push_namespace {
162 2     2 0 4 my $self = shift;
163 2         3 my $namespace = shift;
164 2         6 my $scope = $self->current_scope();
165 2         5 unshift @{ $scope->{$namespace_key} }, $namespace;
  2         9  
166             }
167              
168             sub pop_namespace {
169 2     2 0 3 my $self = shift;
170 2         5 my $scope = $self->current_scope();
171 2         4 shift @{ $scope->{$namespace_key} };
  2         7  
172             }
173              
174             sub current_namespace {
175 695     695 0 539 my $self = shift;
176 695         736 my $scope = $self->current_scope();
177 695         539 my $namespace = @{ $scope->{$namespace_key} }[0];
  695         787  
178 695 100       3036 return "" if ( !defined $namespace );
179 42         203 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 182     182 1 150 my $self = shift;
195 182         158 my $name = shift;
196 182         157 my $value = shift;
197 182         226 my $scope = $self->current_scope();
198 182         302 $name = $self->current_namespace() . "#" . $name;
199 182         4651 $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 262     262 1 232 my $self = shift;
217 262         265 my $name = shift;
218 262         356 my $scope = $self->current_scope();
219 262 100       620 if ( exists $scope->{$name} ) {
    100          
    50          
220 4         6 return $scope->{$name};
221             }
222             elsif ( exists $scope->{ $self->current_namespace() . "#" . $name } ) {
223 253         329 return $scope->{ $self->current_namespace() . "#" . $name };
224             }
225             elsif ( exists $scope->{ "#" . $name } ) {
226 5         10 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 8 my $self = shift;
264 6         8 my $file = shift;
265              
266 6         23 my $dist_dir = dist_dir( 'Language-LispPerl' );
267 6         1164 $log->debug("Using dist dir = $dist_dir");
268              
269 6         27 foreach my $ext ( '', '.clp' ) {
270 9 100       226 if ( -f "$file$ext" ) {
    100          
271 1         26 return "$file$ext";
272             }
273             elsif ( -f $dist_dir. "/lisp/$file$ext" ) {
274 5         88 return $dist_dir . "/lisp/$file$ext";
275             }
276 3         5 foreach my $p (@INC) {
277 33 50       391 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 16 my $self = shift;
298 6         8 my $file = shift;
299              
300             Language::LispPerl::Logger::error(
301             "cannot require file " . $file . " in non-global scope" )
302 6 50       7 if scalar @{ $self->scopes() } > 1;
  6         158  
303              
304 6         18 $file = File::Spec->rel2abs( $self->search_file($file) );
305              
306 6 100       26 return 1 if exists $self->{loaded_files}->{$file};
307 5         13 $self->{loaded_files}->{$file} = 1;
308 5         6 push @{ $self->{file_stack} }, $file;
  5         12  
309 5         13 my $res = $self->read($file);
310 5         8 pop @{ $self->{file_stack} };
  5         17  
311 5         31 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 8 my $self = shift;
328 6         8 my $file = shift;
329 6         39 my $reader = Language::LispPerl::Reader->new();
330 6         21 $reader->read_file($file);
331 6         12 my $res = undef;
332 6     45   22 $reader->ast()->each( sub { $res = $self->_eval( $_[0] ) } );
  45         92  
333 6         205 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 238 sub true{ return $true; }
342 11     11 0 107 sub false{ return $false; }
343 12     12 0 42 sub nil{ return $nil; }
344 1     1 0 3 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 104     104 1 7322 my ($self, $str) = @_;
362 104 100       620 unless( length( defined( $str ) ? $str : '' ) ){
    100          
363 1         47 return $nil;
364             }
365              
366 103         612 my $reader = Language::LispPerl::Reader->new();
367 103         212 $reader->read_string($str);
368 103         91 my $res = undef;
369 103     113   199 $reader->ast()->each( sub { $res = $self->_eval( $_[0] ) } );
  113         241  
370 101         3150 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 674     674 1 550 my $self = shift;
383 674         480 my $ast = shift;
384 674         14737 my $class = $ast->class();
385 674         14632 my $type = $ast->type();
386 674         14499 my $value = $ast->value();
387 674 100 100     8804 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         30 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         3 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       84 $self->{syntaxquotation_scope} += 1 if $type eq "syntaxquotation";
404 41 50       68 $self->{quotation_scope} += 1 if $type eq "quotation";
405 41         82 my $r = $self->bind($value);
406 41 50       120 $self->{syntaxquotation_scope} -= 1 if $type eq "syntaxquotation";
407 41 50       63 $self->{quotation_scope} -= 1 if $type eq "quotation";
408 41         90 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 460 50 66     907 if ( $type eq "dequotation" and $self->{syntaxquotation_scope} == 0 );
420 460         412 my $name = $value;
421 460 100 100     902 if ( $type eq "dequotation" and $value =~ /^@(\S+)$/ ) {
422 33         48 $name = $1;
423             }
424 460 100       659 return $ast
425             if $self->word_is_reserved( $name );
426 197         375 my $var = $self->var($name);
427 197 50       294 $ast->error("unbound symbol '$name'") if !defined $var;
428 197         4465 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     183 return $empty_list if $type eq "list" and $ast->size() == 0;
438 70         1675 my $list = Language::LispPerl::Seq->new({ type => "list" });
439 70         1598 $list->type($type);
440 70         56 foreach my $i ( @{$value} ) {
  70         110  
441 206 100 100     4726 if ( $i->type() eq "dequotation" and $i->value() =~ /^@/ ) {
442 33         56 my $dl = $self->bind($i);
443 33 50       738 $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         714  
446 49         93 $list->append($di);
447             }
448             }
449             else {
450 173         274 $list->append( $self->bind($i) );
451             }
452             }
453 70         124 return $list;
454             }
455 83         192 return $ast;
456             }
457              
458             sub _eval {
459 939     939   719 my $self = shift;
460 939         589 my $ast = shift;
461 939         22009 my $class = $ast->class();
462 939         20562 my $type = $ast->type();
463 939         20761 my $value = $ast->value();
464 939 100 100     3230 if ( $type eq "list" ) {
    50 100        
    50 66        
    100 66        
    100          
    50          
    100          
    100          
    100          
465 316         564 my $size = $ast->size();
466 316 50       478 if ( $size == 0 ) {
467 0         0 return $empty_list;
468             }
469 316         537 my $f = $self->_eval( $ast->first() );
470 316         7263 my $ftype = $f->type();
471 316         6946 my $fvalue = $f->value();
472 316 100       647 if ( $ftype eq "symbol" ) {
    50          
    50          
    100          
    50          
    50          
473 262         453 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 100       460 my $scope = defined( $f->context() ) ? $f->context() : $self->copy_current_scope();
534              
535 19         23 my $fn = $fvalue;
536 19         47 my $fargs = $fn->second();
537 19         79 my @rargs = $ast->slice( 1 .. $size - 1 );
538 19         31 my @rrargs = ();
539 19         30 foreach my $arg (@rargs) {
540 21         39 push @rrargs, $self->_eval($arg);
541             }
542 19         50 $self->push_scope($scope);
543 19         45 $self->push_caller($fn);
544 19         23 my $rest_args = undef;
545 19         29 my $i = 0;
546 19         469 my $fargsvalue = $fargs->value();
547 19         22 my $fargsn = scalar @{$fargsvalue};
  19         26  
548 19         28 my $rrargsn = scalar @rrargs;
549              
550 19         58 for ( $i = 0 ; $i < $fargsn ; $i++ ) {
551 21         497 my $name = $fargsvalue->[$i]->value();
552 21 50       45 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       39 $ast->error("real arguments < formal arguments")
560             if $i >= $rrargsn;
561 21         52 $self->new_var( $name, $rrargs[$i] );
562             }
563             }
564 19 50       36 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       41 $ast->error("real arguments > formal arguments")
572             if $i < $rrargsn;
573             }
574 19         62 my @body = $fn->slice( 2 .. $fn->size() - 1 );
575 19         24 my $res;
576 19         31 foreach my $b (@body) {
577 19         58 $res = $self->_eval($b);
578             }
579 19         53 $self->pop_scope();
580 19         410 $self->pop_caller();
581 19         244 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 100       808 my $scope = defined( $f->context() ) ? $f->context() : $self->copy_current_scope();
594 35         43 my $fn = $fvalue;
595 35         85 my $fargs = $fn->third();
596 35         87 my @rargs = $ast->slice( 1 .. $ast->size() - 1 );
597 35         76 $self->push_scope($scope);
598 35         95 $self->push_caller($fn);
599 35         36 my $rest_args = undef;
600 35         37 my $i = 0;
601 35         786 my $fargsvalue = $fargs->value();
602 35         35 my $fargsn = scalar @{$fargsvalue};
  35         39  
603 35         37 my $rargsn = scalar @rargs;
604              
605 35         121 for ( $i = 0 ; $i < $fargsn ; $i++ ) {
606 98         2404 my $name = $fargsvalue->[$i]->value();
607 98 100       160 if ( $name eq "&" ) {
608 33         32 $i++;
609 33         779 $name = $fargsvalue->[$i]->value();
610 33         785 $rest_args = Language::LispPerl::Seq->new({ type => "list" });
611 33         84 $self->new_var( $name, $rest_args );
612             }
613             else {
614 65 50       111 $ast->error("real arguments < formal arguments")
615             if $i >= $rargsn;
616 65         124 $self->new_var( $name, $rargs[$i] );
617             }
618             }
619 35 100       61 if ( defined $rest_args ) {
620 33         35 $i -= 2;
621 33         66 for ( ; $i < $rargsn ; $i++ ) {
622 49         105 $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         80 my @body = $fn->slice( 3 .. $fn->size() - 1 );
630 35         39 my $res;
631 35         46 foreach my $b (@body) {
632 35         92 $res = $self->_eval($b);
633             }
634 35         72 $self->pop_scope();
635 35         843 $self->pop_caller();
636 35         63 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 382         682 return $self->bind($ast);
668             }
669             elsif ( $type eq "syntaxquotation" ) {
670 41         71 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         122 my $v = Language::LispPerl::Atom->new({ type => "vector" });
677 5         9 my @vv = ();
678 5         5 foreach my $i ( @{$value} ) {
  5         9  
679 12         18 push @vv, $self->_eval($i);
680             }
681 5         136 $v->value( \@vv );
682 5         14 return $v;
683             }
684             elsif ( $class eq "Seq" and ( $type eq "map" or $type eq "meta" ) ) {
685 13         386 my $m = Language::LispPerl::Atom->new({ type => "map" });
686 13         29 my %mv = ();
687 13         15 my $n = scalar @{$value};
  13         19  
688 13 50       41 $ast->error( $type . " should have even number of items" )
689             if ( $n % 2 ) != 0;
690 13         37 for ( my $i = 0 ; $i < $n ; $i += 2 ) {
691 14         49 my $k = $self->_eval( $value->[$i] );
692 14 50 33     331 $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         37 my $v = $self->_eval( $value->[ $i + 1 ] );
698 14         311 $mv{ $k->value() } = $v;
699             }
700 13         289 $m->value( \%mv );
701 13 100       232 $m->type("meta") if $type eq "meta";
702 13         31 return $m;
703             }
704             elsif ( $class eq "Seq" and $type eq "xml" ) {
705 10         19 my $size = $ast->size();
706 10 50       14 $ast->error("xml expects >= 1 arguments") if $size == 0;
707 10         19 my $first = $ast->first();
708 10         224 my $firsttype = $first->type();
709 10 50       21 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     16 "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         245 my $xml = Language::LispPerl::Atom->new({ type => "xml", value => \@items });
721 10         226 $xml->{name} = $first->value();
722 10         40 my @rest = $ast->slice( 1 .. $size - 1 );
723 10         14 foreach my $i (@rest) {
724 13         26 my $iv = $self->_eval($i);
725 13         277 my $it = $iv->type();
726 13 50 100     46 $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       24 if ( $it eq "meta" ) {
    50          
734 2         48 $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         14 push @items, $iv;
744             }
745             }
746 10         25 return $xml;
747             }
748 172         314 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 497     497 1 485 my ($self, $word ) = @_;
764 497         11933 return $self->builtins()->has_function( $word );
765             }
766              
767             sub builtin {
768 262     262 0 261 my ($self, $f , $ast) = @_;
769              
770 262         5869 my $fn = $f->value();
771              
772 262 50       5891 if( my $function = $self->builtins()->has_function( $fn ) ){
773 262         5949 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 15 my $self = shift;
781 12         13 my $perl_func = shift;
782 12         13 my $meta = shift;
783 12         8 my $rargs = shift;
784 12         14 my $ast = shift;
785              
786 12         16 my $ret_type = "scalar";
787 12         17 my @fargtypes = ();
788 12 100       25 if ( defined $meta ) {
789 6 50       155 if ( exists $meta->value()->{"return"} ) {
790 6         137 my $rt = $meta->value()->{"return"};
791 6 50 33     133 $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         147 $ret_type = $rt->value();
796             }
797 6 50       152 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         18 my @args = ();
812 12         12 my $i = 0;
813 12         17 foreach my $arg ( @{$rargs} ) {
  12         17  
814 14         27 my $pobj = $self->clj2perl( $self->_eval($arg) );
815 14 50       48 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       46 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         22 push @args, $pobj;
842             }
843             }
844 14         17 $i++;
845             }
846              
847 12 100       36 if ( $ret_type eq "scalar" ) {
    50          
    50          
    50          
    50          
    50          
    50          
    50          
848 11         63 my $r = $perl_func->(@args);
849 11         49 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         5 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         11 my $ast = shift;
890 16         370 my $type = $ast->type();
891 16         354 my $value = $ast->value();
892 16 100 66     139 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         65 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         25 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   4 my @args = @_;
928 2         63 my $cljf = Language::LispPerl::Seq->new({ type => "list" });
929 2         6 $cljf->append($ast);
930 2         3 foreach my $arg (@args) {
931 2         7 $cljf->append( $self->perl2clj($arg) );
932             }
933 2         4 return $self->clj2perl( $self->_eval($cljf) );
934 2         8 };
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 15 my $v = shift;
945 11         32 while ( ref($v) eq "REF" ) {
946 0         0 $v = ${$v};
  0         0  
947             }
948 11         320 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 16 my ($self, $v) = @_;
963 3 100 66     27 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         59 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 2 my $self = shift;
995 1         2 print @{ $self->scopes() } . "\n";
  1         24  
996 1         2 foreach my $vn ( keys %{ $self->current_scope() } ) {
  1         3  
997 17         26 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;