File Coverage

blib/lib/Language/LispPerl/BuiltIns.pm
Criterion Covered Total %
statement 463 487 95.0
branch 167 282 59.2
condition 55 89 61.8
subroutine 56 57 98.2
pod 3 3 100.0
total 744 918 81.0


line stmt bran cond sub pod time code
1             package Language::LispPerl::BuiltIns;
2             $Language::LispPerl::BuiltIns::VERSION = '0.007';
3 6     6   22 use Moose;
  6         8  
  6         32  
4              
5 6     6   25484 use Carp;
  6         11  
  6         358  
6 6     6   25 use Class::Load;
  6         7  
  6         170  
7              
8 6     6   27 use Language::LispPerl::Atom;
  6         5  
  6         97  
9 6     6   18 use Language::LispPerl::Printer;
  6         7  
  6         100  
10              
11 6     6   2303 use Language::LispPerl::CoreBindings;
  6         12  
  6         150  
12              
13              
14 6     6   27 use Moose::Util;
  6         8  
  6         39  
15             #use Role::Tiny qw//;
16              
17             =head1 NAME
18              
19             Language::LispPerl::BuiltIns - Default builtin functions collection
20              
21             =cut
22              
23             has 'evaler' => ( is => 'ro', required => 1, weak_ref => 1);
24              
25             has 'functions' => (
26             is => 'ro',
27             default => sub {
28             {
29             # Completeness
30             "eval" => \&_impl_eval,
31             "syntax" => \&_impl_syntax,
32             "quote" => \&_impl_quote,
33              
34             # Lisp file inclusion operation
35             "require" => \&_impl_require,
36             "read" => \&_impl_read,
37              
38             # Exception stuff
39             "throw" => \&_impl_throw,
40             "catch" => \&_impl_catch,
41             "exception-label" => \&_impl_exception_label,
42             "exception-message" => \&_impl_exception_message,
43              
44             # Variable stuff (booo)
45             "def" => \&_impl_def,
46             "set!" => \&_impl_set_bang,
47             "let" => \&_impl_let,
48              
49             # Lambda function
50             "fn" => \&_impl_fn,
51             "apply" => \&_impl_apply,
52              
53             # Define macros
54             "defmacro" => \&_impl_defmacro,
55             "gen-sym" => \&_impl_gen_sym,
56              
57             # List stuff
58             "list" => \&_impl_list,
59             "car" => \&_impl_car,
60             "cdr" => \&_impl_cdr,
61             "cons" => \&_impl_cons,
62              
63             # Flow control
64             "if" => \&_impl_if,
65             "while" => \&_impl_while,
66             "begin" => \&_impl_begin,
67              
68             # General purpose "collection" functions
69             "length" => \&_impl_length,
70             "reverse" => \&_impl_reverse,
71             "append" => \&_impl_append,
72              
73             # General purpose equal function
74             "equal" => \&_impl_equal,
75              
76             # xml utilities
77             "xml-name" => \&_impl_xml_name,
78              
79             # Hashmaps utilities
80             "keys" => \&_impl_keys,
81              
82             # Object Introspection
83             "object-id" => \&_impl_object_id,
84             "type" => \&_impl_type,
85             "meta" => \&_impl_meta,
86             "clj->string" => \&_impl_clj_string,
87              
88             "perlobj-type" => \&_impl_perlobj_type,
89             "perl->clj" => \&_impl_perl_clj,
90              
91             # Numeric binary functions
92             "+" => \&_impl_num_bin,
93             "-" => \&_impl_num_bin,
94             "*" => \&_impl_num_bin,
95             "/" => \&_impl_num_bin,
96             "%" => \&_impl_num_bin,
97             # Boolean binary functions
98             "==" => \&_impl_num_bool,
99             "!=" => \&_impl_num_bool,
100             ">" => \&_impl_num_bool,
101             ">=" => \&_impl_num_bool,
102             "<" => \&_impl_num_bool,
103             "<=" => \&_impl_num_bool,
104              
105             # "." => 1,
106             # "->" => 1,
107              
108             "eq" => \&_impl_str_bool,
109             "ne" => \&_impl_str_bool,
110             "lt" => \&_impl_str_bool,
111             "gt" => \&_impl_str_bool,
112              
113             # Logic stuff.
114             "!" => \&_impl_not,
115             "not" => \&_impl_not,
116             "and" => \&_impl_and,
117             "or" => \&_impl_or,
118              
119             # Package building
120             "namespace-begin" => \&_impl_namespace_begin,
121             "namespace-end" => \&_impl_namespace_end,
122              
123             #IO
124             "println" => \&_impl_println,
125              
126             "trace-vars" => \&_impl_trace_vars
127             };
128             }
129             );
130              
131             =head2 apply_role
132              
133             Apply the given builtin Role to this object.
134              
135             Usage (install Coroutine builtin functions):
136              
137             $this->apply_role('Language::LispPerl::Role::BuiltIns::Coro');
138              
139             =cut
140              
141             sub apply_role{
142 1     1 1 2 my ($self, $role) = @_;
143 1         5 Moose::Util::ensure_all_roles( $self, $role );
144             }
145              
146             =head2 has_function
147              
148             Returns true if this BuiltIns container has got the given function.
149              
150             Usage:
151              
152             if( my $f = $this->has_function( 'eval ' ) ){
153             $this->call_function( $f , $ast );
154             }
155              
156             =cut
157              
158             sub has_function{
159 760     760 1 707 my ($self, $function_name ) = @_;
160 760         17830 my $straight_func = $self->functions()->{$function_name};
161 760 100       2161 return $straight_func if $straight_func;
162              
163 263 100       624 if( $function_name =~ /^(\.|->)(\S*)$/ ){
164 30         100 my $opts = { blessed => $1,
165             namespace => $2 };
166             return sub{
167 15     15   18 my ($self, $ast, $symbol) = @_;
168 15         51 return $self->_impl_perlcall( $ast, $symbol, $opts );
169 30         233 };
170             }
171 233         555 return;
172             }
173              
174              
175             =head2 call_function
176              
177             Just calls the given CODEREF with the given args.
178              
179             Usage:
180              
181             $this->call_function( $self->has_function('eval') , $ast );
182              
183             =cut
184              
185             sub call_function{
186 262     262 1 368 my ($self , $code , @args ) = @_;
187 262         519 return $code->( $self, @args);
188             }
189              
190             sub _impl_perlcall{
191 15     15   23 my ($self, $ast, $symbol, $opts) = @_;
192 15         26 my $blessed = $opts->{blessed};
193 15   100     54 my $ns = $opts->{namespace} || 'Language::LispPerl::CoreBindings';
194              
195 15         39 my $size = $ast->size();
196              
197 15 50       36 $ast->error(". expects > 1 arguments") if $size < 2;
198 15 50 66     35 $ast->error(
      66        
199             ". expects a symbol or keyword or stirng as the first argument but got "
200             . $ast->second()->type() )
201             if ( $ast->second()->type() ne "symbol"
202             and $ast->second()->type() ne "keyword"
203             and $ast->second()->type() ne "string" );
204              
205 15         35 my $perl_func = $ast->second()->value();
206 15 100       35 if ( $perl_func eq "require" ) {
207 3 50       11 $ast->error(". require expects 1 argument") if $size != 3;
208 3         9 my $m = $ast->third();
209 3 50 33     111 if ( $m->type() eq "keyword" or $m->type() eq "symbol" ) {
    50          
210             }
211             elsif ( $m->type() eq "string" ) {
212 3         74 $m = $self->evaler()->_eval( $ast->third() );
213             }
214             else {
215 0         0 $ast->error(
216             ". require expects a string but got " . $m->type() );
217             }
218              
219 3         69 my $mn = $m->value();
220              
221 3         5 eval { Class::Load::load_class( $mn ); };
  3         18  
222 3 50       155 if( my $err = $@ ){
223 0         0 $ast->error("Cannot load perl package $mn: $err");
224             }
225 3         81 return $self->evaler()->true();
226             }
227             else {
228 12         17 my $meta = undef;
229 12 100 100     30 $meta = $self->evaler()->_eval( $ast->third() )
230             if defined $ast->third()
231             and $ast->third()->type() eq "meta";
232 12         16 $perl_func = \&{ $ns . "::" . $perl_func };
  12         52  
233 12 100       69 my @rest = $ast->slice( ( defined $meta ? 3 : 2 ) .. $size - 1 );
234 12 100       60 unshift @rest, Language::LispPerl::Atom->new({ type => "string", value => $ns })
235             if $blessed eq "->";
236              
237 12         332 return $self->evaler()->perlfunc_call( $perl_func, $meta, \@rest, $ast );
238             }
239             }
240              
241             sub _impl_eval{
242 2     2   3 my ($self , $ast ) = @_;
243 2 100       5 $ast->error("eval expects 1 argument") if $ast->size != 2;
244 1         3 my $s = $ast->second();
245 1 50       22 $ast->error( "eval expects 1 string as argument but got " . $s->type() )
246             if $s->type() ne "string";
247              
248 1         40 return $self->evaler()->eval( $s->value() );
249             }
250              
251             sub _impl_syntax{
252 4     4   7 my ($self, $ast) = @_;
253 4 50       15 $ast->error("syntax expects 1 argument") if $ast->size != 2;
254 4         93 return $self->evaler()->bind( $ast->second() );
255             }
256              
257             sub _impl_quote{
258 4     4   6 my ($self, $ast) = @_;
259 4 50       11 $ast->error("quote expects 1 argument") if $ast->size != 2;
260 4         8 return $ast->second();
261             }
262              
263             # ( throw someexception "The message that goes with it")
264             sub _impl_throw {
265 4     4   3 my ( $self, $ast ) = @_;
266 4 50       9 $ast->error("throw expects 2 arguments") if $ast->size != 3;
267 4         7 my $label = $ast->second();
268 4 50       89 $ast->error( "throw expects a symbol as the first argument but got "
269             . $label->type() )
270             if $label->type() ne "symbol";
271 4         91 my $msg = $self->evaler()->_eval( $ast->third() );
272 4 50       86 $ast->error( "throw expects a string as the second argument but got "
273             . $msg->type() )
274             if $msg->type() ne "string";
275              
276 4         98 my $e = Language::LispPerl::Atom->new({ type => "exception", value => $msg->value() });
277 4         92 $e->{label} = $label->value();
278 4         91 $e->{caller} = $self->evaler()->copy_caller();
279 4         5 $e->{pos} = $ast->{pos};
280              
281 4         91 $self->evaler()->exception($e);
282 4         101 die $msg->value()."\n";
283             }
284              
285              
286             # ( catch ... ... )
287             sub _impl_catch{
288 3     3   3 my ($self, $ast) = @_;
289 3 50       7 $ast->error("catch expects 2 arguments") if $ast->size != 3;
290 3         76 my $handler = $self->evaler()->_eval( $ast->third() );
291 3 50       66 $ast->error(
292             "catch expects a function/lambda as the second argument but got "
293             . $handler->type() )
294             if $handler->type() ne "function";
295              
296 3         3 my $res;
297 3         68 my $saved_caller_depth = $self->evaler()->caller_size();
298 3         4 eval { $res = $self->evaler()->_eval( $ast->second() ); };
  3         70  
299 3 50       8 if ($@) {
300 3         69 my $e = $self->evaler()->exception();
301 3 50       6 if ( !defined $e ) {
302 0         0 $e = Language::LispPerl::Atom->new({ type => "exception", value => "unkown expection" } );
303 0         0 $e->{label} = "undef";
304 0         0 my @ec = ();
305 0         0 $e->{caller} = \@ec;
306             }
307             $ast->error(
308 3 50       96 "catch expects an exception for handler but got " . $e->type() )
309             if $e->type() ne "exception";
310 3         91 my $i = $self->evaler()->caller_size();
311 3         6 for ( ; $i > $saved_caller_depth ; $i-- ) {
312 0         0 $self->evaler()->pop_caller();
313             }
314 3         89 my $call_handler = Language::LispPerl::Seq->new({ type => "list" });
315 3         8 $call_handler->append($handler);
316 3         6 $call_handler->append($e);
317 3         71 $self->evaler()->clear_exception();
318 3         67 return $self->evaler()->_eval($call_handler);
319             }
320 0         0 return $res;
321             }
322              
323             sub _impl_exception_label{
324 1     1   3 my ($self, $ast) = @_;
325 1 50       2 $ast->error("exception-label expects 1 argument") if $ast->size() != 2;
326 1         25 my $e = $self->evaler()->_eval( $ast->second() );
327 1 50       35 $ast->error( "exception-label expects an exception as argument but got "
328             . $e->type() )
329             if $e->type() ne "exception";
330 1         47 return Language::LispPerl::Atom->new({ type => "string", value => $e->{label} });
331             }
332              
333             sub _impl_exception_message{
334 1     1   2 my ($self, $ast) = @_;
335 1 50       3 $ast->error("exception-message expects 1 argument") if $ast->size() != 2;
336 1         25 my $e = $self->evaler()->_eval( $ast->second() );
337 1 50       24 $ast->error(
338             "exception-message expects an exception as argument but got "
339             . $e->type() )
340             if $e->type() ne "exception";
341 1         23 return Language::LispPerl::Atom->new({ type => "string", value => $e->value() } );
342             }
343              
344             sub _impl_def{
345 36     36   40 my ($self, $ast , $symbol ) = @_;
346 36         80 my $size = $ast->size();
347              
348             # Function name
349 36         804 my $function_name = $symbol->value();
350              
351 36 50 33     183 $ast->error( $function_name . " expects 2 arguments" ) if $size > 4 or $size < 3;
352              
353 36 100       72 if ( $size == 3 ) {
354 35 50       78 $ast->error( $function_name
355             . " expects a symbol as the first argument but got "
356             . $ast->second()->type() )
357             if $ast->second()->type() ne "symbol";
358 35         78 my $name = $ast->second()->value();
359 35 50       796 $ast->error( $name . " is a reserved word" ) if $self->evaler()->word_is_reserved( $name );
360              
361             # A function is stored in a variable.
362 35         814 $self->evaler()->new_var($name);
363 35         976 my $value = $self->evaler()->_eval( $ast->third() );
364 35         823 $self->evaler()->var($name)->value($value);
365              
366 35         861 return $value;
367             }
368              
369             # This is a size 4
370 1         25 my $meta = $self->evaler()->_eval( $ast->second() );
371 1 50       23 $ast->error( $function_name
372             . " expects a meta as the first argument but got "
373             . $meta->type() )
374             if $meta->type() ne "meta";
375              
376 1 50       3 $ast->error( $function_name
377             . " expects a symbol as the first argument but got "
378             . $ast->third()->type() )
379             if $ast->third()->type() ne "symbol";
380              
381 1         3 my $name = $ast->third()->value();
382 1 50       24 $ast->error( $name . " is a reserved word" ) if $self->evaler()->word_is_reserved( $name );
383              
384 1         23 $self->evaler()->new_var($name);
385 1         26 my $value = $self->evaler()->_eval( $ast->fourth() );
386 1         25 $value->meta_data($meta);
387 1         23 $self->evaler()->var($name)->value($value);
388 1         6 return $value;
389             }
390              
391             sub _impl_set_bang{
392 8     8   8 my ($self, $ast, $symbol) = @_;
393              
394 8         188 my $function_name = $symbol->value();
395              
396 8 50       19 $ast->error( $function_name . " expects 2 arguments" ) if $ast->size() != 3;
397 8 50       17 $ast->error( $function_name
398             . " expects a symbol as the first argument but got "
399             . $ast->second()->type() )
400             if $ast->second()->type() ne "symbol";
401              
402 8         18 my $name = $ast->second()->value();
403 8 50       181 $ast->error( "undefined variable " . $name )
404             if !defined $self->evaler()->var($name);
405 8         188 my $value = $self->evaler->_eval( $ast->third() );
406              
407 8         207 $self->evaler()->var($name)->value($value);
408 8         121 return $value;
409             }
410              
411             sub _impl_let{
412 3     3   6 my ($self, $ast, $symbol) = @_;
413              
414 3         74 my $function_name = $symbol->value();
415              
416 3 50       10 $ast->error( $function_name . " expects >=3 arguments" ) if $ast->size < 3;
417              
418 3         10 my $vars = $ast->second();
419 3 50       77 $ast->error(
420             $function_name . " expects a list [name value ...] as the first argument" )
421             if $vars->type() ne "vector";
422 3         9 my $varssize = $vars->size();
423 3 50       15 $ast->error(
424             $function_name . " expects [name value ...] pairs. There is a non-even amount of things here." )
425             if $varssize % 2 != 0;
426              
427 3         69 my $varvs = $vars->value();
428              
429             # In a new scope, define the variables and eval the rest of the expressions.
430             # Return the latest evaluated value.
431 3         75 $self->evaler()->push_scope( $self->evaler()->current_scope() );
432 3         72 $self->evaler()->push_caller($ast);
433              
434 3         11 for ( my $i = 0 ; $i < $varssize ; $i += 2 ) {
435 4         8 my $n = $varvs->[$i];
436 4         8 my $v = $varvs->[ $i + 1 ];
437 4 50       124 $ast->error(
438             $function_name . " expects a symbol as name but got " . $n->type() )
439             if $n->type() ne "symbol";
440 4         94 $self->evaler()->new_var( $n->value(), $self->evaler()->_eval($v) );
441             }
442              
443 3         13 my @body = $ast->slice( 2 .. $ast->size - 1 );
444 3         117 my $res = $self->evaler()->nil();
445 3         7 foreach my $b (@body) {
446 4         96 $res = $self->evaler()->_eval($b);
447             }
448 3         100 $self->evaler()->pop_scope();
449 3         134 $self->evaler()->pop_caller();
450              
451 3         23 return $res;
452             }
453              
454             sub _impl_fn{
455 39     39   46 my ($self, $ast, $symbol) = @_;
456 39 50       89 $ast->error("fn expects >= 3 arguments") if $ast->size < 3;
457              
458 39         75 my $args = $ast->second();
459 39         885 my $argstype = $args->type();
460 39 50       100 $ast->error("fn expects [arg ...] as formal argument list")
461             if $argstype ne "vector";
462              
463 39         857 my $argsvalue = $args->value();
464 39         74 my $argssize = $args->size();
465 39         44 my $i = 0;
466 39         37 foreach my $arg ( @{$argsvalue} ) {
  39         61  
467 56 50       1254 $arg->error(
468             "formal argument should be a symbol but got " . $arg->type() )
469             if $arg->type() ne "symbol";
470 56 50 33     1249 if (
      66        
471             $arg->value() eq "&"
472             and ( $argssize != $i + 2
473             or $argsvalue->[ $i + 1 ]->value() eq "&" )
474             )
475             {
476 0         0 $arg->error("only 1 non-& should follow &");
477             }
478 56         67 $i++;
479             }
480              
481 39         975 my $nast = Language::LispPerl::Atom->new({ type => "function", value => $ast });
482 39         989 my $current_scope = $self->evaler()->copy_current_scope();
483 39         960 $nast->context( $current_scope );
484              
485 39         135 return $nast;
486             }
487              
488             sub _impl_apply{
489 2     2   3 my ($self, $ast ) = @_;
490 2 50       8 $ast->error("apply expects 2 arguments") if $ast->size != 3;
491 2         48 my $f = $self->evaler()->_eval( $ast->second() );
492 2 50 33     46 $ast->error( "apply expects function as the first argument but got "
      66        
493             . $f->type() )
494             if (
495             $f->type() ne "function"
496             and !(
497             $f->type() eq "symbol"
498             and $self->has_function( $f->value() )
499             )
500             );
501              
502 2         46 my $l = $self->evaler()->_eval( $ast->third() );
503 2 50       46 $ast->error(
504             "apply expects list as the first argument but got " . $l->type() )
505             if $l->type() ne "list";
506              
507             # Build a ( <function> ( args list ) ) list.
508 2         47 my $n = Language::LispPerl::Seq->new({ type => "list" });
509 2         6 $n->append($f);
510 2         2 foreach my $i ( @{ $l->value() } ) {
  2         43  
511 4         9 $n->append($i);
512             }
513             # And eval it.
514 2         52 return $self->evaler()->_eval($n);
515             }
516              
517             sub _impl_defmacro{
518 22     22   25 my ($self, $ast, $symbol) = @_;
519 22 50       52 $ast->error("defmacro expects >= 4 arguments") if $ast->size < 4;
520 22         46 my $name = $ast->second()->value();
521 22         51 my $args = $ast->third();
522 22 50       479 $ast->error("defmacro expect [arg ...] as formal argument list")
523             if $args->type() ne "vector";
524 22         26 my $i = 0;
525 22         21 foreach my $arg ( @{ $args->value() } ) {
  22         481  
526 62 50       1312 $arg->error(
527             "formal argument should be a symbol but got " . $arg->type() )
528             if $arg->type() ne "symbol";
529 62 50 33     1307 if (
      66        
530             $arg->value() eq "&"
531             and ( $args->size() != $i + 2
532             or $args->value()->[ $i + 1 ]->value() eq "&" )
533             )
534             {
535 0         0 $arg->error("only 1 non-& should follow &");
536             }
537 62         76 $i++;
538             }
539 22         523 my $nast = Language::LispPerl::Atom->new({ type => "macro", value => $ast });
540              
541 22         546 $nast->context( $self->evaler()->copy_current_scope() );
542              
543 22         513 $self->evaler()->new_var( $name, $nast );
544 22         116 return $nast;
545             }
546              
547             sub _impl_gen_sym{
548 1     1   2 my ($self, $ast, $symbol) = @_;
549 1 50       3 $ast->error("gen-sym expects 0/1 argument") if $ast->size > 2;
550 1         26 my $s = Language::LispPerl::Atom->new({ type => "symbol" });
551 1 50       3 if ( $ast->size() == 2 ) {
552 1         25 my $pre = $self->evaler()->_eval( $ast->second() );
553 1 50       23 $ast->("gen-sym expects string as argument")
554             if $pre->type ne "string";
555 1         23 $s->value( $pre->value() . $s->object_id() );
556             }
557             else {
558 0         0 $s->value( $s->object_id() );
559             }
560 1         6 return $s;
561             }
562              
563             sub _impl_require{
564 3     3   7 my ($self, $ast) = @_;
565 3 50       11 unless( $ast ){
566 0         0 confess("NO AST");
567             }
568 3 50       11 $ast->error("require expects 1 argument") if $ast->size() != 2;
569 3         9 my $m = $ast->second();
570 3 100 66     74 unless ( $m->type() eq "symbol" or $m->type() eq "keyword" ) {
571 2         50 $m = $self->evaler->_eval($m);
572 2 50       48 $ast->error( "require expects a string but got " . $m->type() )
573             if $m->type() ne "string";
574             }
575 3         73 return $self->evaler()->load( $m->value() );
576             }
577              
578             sub _impl_read{
579 1     1   2 my ($self, $ast) = @_;
580 1 50       4 $ast->error("read expects 1 argument") if $ast->size() != 2;
581 1         25 my $f = $self->evaler()->_eval( $ast->second() );
582 1 50       30 $ast->error( "read expects a string but got " . $f->type() )
583             if $f->type() ne "string";
584 1         25 return $self->evaler()->read( $f->value() );
585             }
586              
587             sub _impl_list{
588 10     10   11 my ($self, $ast) = @_;
589 10 50       24 return $self->evaler()->empty_list() if $ast->size == 1;
590 10         22 my @vs = $ast->slice( 1 .. $ast->size - 1 );
591 10         248 my $r = Language::LispPerl::Seq->new({ type => "list" });
592 10         18 foreach my $i (@vs) {
593 27         629 $r->append( $self->evaler()->_eval($i) );
594             }
595 10         34 return $r;
596             }
597              
598             sub _impl_car{
599 1     1   2 my ($self, $ast) = @_;
600 1 50       3 $ast->error("car expects 1 argument") if $ast->size != 2;
601 1         26 my $v = $self->evaler->_eval( $ast->second() );
602 1 50       24 $ast->error( "car expects 1 list as argument but got " . $v->type() )
603             if $v->type() ne "list";
604 1         3 my $fv = $v->first();
605 1         23 return $fv;
606             }
607              
608             sub _impl_cdr{
609 1     1   3 my ($self, $ast) = @_;
610 1 50       3 $ast->error("cdr expects 1 argument") if $ast->size != 2;
611 1         25 my $v = $self->evaler()->_eval( $ast->second() );
612 1 50       23 $ast->error( "cdr expects 1 list as argument but got " . $v->type() )
613             if $v->type() ne "list";
614 1 50       2 return $self->evaler()->empty_list() if ( $v->size() == 0 );
615 1         3 my @vs = $v->slice( 1 .. $v->size() - 1 );
616 1         43 my $r = Language::LispPerl::Seq->new({ type => "list"});
617 1         24 $r->value( \@vs );
618 1         23 return $r;
619             }
620              
621             sub _impl_cons{
622 1     1   1 my ($self, $ast) = @_;
623 1 50       4 $ast->error("cons expects 2 arguments") if $ast->size != 3;
624 1         26 my $fv = $self->evaler()->_eval( $ast->second() );
625 1         24 my $rvs = $self->evaler()->_eval( $ast->third() );
626 1 50       23 $ast->error( "cons expects 1 list as the second argument but got "
627             . $rvs->type() )
628             if $rvs->type() ne "list";
629 1         2 my @vs = ();
630 1 50       3 @vs = $rvs->slice( 0 .. $rvs->size() - 1 ) if $rvs->size() > 0;
631 1         2 unshift @vs, $fv;
632 1         26 my $r = Language::LispPerl::Seq->new({ type => "list" });
633 1         24 $r->value( \@vs );
634 1         23 return $r;
635             }
636              
637             sub _impl_if{
638 7     7   7 my ($self, $ast) = @_;
639 7         14 my $size = $ast->size();
640 7 50 33     26 $ast->error("if expects 2 or 3 arguments") if $size > 4 or $size < 3;
641 7         166 my $cond = $self->evaler()->_eval( $ast->second() );
642 7 50       154 $ast->error(
643             "if expects a bool as the first argument but got " . $cond->type() )
644             unless $cond->type() eq "bool";
645              
646 7 100       151 if ( $cond->value() eq "true" ) {
    100          
647 4         92 return $self->evaler()->_eval( $ast->third() );
648             }
649             elsif ( $size == 4 ) {
650 2         71 return $self->evaler()->_eval( $ast->fourth() );
651             }
652             else {
653 1         25 return $self->evaler()->nil();
654             }
655             }
656              
657             sub _impl_while{
658 1     1   2 my ($self, $ast) = @_;
659 1         4 my $size = $ast->size();
660 1 50       4 $ast->error("while expects >= 2 arguments") if $size < 3;
661              
662 1         25 my $res = $self->evaler()->nil();
663 1         6 my @body = $ast->slice( 2 .. $size - 1 );
664              
665 1         2 while(1){
666             # Evaluates the condition a first time.
667 6         139 my $cond = $self->evaler()->_eval( $ast->second() );
668 6 50       135 $ast->error( "while expects a bool as the evaluation of the condition but got "
669             . $cond->type() )
670             if $cond->type() ne "bool";
671              
672             # Condition is false. Just exit the loop
673 6 100       130 unless( $cond->value() eq "true" ) {
674 1         2 last;
675             }
676             # Condition is true. Eval the body
677 5         7 foreach my $i (@body) {
678 5         116 $res = $self->evaler()->_eval($i);
679             }
680             }
681 1         5 return $res;
682             }
683              
684             sub _impl_begin{
685 2     2   4 my ($self, $ast) = @_;
686 2         8 my $size = $ast->size();
687 2 50       7 $ast->error("being expects >= 1 arguments") if $size < 2;
688 2         53 my $res = $self->evaler()->nil();
689 2         10 my @body = $ast->slice( 1 .. $size - 1 );
690 2         6 foreach my $i (@body) {
691 9         211 $res = $self->evaler()->_eval($i);
692             }
693 2         36 return $res;
694             }
695              
696             my $NUM_FUNCTIONS = {
697             '+' => sub{ shift() + shift(); },
698             '-' => sub{ shift() - shift(); },
699             '*' => sub{ shift() * shift(); },
700             '/' => sub{ shift() / shift(); },
701             '%' => sub{ shift() % shift(); },
702              
703             '==' => sub{ shift() == shift(); },
704             '>' => sub{ shift() > shift(); },
705             '<' => sub{ shift() < shift(); },
706             '>=' => sub{ shift() >= shift(); },
707             '<=' => sub{ shift() <= shift(); },
708             '!=' => sub{ shift() != shift(); },
709             };
710              
711             sub _impl_num_bool{
712 9     9   8 my ($self, $ast, $symbol) = @_;
713 9         13 my $res = $self->_impl_num_bin( $ast , $symbol );
714 9 100       220 if( $res->value() ){
715 6         151 return $self->evaler()->true();
716             }
717 3         68 return $self->evaler()->false();
718             }
719              
720             # Binary numeric operators.
721             sub _impl_num_bin{
722 29     29   27 my ($self, $ast, $symbol) = @_;
723 29         64 my $size = $ast->size();
724 29         684 my $fn = $symbol->value();
725              
726 29 50       65 my $num_func = $NUM_FUNCTIONS->{$fn} or
727             $ast->error("Unknown numerical function $fn");
728              
729 29 50       48 $ast->error( $fn . " expects 2 arguments" ) if $size != 3;
730 29         690 my $v1 = $self->evaler()->_eval( $ast->second() );
731 29         663 my $v2 = $self->evaler()->_eval( $ast->third() );
732              
733              
734 29 50 33     641 $ast->error( $fn
735             . " expects number as arguments but got "
736             . $v1->type() . " and "
737             . $v2->type() )
738             if $v1->type() ne "number"
739             or $v2->type() ne "number";
740              
741 29         620 my $vv1 = $v1->value();
742 29         609 my $vv2 = $v2->value();
743 29         65 return Language::LispPerl::Atom->new({ type => "number", value => $num_func->( $vv1 , $vv2 ) * 1 });
744             }
745             my $STRING_FUNCTIONS = {
746             'eq' => sub{ shift() eq shift(); },
747             'ne' => sub{ shift() ne shift(); },
748             'lt' => sub{ shift() lt shift(); },
749             'gt' => sub{ shift() gt shift(); },
750             };
751             sub _impl_str_bool{
752 4     4   4 my ($self, $ast, $symbol) = @_;
753              
754 4         9 my $size = $ast->size();
755 4         89 my $fn = $symbol->value();
756 4 50       10 my $str_func = $STRING_FUNCTIONS->{$fn} or
757             $ast->error("Unknown string function $fn");
758              
759 4 50       7 $ast->error( $fn . " expects 2 arguments" ) if $size != 3;
760 4         92 my $v1 = $self->evaler()->_eval( $ast->second() );
761 4         92 my $v2 = $self->evaler()->_eval( $ast->third() );
762 4 50 33     88 $ast->error( $fn
763             . " expects string as arguments but got "
764             . $v1->type() . " and "
765             . $v2->type() )
766             if $v1->type() ne "string"
767             or $v2->type() ne "string";
768              
769 4 100       86 return $str_func->($v1->value(), $v2->value()) ? $self->evaler()->true() : $self->evaler()->false();
770             }
771              
772             sub _impl_equal{
773 5     5   5 my ($self, $ast, $symbol) = @_;
774 5         107 my $fn = $symbol->value();
775              
776 5 50       11 $ast->error( $fn . " expects 2 arguments" ) if $ast->size != 3;
777 5         112 my $v1 = $self->evaler()->_eval( $ast->second() );
778 5         119 my $v2 = $self->evaler()->_eval( $ast->third() );
779              
780             # Different type, FALSE
781 5 100       110 if ( $v1->type() ne $v2->type() ) {
782 1         25 return $self->evaler()->false();
783             }
784              
785 4         84 my $type = $v1->type();
786 4 100 66     49 if( $type eq "string" or $type eq "keyword" or $type eq "quotation" or $type eq "bool" or $type eq "nil" ){
      66        
      66        
      66        
787 3 100       64 return ( $v1->value() eq $v2->value() ) ? $self->evaler()->true() : $self->evaler()->false() ;
788             }
789              
790 1 50       4 if ( $type eq "number" ) {
791 1 50       28 return ( $v1->value() == $v2->value() ) ? $self->evaler()->true() : $self->evaler()->false();
792             }
793              
794 0 0       0 return ( $v1->value() eq $v2->value() ) ? $self->evaler()->true() : $self->evaler()->false();
795             }
796              
797              
798             sub _impl_not{
799 2     2   3 my ($self, $ast) = @_;
800 2 50       5 $ast->error("!/not expects 1 argument") if $ast->size != 2;
801 2         46 my $v = $self->evaler()->_eval( $ast->second() );
802 2 50       45 $ast->error(
803             "!/not expects a bool as the first argument but got " . $v->type() )
804             if $v->type() ne "bool";
805 2 100       43 return $v->value() eq 'true' ? $self->evaler()->false() : $self->evaler()->true();
806             }
807              
808             sub _impl_and{
809 3     3   4 my ($self, $ast, $symbol) = @_;
810 3         68 my $fn = $symbol->value();
811 3 50       7 $ast->error( $fn . " expects 2 arguments" ) if $ast->size() != 3;
812 3         71 my $v1 = $self->evaler()->_eval( $ast->second() );
813 3 50       71 $ast->error( $fn . " expects bool as arguments but got " . $v1->type() )
814             if $v1->type() ne "bool";
815              
816 3 100       65 return $self->evaler()->false() if $v1->value() eq "false";
817              
818             # First argument is true, we need to evaluate the second one.
819 2         45 my $v2 = $self->evaler()->_eval( $ast->third() );
820 2 50       43 $ast->error( $fn . " expects bool as arguments but got " . $v2->type() )
821             if $v2->type() ne "bool";
822              
823 2 100       43 return $v2->value() eq 'true' ? $self->evaler()->true() : $self->evaler()->false();
824             }
825              
826             sub _impl_or{
827 4     4   4 my ($self, $ast, $symbol) = @_;
828 4         89 my $fn = $symbol->value();
829 4 50       9 $ast->error( $fn . " expects 2 arguments" ) if $ast->size() != 3;
830 4         95 my $v1 = $self->evaler()->_eval( $ast->second() );
831 4 50       86 $ast->error( $fn . " expects bool as arguments but got " . $v1->type() )
832             if $v1->type() ne "bool";
833              
834 4 100       84 return $self->evaler()->true() if $v1->value() eq "true";
835              
836             # First argument is false. Need to eval the second one.
837 2         45 my $v2 = $self->evaler()->_eval( $ast->third() );
838 2 50       43 $ast->error( $fn . " expects bool as arguments but got " . $v2->type() )
839             if $v2->type() ne "bool";
840              
841 2 100       44 return $v2->value() eq 'true' ? $self->evaler()->true() : $self->evaler()->false();
842             }
843              
844             sub _impl_length{
845 4     4   5 my ($self, $ast, $symbol) = @_;
846 4 50       8 $ast->error("length expects 1 argument") if $ast->size() != 2;
847 4         92 my $v = $self->evaler()->_eval( $ast->second() );
848 4         98 my $r = Language::LispPerl::Atom->new({ type => "number", value => 0 });
849 4 100       97 if ( $v->type() eq "string" ) {
850 1         22 $r->value( length( $v->value() ) );
851 1         6 return $r;
852             }
853              
854 3 50 100     66 if ($v->type() eq "list"
      66        
855             or $v->type() eq "vector"
856             or $v->type() eq "xml" ){
857 3         3 $r->value( scalar @{ $v->value() } );
  3         66  
858 3         70 return $r;
859             }
860              
861 0 0       0 if ( $v->type() eq "map" ) {
862 0         0 $r->value( scalar %{ $v->value() } );
  0         0  
863 0         0 return $r;
864             }
865              
866             $ast->error(
867 0         0 "unexpected type " . $v->type() . " of argument for length." );
868             }
869              
870             sub _impl_reverse{
871 5     5   3 my ($self, $ast, $symbol) = @_;
872 5 50       11 $ast->error("length expects 1 argument") if $ast->size != 2;
873 5         115 my $v = $self->evaler()->_eval( $ast->second() );
874              
875 5 100       106 if ( $v->type() eq "string" ) {
876 2         45 return Language::LispPerl::Atom->new({ type => "string", value => scalar( reverse( $v->value() ) ) });
877             }
878              
879 3 100       65 if ( $v->type() eq "list" ) {
880 1         25 my $r = Language::LispPerl::Seq->new({ type => "list" });
881 1         3 my @vv = reverse @{ $v->value() };
  1         23  
882 1         24 $r->value( \@vv );
883 1         23 return $r;
884             }
885              
886 2 50 66     44 if ( $v->type() eq "vector" or $v->type() eq "xml" ) {
887 2         43 my $r = Language::LispPerl::Atom->new({ type => $v->type() });
888 2         4 my @vv = reverse @{ $v->value() };
  2         51  
889 2         43 $r->value( \@vv );
890 2         46 return $r;
891             }
892              
893             $ast->error(
894 0         0 "unexpected type " . $v->type() . " of argument for reverse" );
895             }
896              
897             sub _impl_append{
898 6     6   9 my ($self, $ast, $symbol) = @_;
899 6 50       18 $ast->error("append expects 2 arguments") if $ast->size != 3;
900 6         158 my $v1 = $self->evaler()->_eval( $ast->second() );
901 6         162 my $v2 = $self->evaler()->_eval( $ast->third() );
902 6         132 my $v1type = $v1->type();
903 6         129 my $v2type = $v2->type();
904              
905 6 50 100     62 $ast->error(
      100        
      66        
      33        
906             "append expects string or list or vector as arguments but got "
907             . $v1type . " and "
908             . $v2type )
909             if (
910             ( $v1type ne $v2type )
911             or ( $v1type ne "string"
912             and $v1type ne "list"
913             and $v1type ne "vector"
914             and $v1type ne "map" )
915             );
916              
917 6 100       14 if ( $v1type eq "string" ) {
918             # Concat strings.
919 3         69 return Language::LispPerl::Atom->new({ type => "string", value => $v1->value() . $v2->value() });
920             }
921              
922 3 100 100     11 if ( $v1type eq "list" or $v1type eq "vector" ) {
923 2         3 my @r = ();
924 2         3 push @r, @{ $v1->value() };
  2         46  
925 2         2 push @r, @{ $v2->value() };
  2         42  
926 2 100       6 if ( $v1type eq "list" ) {
927 1         30 return Language::LispPerl::Seq->new({ type => "list", value => \@r });
928             }
929             else {
930 1         24 return Language::LispPerl::Atom->new({ type => "vector", value => \@r });
931             }
932             }
933              
934             # Not a string, a list or a vector. Must be a map (cause we checked typing earlier)
935 1         2 my %r = ( %{ $v1->value() }, %{ $v2->value() } );
  1         25  
  1         22  
936 1         24 return Language::LispPerl::Atom->new({ type => "map", value => \%r });
937             }
938              
939             sub _impl_xml_name{
940 1     1   1 my ($self, $ast) = @_;
941 1 50       4 $ast->error( "xml-name expects 1 argument" ) if $ast->size() != 2;
942              
943 1         26 my $v = $self->evaler()->_eval( $ast->second() );
944 1 50       23 $ast->error( "xml-name expects xml as argument but got " . $v->type() )
945             if $v->type() ne "xml";
946 1         26 return Language::LispPerl::Atom->new({ type => "string", value => $v->{name} });
947             }
948              
949             sub _impl_keys{
950 1     1   2 my ($self, $ast) = @_;
951              
952 1 50       4 $ast->error("keys expects 1 argument") if $ast->size() != 2;
953 1         25 my $v = $self->evaler()->_eval( $ast->second() );
954 1 50       55 $ast->error( "keys expects map as arguments but got " . $v->type() )
955             if $v->type() ne "map";
956 1         2 my @r = ();
957 1         2 foreach my $k ( keys %{ $v->value() } ) {
  1         22  
958 2         49 push @r, Language::LispPerl::Atom->new({ type => "keyword", value => $k });
959             }
960 1         26 return Language::LispPerl::Seq->new({ type => "list", value => \@r });
961             }
962              
963             sub _impl_namespace_begin{
964 2     2   124 my ($self, $ast) = @_;
965 2 50       8 $ast->error("namespace-begin expects 1 argument") if $ast->size() != 2;
966 2         6 my $v = $ast->second();
967 2 100 66     50 unless( $v->type() eq "symbol" or $v->type() eq "keyword" ) {
968             # Not already a symbol or keyword.
969 1         25 $v = $self->evaler()->_eval($v);
970 1 50       24 $ast->error( "namespace-begin expects string as argument but got "
971             . $v->type() )
972             if $v->type() ne "string";
973             }
974 2         50 $self->evaler()->push_namespace( $v->value() );
975 2         11 return $v;
976             }
977              
978             sub _impl_namespace_end{
979 2     2   5 my ($self, $ast) = @_;
980 2 50       8 $ast->error("namespace-end expects 0 argument") if $ast->size() != 1;
981 2         50 $self->evaler()->pop_namespace();
982 2         48 return $self->evaler()->nil();
983             }
984              
985             sub _impl_object_id{
986 1     1   3 my ($self, $ast) = @_;
987 1 50       4 $ast->error("object-id expects 1 argument") if $ast->size != 2;
988 1         26 my $v = $self->evaler()->_eval( $ast->second() );
989 1         26 return Language::LispPerl::Atom->new({ type => "string", value => $v->object_id() });
990             }
991              
992             sub _impl_type{
993 11     11   29 my ($self, $ast) = @_ ;
994 11 50       31 $ast->error("type expects 1 argument") if $ast->size != 2;
995 11         293 my $v = $self->evaler()->_eval( $ast->second() );
996 11         303 return Language::LispPerl::Atom->new({ type => "string", value => $v->type() } );
997             }
998              
999             sub _impl_meta{
1000 2     2   3 my ($self, $ast) = @_;
1001              
1002 2         5 my $size = $ast->size();
1003 2 50 33     11 $ast->error("meta expects 1 or 2 arguments") if $size < 2 or $size > 3;
1004 2         50 my $v = $self->evaler()->_eval( $ast->second() );
1005 2 100       5 if ( $size == 3 ) {
1006 1         25 my $vm = $self->evaler()->_eval( $ast->third() );
1007 1 50       24 $ast->error(
1008             "meta expects 1 meta data as the second arguments but got "
1009             . $vm->type() )
1010             if $vm->type() ne "meta";
1011 1         24 $v->meta_data($vm);
1012             }
1013 2         45 my $m = $v->meta_data();
1014 2 50       5 $ast->error( "no meta data in " . Language::LispPerl::Printer::to_string($v) )
1015             if !defined $m;
1016 2         27 return $m;
1017             }
1018              
1019             sub _impl_println{
1020 1     1   2 my ($self, $ast) = @_;
1021 1 50       5 $ast->error("println expects 1 argument") if $ast->size != 2;
1022 1         25 print Language::LispPerl::Printer::to_string( $self->evaler()->_eval( $ast->second() ) )
1023             . "\n";
1024 1         29 return $self->evaler()->nil();
1025             }
1026              
1027              
1028             sub _impl_clj_string{
1029 5     5   7 my ($self, $ast) = @_;
1030 5 50       13 $ast->error("clj->string expects 1 argument") if $ast->size() != 2;
1031 5         119 my $v = $self->evaler()->_eval( $ast->second() );
1032 5         20 return Language::LispPerl::Atom->new({ type => "string", value => Language::LispPerl::Printer::to_string($v) });
1033             }
1034              
1035             sub _impl_trace_vars{
1036 1     1   2 my ($self, $ast) = @_;
1037 1 50       4 $ast->error("trace-vars expects 0 argument") if $ast->size != 1;
1038 1         25 $self->evaler()->trace_vars();
1039 1         25 return $self->evaler()->nil();
1040             }
1041              
1042             sub _impl_perlobj_type{
1043 0     0   0 my ($self, $ast) = @_;
1044 0 0       0 $ast->error("perlobj-type expects 1 argument") if $ast->size != 2;
1045 0         0 my $v = $self->evaler()->_eval( $ast->second() );
1046 0 0       0 $ast->error( "perlobj-type expects perlobject as argument but got "
1047             . $v->type() )
1048             if ( $v->type() ne "perlobject" );
1049 0         0 return Language::LispPerl::Atom->new({ type => "string", value => ref( $v->value() ) });
1050             }
1051              
1052             sub _impl_perl_clj{
1053 1     1   2 my ($self, $ast) = @_;
1054 1 50       3 $ast->error("perl->clj expects 1 argument") if $ast->size() != 2;
1055 1         24 my $o = $self->evaler()->_eval( $ast->second() );
1056 1 50       24 $ast->error(
1057             "perl->clj expects perlobject as argument but got " . $o->type() )
1058             if $o->type() ne "perlobject";
1059 1         24 return $self->evaler()->perl2clj( $o->value() );
1060             }
1061              
1062             __PACKAGE__->meta()->make_immutable();
1063             1;