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.007';
3 6     6   23 use strict;
  6         7  
  6         159  
4 6     6   20 use warnings;
  6         44  
  6         152  
5 6     6   2072 use Language::LispPerl::Seq;
  6         20  
  6         313  
6 6     6   3104 use Language::LispPerl::Atom;
  6         17  
  6         524  
7 6     6   39 use Language::LispPerl::Logger;
  6         8  
  6         100  
8              
9 6     6   22 use Carp;
  6         7  
  6         368  
10 6     6   25 use Class::Load;
  6         8  
  6         12651  
11              
12             sub from_perl{
13 680     680 0 620 my ($thing) = @_;
14 680         536 my $refthing = ref($thing);
15 680 100       985 unless( $refthing ){
16 525         3045 return $thing;
17             }
18 155 100       237 if( $refthing eq 'HASH' ){
19 128 100       216 if( my $class = $thing->{__class} ){
20 65         118 Class::Load::load_class( $class );
21 65         1394 return $class->from_hash( $thing );
22             }
23             return {
24 63         127 map{ $_ => from_perl( $thing->{$_} ) } keys %$thing
  185         252  
25             };
26             }
27 27 50       44 if( $refthing eq 'ARRAY' ){
28 27         76 return [ map{ from_perl( $_ ) } @$thing ];
  48         78  
29             }
30 0         0 confess("No idea how to turn $thing into objects");
31             }
32              
33             sub new {
34 109     109 0 120 my $class = shift;
35 109         481 my $self = {
36             class => $class,
37             ast => {},
38             nest => 0,
39             filehandler => undef,
40             filename => "unknown",
41             line => 1,
42             col => 1
43             };
44 109         130 bless $self;
45 109         185 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 34633     34633 0 21981 my $self = shift;
55 34633         21513 my $fh = shift;
56 34633 100       32393 if ( defined $fh ) {
57 109         167 $self->{filehandler} = $fh;
58             }
59             else {
60 34524         31777 return $self->{filehandler};
61             }
62             }
63              
64             sub filename {
65 2300     2300 0 1827 my $self = shift;
66 2300         1614 my $fn = shift;
67 2300 100       2536 if ( defined $fn ) {
68 109         118 $self->{filename} = $fn;
69             }
70             else {
71 2191         3756 return $self->{filename};
72             }
73             }
74              
75             sub line {
76 2886     2886 0 2082 my $self = shift;
77 2886         2087 my $line = shift;
78 2886 100       3243 if ( defined $line ) {
79 402         380 $self->{line} = $line;
80             }
81             else {
82 2484         3611 return $self->{line};
83             }
84             }
85              
86             sub col {
87 18551     18551 0 11764 my $self = shift;
88 18551         11558 my $col = shift;
89 18551 100       17301 if ( defined $col ) {
90 8381         7007 $self->{col} = $col;
91             }
92             else {
93 10170         26997 return $self->{col};
94             }
95             }
96              
97             sub ast {
98 109     109 0 108 my $self = shift;
99 109         557 return $self->{ast};
100             }
101              
102             sub peekc {
103 26252     26252 0 16410 my $self = shift;
104 26252         23709 my $fh = $self->filehandler();
105 26252 50       30879 die "file handler is un-defined" if ( !defined $fh );
106 26252         16088 my $c = undef;
107 26252 100       56011 if ( !eof($fh) ) {
108 26143         22997 $c = getc($fh);
109 26143         26840 seek( $fh, -1, 1 );
110             }
111 26252         27949 return $c;
112             }
113              
114             sub readc {
115 8272     8272 0 5577 my $self = shift;
116 8272         7916 my $fh = $self->filehandler();
117 8272         8657 my $c = $self->peekc();
118 8272 50       11022 if ( defined $c ) {
119 8272 100       9197 if ( $c eq "\n" ) {
120 293         383 $self->line( 1 + $self->line() );
121 293         356 $self->col(1);
122             }
123             else {
124 7979         8706 $self->col( 1 + $self->col() );
125             }
126 8272         8255 seek( $fh, 1, 1 );
127             }
128 8272         17822 return $c;
129             }
130              
131             sub consume {
132 7852     7852 0 5394 my $self = shift;
133 7852         5519 my $offset = shift;
134 7852         11012 for ( my $i = 0 ; $i < $offset ; $i++ ) {
135 7852         7809 $self->readc();
136             }
137             }
138              
139             sub skip_blanks {
140 6425     6425 0 4830 my $self = shift;
141 6425         4155 my $c = undef;
142 6425         4364 do {
143 8441         8354 $c = $self->peekc();
144 8441 100       9103 if ( defined $c ) {
145 8332 100       17295 if ( $c eq ";" ) {
    100          
146 22         30 $self->consume(1);
147 22         50 $self->comment();
148             }
149             elsif ( $c =~ /\s/ ) {
150 1994         2393 $self->consume(1);
151             }
152             else {
153 6316         10430 $c = undef;
154             }
155             }
156             else {
157 109         182 $c = undef;
158             }
159             } until !defined $c;
160             }
161              
162             sub parse {
163 109     109 0 87 my $self = shift;
164 109         107 my $file_or_str = shift;
165 109         100 my $mode = shift;
166 109 100       234 $mode = "string" if !defined $mode;
167 109         90 my $fh = undef;
168 109 100       168 if ( $mode eq "string" ) {
169 103 50   4   1203 open $fh, "<", \$file_or_str or die "cannot read string $file_or_str";
  4         37  
  4         8  
  4         24  
170             }
171             else {
172 6 50       240 open $fh, "<", $file_or_str or die "cannot open file $file_or_str";
173             }
174 109         3006 $self->filehandler($fh);
175 109         164 $self->filename($file_or_str);
176 109         158 $self->line(1);
177 109         164 $self->col(1);
178 109         3345 my $ast = Language::LispPerl::Seq->new();
179 109         153 do {
180 158         258 $self->skip_blanks();
181 158         304 my $r = $self->lex();
182 158 50       529 $ast->append($r) if defined $r;
183             } until eof($fh);
184 109         133 $self->{ast} = $ast;
185 109 100       533 close $fh if $mode ne "string";
186             }
187              
188             sub read_file {
189 6     6 0 8 my $self = shift;
190 6         12 my $file = shift;
191 6         12 $self->parse( $file, "file" );
192             }
193              
194             sub read_string {
195 103     103 0 111 my $self = shift;
196 103         94 my $str = shift;
197 103         190 $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 2254     2254 0 1682 my $self = shift;
209 2254         2413 my $c = $self->peekc();
210 2254 50       3374 if ( defined $c ) {
211 2254 100 100     16070 if ( $c eq '(' ) {
    100 66        
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
212 433         791 return $self->seq( "list", "(", ")" );
213             }
214             elsif ( $c eq '"' ) {
215 77         141 return $self->string();
216             }
217             elsif ( $c =~ /\d/ ) {
218 93         154 return $self->number();
219             }
220             elsif ( $c eq '[' ) {
221 92         188 return $self->seq( "vector", "[", "]" );
222             }
223             elsif ( $c eq '{' ) {
224 14         42 return $self->seq( "map", "{", "}" );
225             }
226             elsif ( $c eq '#' ) {
227 25         46 $self->consume(1);
228 25         51 return $self->dispatch();
229             }
230             elsif ( $c eq '^' ) {
231 11         26 $self->consume(1);
232 11 50       23 $self->error("meta should be a map") if $self->peekc() ne "{";
233 11         26 my $md = $self->lex();
234 11         315 $md->type("meta");
235 11         23 return $md;
236             }
237             elsif ( $c eq ':' ) {
238 12         24 $self->consume(1);
239 12         21 my $k = $self->symbol();
240 12         342 $k->type("keyword");
241 12         18 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 37         60 $self->consume(1);
250 37         69 my $sq = $self->lex();
251 37         1033 return Language::LispPerl::Atom->new({ type => "syntaxquotation", value => $sq });
252             }
253             elsif ( $c eq "~" ) {
254 52         84 $self->consume(1);
255 52         69 my $dq = $self->symbol();
256 52         1474 $dq->type("dequotation");
257 52         148 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 1408         1905 return $self->symbol();
277             }
278             }
279 0         0 return undef;
280             }
281              
282             sub dispatch {
283 25     25 0 22 my $self = shift;
284 25         40 my $c = $self->peekc();
285 25 50       51 if ( defined $c ) {
286 25 100       57 if ( $c eq ":" ) {
    50          
    50          
287 15         28 $self->consume(1);
288 15         32 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         20 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 21 my $self = shift;
306 22         23 my $c = undef;
307 22         24 do {
308 420         425 $c = $self->readc();
309 420 100 66     1462 if ( defined $c and $c eq "\n" ) {
310 22         41 $c = undef;
311             }
312             } until !defined $c;
313 22         49 $self->skip_blanks();
314              
315 22         43 return undef;
316             }
317              
318             sub string {
319 77     77 0 74 my $self = shift;
320 77         59 my $c = undef;
321 77         2118 my $s = Language::LispPerl::Atom->new({ type => "string" });
322             $s->{pos} = {
323 77         154 filename => $self->filename(),
324             line => $self->line(),
325             col => $self->col()
326             };
327 77         127 $self->consume(1);
328 77         71 do {
329 454         459 $c = $self->peekc();
330 454 50       573 if ( defined $c ) {
331 454 100       696 if ( $c eq "\\" ) {
    100          
332 1         2 $self->consume(1);
333 1         2 my $nc = $self->peekc();
334 1 50       2 $self->error("unexpected eof") if !defined $nc;
335 1         3 $self->consume(1);
336 1         2 my $rc = $nc;
337 1 50       11 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         2 $s->{value} .= $rc;
359             }
360             elsif ( $c ne '"' ) {
361 376         315 $s->{value} .= $c;
362 376         400 $self->consume(1);
363             }
364             else {
365 77         117 $c = undef;
366             }
367             }
368             } until !defined $c;
369 77         88 $c = $self->peekc();
370 77 50 33     263 if ( defined $c and $c eq '"' ) {
371 77         89 $self->consume(1);
372             }
373             else {
374 0         0 $self->error("expect \"");
375             }
376 77         99 $self->skip_blanks();
377 77         98 return $s;
378             }
379              
380             sub number {
381 93     93 0 98 my $self = shift;
382 93         77 my $c = undef;
383 93         2547 my $n = Language::LispPerl::Atom->new({ type => "number" });
384             $n->{pos} = {
385 93         180 filename => $self->filename(),
386             line => $self->line(),
387             col => $self->col()
388             };
389 93         111 do {
390 189         226 $c = $self->peekc();
391 189 100 66     1888 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 96 50       195 if ( $c =~ /[\+\-\d\.xXabcdefABCDEF\/\_]/ ) {
402 96         136 $self->consume(1);
403 96         232 $n->{value} .= $c;
404             }
405             else {
406 0         0 $self->error( "unexpect letter " . $c . " for number" );
407             }
408             }
409             else {
410 93         149 $c = undef;
411             }
412             } until !defined $c;
413             local $SIG{__WARN__} = sub {
414 0     0   0 $n->error( "invild number literal " . $n->{value} );
415 93         550 };
416 93         265 $n->{value} = 0 + $n->{value};
417 93         285 delete $SIG{__WARN__};
418 93         132 $self->skip_blanks();
419 93         434 return $n;
420             }
421              
422             sub symbol {
423 1472     1472 0 1133 my $self = shift;
424 1472         1092 my $c = undef;
425 1472         40626 my $sym = Language::LispPerl::Atom->new({ type => "symbol" });
426 1472         2570 $self->skip_blanks();
427             $sym->{pos} = {
428 1472         1945 filename => $self->filename(),
429             line => $self->line(),
430             col => $self->col()
431             };
432 1472         1846 do {
433 5430         5686 $c = $self->peekc();
434 5430 50       7238 if ( defined $c ) {
435 5430 100 66     51640 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 3958 50       6179 $self->error( "unexpected letter " . $c . " for symbol" )
445             if $c =~ /[^0-9a-zA-Z_!&\?\*\/\.\+\|=%\$<>#@\:\-\\]/;
446 3958         3968 $sym->{value} .= $c;
447 3958         4482 $self->consume(1);
448             }
449             else {
450 1472         2264 $c = undef;
451             }
452             }
453             } until !defined $c;
454 1472         1705 $self->skip_blanks();
455 1472 100       1912 if ( $sym->{value} eq "" ) {
456 549         14468 return undef;
457             }
458             else {
459 923         1326 return $sym;
460             }
461             }
462              
463             sub seq {
464 549     549 0 448 my $self = shift;
465 549         501 my $type = shift;
466 549         405 my $begin = shift;
467 549         397 my $end = shift;
468 549 50       754 $type = "list" if !defined $type;
469 549 50       712 $begin = "(" if !defined $begin;
470 549 50       663 $end = ")" if !defined $end;
471 549         481 my $e = undef;
472 549         611 my $c = $self->peekc();
473              
474 549 50 33     1704 if ( defined $c and $c eq $begin ) {
475 549         711 $self->consume(1);
476             }
477             else {
478 0         0 $self->error( "expect " . $begin );
479             }
480 549         612 $self->skip_blanks();
481 549         15942 my $seq = Language::LispPerl::Seq->new({ type => $type });
482 549         1102 $seq->pos({
483             filename => $self->filename(),
484             line => $self->line(),
485             col => $self->col()
486             });
487 549         619 $self->{nest} += 1;
488 549         388 do {
489 2033         2821 $e = $self->lex();
490 2033         2490 $self->skip_blanks();
491 2033 100       5555 $seq->append($e) if defined $e;
492             } until !defined $e;
493 549         650 $c = $self->peekc();
494 549 50 33     1701 if ( defined $c and $c eq $end ) {
495 549         831 $self->consume(1);
496 549         607 $self->{nest} -= 1;
497             }
498             else {
499 0         0 $self->error( "expect " . $end );
500             }
501 549         652 $self->skip_blanks();
502 549         887 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;