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.006';
3 6     6   20 use strict;
  6         10  
  6         139  
4 6     6   23 use warnings;
  6         44  
  6         156  
5 6     6   1854 use Language::LispPerl::Seq;
  6         14  
  6         290  
6 6     6   3246 use Language::LispPerl::Atom;
  6         15  
  6         198  
7 6     6   35 use Language::LispPerl::Logger;
  6         7  
  6         96  
8              
9 6     6   21 use Carp;
  6         8  
  6         371  
10 6     6   21 use Class::Load;
  6         6  
  6         11764  
11              
12             sub from_perl{
13 680     680 0 480 my ($thing) = @_;
14 680         459 my $refthing = ref($thing);
15 680 100       750 unless( $refthing ){
16 525         2546 return $thing;
17             }
18 155 100       198 if( $refthing eq 'HASH' ){
19 128 100       183 if( my $class = $thing->{__class} ){
20 65         103 Class::Load::load_class( $class );
21 65         1045 return $class->from_hash( $thing );
22             }
23             return {
24 63         142 map{ $_ => from_perl( $thing->{$_} ) } keys %$thing
  185         202  
25             };
26             }
27 27 50       43 if( $refthing eq 'ARRAY' ){
28 27         45 return [ map{ from_perl( $_ ) } @$thing ];
  48         60  
29             }
30 0         0 confess("No idea how to turn $thing into objects");
31             }
32              
33             sub new {
34 108     108 0 144 my $class = shift;
35 108         678 my $self = {
36             class => $class,
37             ast => {},
38             nest => 0,
39             filehandler => undef,
40             filename => "unknown",
41             line => 1,
42             col => 1
43             };
44 108         173 bless $self;
45 108         207 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 34404     34404 0 21095 my $self = shift;
55 34404         19840 my $fh = shift;
56 34404 100       31665 if ( defined $fh ) {
57 108         177 $self->{filehandler} = $fh;
58             }
59             else {
60 34296         31441 return $self->{filehandler};
61             }
62             }
63              
64             sub filename {
65 2286     2286 0 1829 my $self = shift;
66 2286         1775 my $fn = shift;
67 2286 100       2725 if ( defined $fn ) {
68 108         145 $self->{filename} = $fn;
69             }
70             else {
71 2178         4035 return $self->{filename};
72             }
73             }
74              
75             sub line {
76 2872     2872 0 2189 my $self = shift;
77 2872         1972 my $line = shift;
78 2872 100       2891 if ( defined $line ) {
79 401         367 $self->{line} = $line;
80             }
81             else {
82 2471         3406 return $self->{line};
83             }
84             }
85              
86             sub col {
87 18425     18425 0 12180 my $self = shift;
88 18425         11750 my $col = shift;
89 18425 100       16620 if ( defined $col ) {
90 8324         6689 $self->{col} = $col;
91             }
92             else {
93 10101         26860 return $self->{col};
94             }
95             }
96              
97             sub ast {
98 108     108 0 119 my $self = shift;
99 108         710 return $self->{ast};
100             }
101              
102             sub peekc {
103 26080     26080 0 16425 my $self = shift;
104 26080         23745 my $fh = $self->filehandler();
105 26080 50       30518 die "file handler is un-defined" if ( !defined $fh );
106 26080         16302 my $c = undef;
107 26080 100       57326 if ( !eof($fh) ) {
108 25972         22487 $c = getc($fh);
109 25972         26366 seek( $fh, -1, 1 );
110             }
111 26080         29061 return $c;
112             }
113              
114             sub readc {
115 8216     8216 0 5196 my $self = shift;
116 8216         7825 my $fh = $self->filehandler();
117 8216         8581 my $c = $self->peekc();
118 8216 50       10556 if ( defined $c ) {
119 8216 100       8537 if ( $c eq "\n" ) {
120 293         374 $self->line( 1 + $self->line() );
121 293         337 $self->col(1);
122             }
123             else {
124 7923         8541 $self->col( 1 + $self->col() );
125             }
126 8216         8230 seek( $fh, 1, 1 );
127             }
128 8216         17741 return $c;
129             }
130              
131             sub consume {
132 7796     7796 0 5482 my $self = shift;
133 7796         5221 my $offset = shift;
134 7796         10818 for ( my $i = 0 ; $i < $offset ; $i++ ) {
135 7796         7563 $self->readc();
136             }
137             }
138              
139             sub skip_blanks {
140 6387     6387 0 4806 my $self = shift;
141 6387         4282 my $c = undef;
142 6387         4534 do {
143 8389         8120 $c = $self->peekc();
144 8389 100       9132 if ( defined $c ) {
145 8281 100       16655 if ( $c eq ";" ) {
    100          
146 22         45 $self->consume(1);
147 22         50 $self->comment();
148             }
149             elsif ( $c =~ /\s/ ) {
150 1980         2281 $self->consume(1);
151             }
152             else {
153 6279         9957 $c = undef;
154             }
155             }
156             else {
157 108         186 $c = undef;
158             }
159             } until !defined $c;
160             }
161              
162             sub parse {
163 108     108 0 118 my $self = shift;
164 108         110 my $file_or_str = shift;
165 108         116 my $mode = shift;
166 108 100       269 $mode = "string" if !defined $mode;
167 108         150 my $fh = undef;
168 108 100       224 if ( $mode eq "string" ) {
169 102 50   3   1468 open $fh, "<", \$file_or_str or die "cannot read string $file_or_str";
  3         13  
  3         5  
  3         15  
170             }
171             else {
172 6 50       285 open $fh, "<", $file_or_str or die "cannot open file $file_or_str";
173             }
174 108         2175 $self->filehandler($fh);
175 108         199 $self->filename($file_or_str);
176 108         201 $self->line(1);
177 108         181 $self->col(1);
178 108         3356 my $ast = Language::LispPerl::Seq->new();
179 108         160 do {
180 157         320 $self->skip_blanks();
181 157         283 my $r = $self->lex();
182 157 50       602 $ast->append($r) if defined $r;
183             } until eof($fh);
184 108         144 $self->{ast} = $ast;
185 108 100       644 close $fh if $mode ne "string";
186             }
187              
188             sub read_file {
189 6     6 0 19 my $self = shift;
190 6         7 my $file = shift;
191 6         21 $self->parse( $file, "file" );
192             }
193              
194             sub read_string {
195 102     102 0 109 my $self = shift;
196 102         110 my $str = shift;
197 102         235 $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 2241     2241 0 1779 my $self = shift;
209 2241         2370 my $c = $self->peekc();
210 2241 50       3260 if ( defined $c ) {
211 2241 100 100     15987 if ( $c eq '(' ) {
    100 66        
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
212 430         892 return $self->seq( "list", "(", ")" );
213             }
214             elsif ( $c eq '"' ) {
215 76         161 return $self->string();
216             }
217             elsif ( $c =~ /\d/ ) {
218 93         190 return $self->number();
219             }
220             elsif ( $c eq '[' ) {
221 92         228 return $self->seq( "vector", "[", "]" );
222             }
223             elsif ( $c eq '{' ) {
224 14         31 return $self->seq( "map", "{", "}" );
225             }
226             elsif ( $c eq '#' ) {
227 25         48 $self->consume(1);
228 25         60 return $self->dispatch();
229             }
230             elsif ( $c eq '^' ) {
231 11         24 $self->consume(1);
232 11 50       22 $self->error("meta should be a map") if $self->peekc() ne "{";
233 11         24 my $md = $self->lex();
234 11         301 $md->type("meta");
235 11         18 return $md;
236             }
237             elsif ( $c eq ':' ) {
238 12         22 $self->consume(1);
239 12         22 my $k = $self->symbol();
240 12         315 $k->type("keyword");
241 12         14 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         72 $self->consume(1);
250 37         65 my $sq = $self->lex();
251 37         1011 return Language::LispPerl::Atom->new({ type => "syntaxquotation", value => $sq });
252             }
253             elsif ( $c eq "~" ) {
254 52         85 $self->consume(1);
255 52         74 my $dq = $self->symbol();
256 52         1486 $dq->type("dequotation");
257 52         133 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 1399         1951 return $self->symbol();
277             }
278             }
279 0         0 return undef;
280             }
281              
282             sub dispatch {
283 25     25 0 27 my $self = shift;
284 25         40 my $c = $self->peekc();
285 25 50       51 if ( defined $c ) {
286 25 100       65 if ( $c eq ":" ) {
    50          
    50          
287 15         27 $self->consume(1);
288 15         50 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         18 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 28 my $self = shift;
306 22         28 my $c = undef;
307 22         21 do {
308 420         401 $c = $self->readc();
309 420 100 66     1424 if ( defined $c and $c eq "\n" ) {
310 22         45 $c = undef;
311             }
312             } until !defined $c;
313 22         49 $self->skip_blanks();
314              
315 22         31 return undef;
316             }
317              
318             sub string {
319 76     76 0 76 my $self = shift;
320 76         66 my $c = undef;
321 76         2190 my $s = Language::LispPerl::Atom->new({ type => "string" });
322             $s->{pos} = {
323 76         177 filename => $self->filename(),
324             line => $self->line(),
325             col => $self->col()
326             };
327 76         151 $self->consume(1);
328 76         69 do {
329 458         450 $c = $self->peekc();
330 458 50       603 if ( defined $c ) {
331 458 100       703 if ( $c eq "\\" ) {
    100          
332 1         5 $self->consume(1);
333 1         2 my $nc = $self->peekc();
334 1 50       4 $self->error("unexpected eof") if !defined $nc;
335 1         3 $self->consume(1);
336 1         2 my $rc = $nc;
337 1 50       13 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         3 $s->{value} .= $rc;
359             }
360             elsif ( $c ne '"' ) {
361 381         348 $s->{value} .= $c;
362 381         401 $self->consume(1);
363             }
364             else {
365 76         115 $c = undef;
366             }
367             }
368             } until !defined $c;
369 76         91 $c = $self->peekc();
370 76 50 33     259 if ( defined $c and $c eq '"' ) {
371 76         94 $self->consume(1);
372             }
373             else {
374 0         0 $self->error("expect \"");
375             }
376 76         108 $self->skip_blanks();
377 76         107 return $s;
378             }
379              
380             sub number {
381 93     93 0 101 my $self = shift;
382 93         81 my $c = undef;
383 93         2633 my $n = Language::LispPerl::Atom->new({ type => "number" });
384             $n->{pos} = {
385 93         215 filename => $self->filename(),
386             line => $self->line(),
387             col => $self->col()
388             };
389 93         133 do {
390 189         205 $c = $self->peekc();
391 189 100 66     2074 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       186 if ( $c =~ /[\+\-\d\.xXabcdefABCDEF\/\_]/ ) {
402 96         145 $self->consume(1);
403 96         258 $n->{value} .= $c;
404             }
405             else {
406 0         0 $self->error( "unexpect letter " . $c . " for number" );
407             }
408             }
409             else {
410 93         177 $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         611 };
416 93         263 $n->{value} = 0 + $n->{value};
417 93         366 delete $SIG{__WARN__};
418 93         142 $self->skip_blanks();
419 93         426 return $n;
420             }
421              
422             sub symbol {
423 1463     1463 0 1086 my $self = shift;
424 1463         1094 my $c = undef;
425 1463         40007 my $sym = Language::LispPerl::Atom->new({ type => "symbol" });
426 1463         2567 $self->skip_blanks();
427             $sym->{pos} = {
428 1463         1918 filename => $self->filename(),
429             line => $self->line(),
430             col => $self->col()
431             };
432 1463         1744 do {
433 5382         5559 $c = $self->peekc();
434 5382 50       7128 if ( defined $c ) {
435 5382 100 66     51684 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 3919 50       5645 $self->error( "unexpected letter " . $c . " for symbol" )
445             if $c =~ /[^0-9a-zA-Z_!&\?\*\/\.\+\|=%\$<>#@\:\-\\]/;
446 3919         3761 $sym->{value} .= $c;
447 3919         4211 $self->consume(1);
448             }
449             else {
450 1463         2309 $c = undef;
451             }
452             }
453             } until !defined $c;
454 1463         1712 $self->skip_blanks();
455 1463 100       1951 if ( $sym->{value} eq "" ) {
456 546         14660 return undef;
457             }
458             else {
459 917         1414 return $sym;
460             }
461             }
462              
463             sub seq {
464 546     546 0 452 my $self = shift;
465 546         482 my $type = shift;
466 546         449 my $begin = shift;
467 546         445 my $end = shift;
468 546 50       813 $type = "list" if !defined $type;
469 546 50       690 $begin = "(" if !defined $begin;
470 546 50       660 $end = ")" if !defined $end;
471 546         413 my $e = undef;
472 546         656 my $c = $self->peekc();
473              
474 546 50 33     1679 if ( defined $c and $c eq $begin ) {
475 546         706 $self->consume(1);
476             }
477             else {
478 0         0 $self->error( "expect " . $begin );
479             }
480 546         641 $self->skip_blanks();
481 546         15330 my $seq = Language::LispPerl::Seq->new({ type => $type });
482 546         1171 $seq->pos({
483             filename => $self->filename(),
484             line => $self->line(),
485             col => $self->col()
486             });
487 546         595 $self->{nest} += 1;
488 546         409 do {
489 2021         3038 $e = $self->lex();
490 2021         2358 $self->skip_blanks();
491 2021 100       5578 $seq->append($e) if defined $e;
492             } until !defined $e;
493 546         639 $c = $self->peekc();
494 546 50 33     1746 if ( defined $c and $c eq $end ) {
495 546         676 $self->consume(1);
496 546         653 $self->{nest} -= 1;
497             }
498             else {
499 0         0 $self->error( "expect " . $end );
500             }
501 546         605 $self->skip_blanks();
502 546         1048 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;