File Coverage

blib/lib/Language/LispPerl/Reader.pm
Criterion Covered Total %
statement 246 283 86.9
branch 98 132 74.2
condition 44 63 69.8
subroutine 29 33 87.8
pod 0 24 0.0
total 417 535 77.9


line stmt bran cond sub pod time code
1             package Language::LispPerl::Reader;
2             $Language::LispPerl::Reader::VERSION = '0.005';
3 6     6   27 use strict;
  6         10  
  6         178  
4 6     6   28 use warnings;
  6         50  
  6         217  
5 6     6   2401 use Language::LispPerl::Seq;
  6         26  
  6         395  
6 6     6   4119 use Language::LispPerl::Atom;
  6         28  
  6         289  
7 6     6   61 use Language::LispPerl::Logger;
  6         13  
  6         174  
8              
9 6     6   36 use Carp;
  6         10  
  6         597  
10 6     6   42 use Class::Load;
  6         11  
  6         20240  
11              
12             sub from_perl{
13 569     569 0 420 my ($thing) = @_;
14 569         377 my $refthing = ref($thing);
15 569 100       666 unless( $refthing ){
16 438         1997 return $thing;
17             }
18 131 100       172 if( $refthing eq 'HASH' ){
19 107 100       167 if( my $class = $thing->{__class} ){
20 54         84 Class::Load::load_class( $class );
21 54         877 return $class->from_hash( $thing );
22             }
23             return {
24 53         110 map{ $_ => from_perl( $thing->{$_} ) } keys %$thing
  154         184  
25             };
26             }
27 24 50       43 if( $refthing eq 'ARRAY' ){
28 24         71 return [ map{ from_perl( $_ ) } @$thing ];
  41         54  
29             }
30 0         0 confess("No idea how to turn $thing into objects");
31             }
32              
33             sub new {
34 105     105 0 192 my $class = shift;
35 105         796 my $self = {
36             class => $class,
37             ast => {},
38             nest => 0,
39             filehandler => undef,
40             filename => "unknown",
41             line => 1,
42             col => 1
43             };
44 105         167 bless $self;
45 105         311 return $self;
46             }
47              
48             sub class {
49 0     0 0 0 my $self = shift;
50 0         0 return $self->{class};
51             }
52              
53             sub filehandler {
54 33972     33972 0 25660 my $self = shift;
55 33972         23519 my $fh = shift;
56 33972 100       38095 if ( defined $fh ) {
57 105         241 $self->{filehandler} = $fh;
58             }
59             else {
60 33867         40965 return $self->{filehandler};
61             }
62             }
63              
64             sub filename {
65 2261     2261 0 2175 my $self = shift;
66 2261         1977 my $fn = shift;
67 2261 100       3040 if ( defined $fn ) {
68 105         190 $self->{filename} = $fn;
69             }
70             else {
71 2156         5074 return $self->{filename};
72             }
73             }
74              
75             sub line {
76 2841     2841 0 2458 my $self = shift;
77 2841         2382 my $line = shift;
78 2841 100       3492 if ( defined $line ) {
79 395         856 $self->{line} = $line;
80             }
81             else {
82 2446         4752 return $self->{line};
83             }
84             }
85              
86             sub col {
87 18185     18185 0 14598 my $self = shift;
88 18185         13781 my $col = shift;
89 18185 100       21350 if ( defined $col ) {
90 8212         9330 $self->{col} = $col;
91             }
92             else {
93 9973         37193 return $self->{col};
94             }
95             }
96              
97             sub ast {
98 105     105 0 153 my $self = shift;
99 105         883 return $self->{ast};
100             }
101              
102             sub peekc {
103 25760     25760 0 19873 my $self = shift;
104 25760         31232 my $fh = $self->filehandler();
105 25760 50       37330 die "file handler is un-defined" if ( !defined $fh );
106 25760         19537 my $c = undef;
107 25760 100       82113 if ( !eof($fh) ) {
108 25655         30364 $c = getc($fh);
109 25655         33091 seek( $fh, -1, 1 );
110             }
111 25760         41347 return $c;
112             }
113              
114             sub readc {
115 8107     8107 0 6420 my $self = shift;
116 8107         9792 my $fh = $self->filehandler();
117 8107         11113 my $c = $self->peekc();
118 8107 50       13372 if ( defined $c ) {
119 8107 100       11152 if ( $c eq "\n" ) {
120 290         596 $self->line( 1 + $self->line() );
121 290         548 $self->col(1);
122             }
123             else {
124 7817         12319 $self->col( 1 + $self->col() );
125             }
126 8107         11096 seek( $fh, 1, 1 );
127             }
128 8107         22444 return $c;
129             }
130              
131             sub consume {
132 7687     7687 0 6925 my $self = shift;
133 7687         5649 my $offset = shift;
134 7687         13462 for ( my $i = 0 ; $i < $offset ; $i++ ) {
135 7687         9936 $self->readc();
136             }
137             }
138              
139             sub skip_blanks {
140 6323     6323 0 5830 my $self = shift;
141 6323         4934 my $c = undef;
142 6323         5264 do {
143 8306         10471 $c = $self->peekc();
144 8306 100       11496 if ( defined $c ) {
145 8201 100       23221 if ( $c eq ";" ) {
    100          
146 22         59 $self->consume(1);
147 22         217 $self->comment();
148             }
149             elsif ( $c =~ /\s/ ) {
150 1961         3061 $self->consume(1);
151             }
152             else {
153 6218         12905 $c = undef;
154             }
155             }
156             else {
157 105         245 $c = undef;
158             }
159             } until !defined $c;
160             }
161              
162             sub parse {
163 105     105 0 213 my $self = shift;
164 105         173 my $file_or_str = shift;
165 105         137 my $mode = shift;
166 105 100       324 $mode = "string" if !defined $mode;
167 105         155 my $fh = undef;
168 105 100       306 if ( $mode eq "string" ) {
169 99 50   3   1788 open $fh, "<", \$file_or_str or die "cannot read string $file_or_str";
  3         24  
  3         5  
  3         25  
170             }
171             else {
172 6 50       265 open $fh, "<", $file_or_str or die "cannot open file $file_or_str";
173             }
174 105         3460 $self->filehandler($fh);
175 105         275 $self->filename($file_or_str);
176 105         286 $self->line(1);
177 105         244 $self->col(1);
178 105         4749 my $ast = Language::LispPerl::Seq->new();
179 105         232 do {
180 153         477 $self->skip_blanks();
181 153         393 my $r = $self->lex();
182 153 50       782 $ast->append($r) if defined $r;
183             } until eof($fh);
184 105         208 $self->{ast} = $ast;
185 105 100       727 close $fh if $mode ne "string";
186             }
187              
188             sub read_file {
189 6     6 0 10 my $self = shift;
190 6         10 my $file = shift;
191 6         26 $self->parse( $file, "file" );
192             }
193              
194             sub read_string {
195 99     99 0 144 my $self = shift;
196 99         135 my $str = shift;
197 99         282 $self->parse($str);
198             }
199              
200             sub show {
201 0     0 0 0 my $self = shift;
202 0         0 my $indent = shift;
203 0 0       0 $indent = "" if !defined $indent;
204 0         0 $self->{ast}->show($indent);
205             }
206              
207             sub lex {
208 2218     2218 0 2156 my $self = shift;
209 2218         3117 my $c = $self->peekc();
210 2218 50       3834 if ( defined $c ) {
211 2218 100 100     20183 if ( $c eq '(' ) {
    100 66        
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
212 425         1176 return $self->seq( "list", "(", ")" );
213             }
214             elsif ( $c eq '"' ) {
215 76         232 return $self->string();
216             }
217             elsif ( $c =~ /\d/ ) {
218 91         251 return $self->number();
219             }
220             elsif ( $c eq '[' ) {
221 91         304 return $self->seq( "vector", "[", "]" );
222             }
223             elsif ( $c eq '{' ) {
224 14         45 return $self->seq( "map", "{", "}" );
225             }
226             elsif ( $c eq '#' ) {
227 25         61 $self->consume(1);
228 25         77 return $self->dispatch();
229             }
230             elsif ( $c eq '^' ) {
231 11         35 $self->consume(1);
232 11 50       29 $self->error("meta should be a map") if $self->peekc() ne "{";
233 11         45 my $md = $self->lex();
234 11         419 $md->type("meta");
235 11         22 return $md;
236             }
237             elsif ( $c eq ':' ) {
238 12         35 $self->consume(1);
239 12         27 my $k = $self->symbol();
240 12         431 $k->type("keyword");
241 12         25 return $k;
242             }
243             elsif ( $c eq "'" ) {
244 0         0 $self->consume(1);
245 0         0 my $q = $self->lex();
246 0         0 return Language::LispPerl::Atom->new({type => "quotation", value => $q });
247             }
248             elsif ( $c eq "`" ) {
249 36         76 $self->consume(1);
250 36         84 my $sq = $self->lex();
251 36         1414 return Language::LispPerl::Atom->new({ type => "syntaxquotation", value => $sq });
252             }
253             elsif ( $c eq "~" ) {
254 52         125 $self->consume(1);
255 52         104 my $dq = $self->symbol();
256 52         2042 $dq->type("dequotation");
257 52         211 return $dq;
258              
259             #} elsif($c eq "@") {
260             # $self->consume(1);
261             # my $dr = $self->symbol();
262             #$dr->type("deref");
263             #return $dr;
264             }
265             elsif ( $c eq ";" ) {
266 0         0 $self->consume(1);
267 0         0 $self->comment();
268 0         0 return undef;
269             }
270             elsif ( ( $c eq ')' or $c eq ']' or $c eq '}' )
271             and $self->{nest} == 0 )
272             {
273 0         0 $self->error( "unexpected " . $c );
274             }
275             else {
276 1385         2664 return $self->symbol();
277             }
278             }
279 0         0 return undef;
280             }
281              
282             sub dispatch {
283 25     25 0 34 my $self = shift;
284 25         54 my $c = $self->peekc();
285 25 50       71 if ( defined $c ) {
286 25 100       81 if ( $c eq ":" ) {
    50          
    50          
287 15         45 $self->consume(1);
288 15         64 return Language::LispPerl::Atom->new({ type => "accessor", value => $self->lex() });
289             }
290             elsif ( $c eq "!" ) {
291 0         0 $self->consume(1);
292 0         0 return Language::LispPerl::Atom->new({ type => "sender", value => $self->lex() });
293             }
294             elsif ( $c eq '[' ) {
295 10         29 return $self->seq( "xml", "[", "]" );
296             }
297             else {
298 0         0 $self->error("unsupport syntax for disptacher");
299             }
300             }
301 0         0 return undef;
302             }
303              
304             sub comment {
305 22     22 0 41 my $self = shift;
306 22         33 my $c = undef;
307 22         32 do {
308 420         569 $c = $self->readc();
309 420 100 66     1918 if ( defined $c and $c eq "\n" ) {
310 22         58 $c = undef;
311             }
312             } until !defined $c;
313 22         78 $self->skip_blanks();
314              
315 22         53 return undef;
316             }
317              
318             sub string {
319 76     76 0 102 my $self = shift;
320 76         80 my $c = undef;
321 76         2798 my $s = Language::LispPerl::Atom->new({ type => "string" });
322             $s->{pos} = {
323 76         243 filename => $self->filename(),
324             line => $self->line(),
325             col => $self->col()
326             };
327 76         216 $self->consume(1);
328 76         84 do {
329 458         556 $c = $self->peekc();
330 458 50       725 if ( defined $c ) {
331 458 100       866 if ( $c eq "\\" ) {
    100          
332 1         4 $self->consume(1);
333 1         3 my $nc = $self->peekc();
334 1 50       7 $self->error("unexpected eof") if !defined $nc;
335 1         3 $self->consume(1);
336 1         3 my $rc = $nc;
337 1 50       19 if ( $nc eq "a" ) {
    50          
    50          
    50          
    50          
    50          
    50          
338 0         0 $rc = "\a";
339             }
340             elsif ( $nc eq "b" ) {
341 0         0 $rc = "\b";
342             }
343             elsif ( $nc eq "e" ) {
344 0         0 $rc = "\e";
345             }
346             elsif ( $nc eq "f" ) {
347 0         0 $rc = "\f";
348             }
349             elsif ( $nc eq "n" ) {
350 0         0 $rc = "\n";
351             }
352             elsif ( $nc eq "r" ) {
353 0         0 $rc = "\r";
354             }
355             elsif ( $nc eq "t" ) {
356 0         0 $rc = "\t";
357             }
358 1         4 $s->{value} .= $rc;
359             }
360             elsif ( $c ne '"' ) {
361 381         443 $s->{value} .= $c;
362 381         527 $self->consume(1);
363             }
364             else {
365 76         178 $c = undef;
366             }
367             }
368             } until !defined $c;
369 76         152 $c = $self->peekc();
370 76 50 33     345 if ( defined $c and $c eq '"' ) {
371 76         136 $self->consume(1);
372             }
373             else {
374 0         0 $self->error("expect \"");
375             }
376 76         136 $self->skip_blanks();
377 76         151 return $s;
378             }
379              
380             sub number {
381 91     91 0 119 my $self = shift;
382 91         116 my $c = undef;
383 91         3485 my $n = Language::LispPerl::Atom->new({ type => "number" });
384             $n->{pos} = {
385 91         307 filename => $self->filename(),
386             line => $self->line(),
387             col => $self->col()
388             };
389 91         187 do {
390 183         343 $c = $self->peekc();
391 183 100 66     2519 if ( defined $c
      66        
      66        
      66        
      66        
      100        
      66        
      100        
392             and $c =~ /\S/
393             and $c ne ";"
394             and $c ne '('
395             and $c ne ')'
396             and $c ne '['
397             and $c ne ']'
398             and $c ne '{'
399             and $c ne '}' )
400             {
401 92 50       270 if ( $c =~ /[\+\-\d\.xXabcdefABCDEF\/\_]/ ) {
402 92         192 $self->consume(1);
403 92         387 $n->{value} .= $c;
404             }
405             else {
406 0         0 $self->error( "unexpect letter " . $c . " for number" );
407             }
408             }
409             else {
410 91         193 $c = undef;
411             }
412             } until !defined $c;
413             local $SIG{__WARN__} = sub {
414 0     0   0 $n->error( "invild number literal " . $n->{value} );
415 91         787 };
416 91         281 $n->{value} = 0 + $n->{value};
417 91         412 delete $SIG{__WARN__};
418 91         180 $self->skip_blanks();
419 91         652 return $n;
420             }
421              
422             sub symbol {
423 1449     1449 0 1391 my $self = shift;
424 1449         1216 my $c = undef;
425 1449         54526 my $sym = Language::LispPerl::Atom->new({ type => "symbol" });
426 1449         3542 $self->skip_blanks();
427             $sym->{pos} = {
428 1449         2470 filename => $self->filename(),
429             line => $self->line(),
430             col => $self->col()
431             };
432 1449         2254 do {
433 5295         6833 $c = $self->peekc();
434 5295 50       8705 if ( defined $c ) {
435 5295 100 66     62736 if ( $c =~ /\S/
      66        
      66        
      66        
      100        
      66        
      100        
436             and $c ne ';'
437             and $c ne '('
438             and $c ne ')'
439             and $c ne '['
440             and $c ne ']'
441             and $c ne '{'
442             and $c ne '}' )
443             {
444 3846 50       7165 $self->error( "unexpected letter " . $c . " for symbol" )
445             if $c =~ /[^0-9a-zA-Z_!&\?\*\/\.\+\|=%\$<>#@\:\-\\]/;
446 3846         4896 $sym->{value} .= $c;
447 3846         5804 $self->consume(1);
448             }
449             else {
450 1449         2627 $c = undef;
451             }
452             }
453             } until !defined $c;
454 1449         2354 $self->skip_blanks();
455 1449 100       2490 if ( $sym->{value} eq "" ) {
456 540         20154 return undef;
457             }
458             else {
459 909         1679 return $sym;
460             }
461             }
462              
463             sub seq {
464 540     540 0 647 my $self = shift;
465 540         646 my $type = shift;
466 540         608 my $begin = shift;
467 540         619 my $end = shift;
468 540 50       1013 $type = "list" if !defined $type;
469 540 50       888 $begin = "(" if !defined $begin;
470 540 50       857 $end = ")" if !defined $end;
471 540         539 my $e = undef;
472 540         839 my $c = $self->peekc();
473              
474 540 50 33     2218 if ( defined $c and $c eq $begin ) {
475 540         965 $self->consume(1);
476             }
477             else {
478 0         0 $self->error( "expect " . $begin );
479             }
480 540         906 $self->skip_blanks();
481 540         21500 my $seq = Language::LispPerl::Seq->new({ type => $type });
482 540         1680 $seq->pos({
483             filename => $self->filename(),
484             line => $self->line(),
485             col => $self->col()
486             });
487 540         792 $self->{nest} += 1;
488 540         461 do {
489 2003         3722 $e = $self->lex();
490 2003         3476 $self->skip_blanks();
491 2003 100       7148 $seq->append($e) if defined $e;
492             } until !defined $e;
493 540         934 $c = $self->peekc();
494 540 50 33     2181 if ( defined $c and $c eq $end ) {
495 540         987 $self->consume(1);
496 540         758 $self->{nest} -= 1;
497             }
498             else {
499 0         0 $self->error( "expect " . $end );
500             }
501 540         824 $self->skip_blanks();
502 540         1372 return $seq;
503             }
504              
505             sub error {
506 0     0 0 0 my $self = shift;
507 0         0 my $msg = shift;
508 0         0 $msg .= " @[file: " . $self->filename();
509 0         0 $msg .= "; line: " . $self->line();
510 0         0 $msg .= "; col: " . $self->col() . "]";
511 0         0 Language::LispPerl::Logger::error($msg);
512             }
513             1;