File Coverage

blib/lib/CljPerl/Reader.pm
Criterion Covered Total %
statement 15 260 5.7
branch 0 124 0.0
condition 0 63 0.0
subroutine 5 29 17.2
pod 0 23 0.0
total 20 499 4.0


line stmt bran cond sub pod time code
1             package CljPerl::Reader;
2            
3 2     2   11 use strict;
  2         3  
  2         66  
4 2     2   11 use warnings;
  2         4  
  2         53  
5 2     2   1115 use CljPerl::Seq;
  2         4  
  2         47  
6 2     2   848 use CljPerl::Atom;
  2         4  
  2         46  
7 2     2   11 use CljPerl::Logger;
  2         3  
  2         4827  
8            
9             our $VERSION = '0.10';
10            
11             sub new {
12 0     0 0   my $class = shift;
13 0           my $self = {class => $class,
14             ast => {},
15             nest => 0,
16             filehandler => undef,
17             filename => "unknown",
18             line => 1,
19             col => 1};
20 0           bless $self;
21 0           return $self;
22             }
23            
24             sub class {
25 0     0 0   my $self = shift;
26 0           return $self->{class};
27             }
28            
29             sub filehandler {
30 0     0 0   my $self = shift;
31 0           my $fh = shift;
32 0 0         if(defined $fh) {
33 0           $self->{filehandler} = $fh;
34             } else {
35 0           return $self->{filehandler};
36             }
37             }
38            
39             sub filename {
40 0     0 0   my $self = shift;
41 0           my $fn = shift;
42 0 0         if(defined $fn) {
43 0           $self->{filename} = $fn;
44             } else {
45 0           return $self->{filename};
46             }
47             }
48            
49             sub line {
50 0     0 0   my $self = shift;
51 0           my $line = shift;
52 0 0         if(defined $line) {
53 0           $self->{line} = $line;
54             } else {
55 0           return $self->{line};
56             };
57             }
58            
59             sub col {
60 0     0 0   my $self = shift;
61 0           my $col = shift;
62 0 0         if(defined $col) {
63 0           $self->{col} = $col;
64             } else {
65 0           return $self->{col};
66             };
67             }
68            
69             sub ast {
70 0     0 0   my $self = shift;
71 0           return $self->{ast};
72             }
73            
74             sub peekc {
75 0     0 0   my $self = shift;
76 0           my $fh = $self->filehandler();
77 0 0         die "file handler is un-defined" if(!defined $fh);
78 0           my $c = undef;
79 0 0         if(!eof($fh)) {
80 0           $c = getc($fh);
81 0           seek($fh, -1, 1);
82             }
83 0           return $c;
84             }
85            
86             sub readc {
87 0     0 0   my $self = shift;
88 0           my $fh = $self->filehandler();
89 0           my $c = $self->peekc();
90 0 0         if(defined $c) {
91 0 0         if($c eq "\n"){
92 0           $self->line(1 + $self->line());
93 0           $self->col(1);
94             } else {
95 0           $self->col(1 + $self->col());
96             };
97 0           seek($fh, 1, 1);
98             };
99 0           return $c;
100             }
101            
102             sub consume {
103 0     0 0   my $self = shift;
104 0           my $offset = shift;
105 0           for(my $i=0; $i<$offset; $i++){
106 0           $self->readc();
107             }
108             }
109            
110             sub skip_blanks {
111 0     0 0   my $self = shift;
112 0           my $c = undef;
113 0           do {
114 0           $c = $self->peekc();
115 0 0         if(defined $c){
116 0 0         if($c eq ";"){
    0          
117 0           $self->consume(1);
118 0           $self->comment();
119             } elsif($c =~ /\s/) {
120 0           $self->consume(1);
121             } else {
122 0           $c = undef;
123             }
124             } else {
125 0           $c = undef;
126             }
127             } until ! defined $c;
128             }
129            
130             sub parse {
131 0     0 0   my $self = shift;
132 0           my $file_or_str = shift;
133 0           my $mode = shift;
134 0 0         $mode = "string" if !defined $mode;
135 0           my $fh = undef;
136 0 0         if($mode eq "string"){
137 0 0         open $fh, "<", \$file_or_str or die "cannot read string $file_or_str";
138             } else {
139 0 0         open $fh, "<", $file_or_str or die "cannot open file $file_or_str";
140             };
141 0           $self->filehandler($fh);
142 0           $self->filename($file_or_str);
143 0           $self->line(1);
144 0           $self->col(1);
145 0           my $ast = CljPerl::Seq->new();
146 0           do {
147 0           $self->skip_blanks();
148 0           my $r = $self->lex();
149 0 0         $ast->append($r) if defined $r;
150             } until eof($fh);
151 0           $self->{ast} = $ast;
152 0 0         close $fh if $mode ne "string";
153             }
154            
155             sub read_file {
156 0     0 0   my $self = shift;
157 0           my $file = shift;
158 0           $self->parse($file, "file");
159             }
160            
161             sub read_string {
162 0     0 0   my $self = shift;
163 0           my $str = shift;
164 0           $self->parse($str);
165             }
166            
167             sub show {
168 0     0 0   my $self = shift;
169 0           my $indent = shift;
170 0 0         $indent = "" if !defined $indent;
171 0           $self->{ast}->show($indent);
172             }
173            
174             sub lex {
175 0     0 0   my $self = shift;
176 0           my $c = $self->peekc();
177 0 0         if(defined $c) {
178 0 0 0       if($c eq '(') {
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
179 0           return $self->seq("list", "(", ")");
180             } elsif($c eq '"') {
181 0           return $self->string();
182             } elsif($c =~ /\d/) {
183 0           return $self->number();
184             } elsif($c eq '[') {
185 0           return $self->seq("vector", "[", "]");
186             } elsif($c eq '{') {
187 0           return $self->seq("map", "{", "}");
188             } elsif($c eq '#') {
189 0           $self->consume(1);
190 0           return $self->dispatch();
191             } elsif($c eq '^') {
192 0           $self->consume(1);
193 0 0         $self->error("meta should be a map") if $self->peekc() ne "{";
194 0           my $md = $self->lex();
195 0           $md->type("meta");
196 0           return $md;
197             } elsif($c eq ':') {
198 0           $self->consume(1);
199 0           my $k = $self->symbol();
200 0           $k->type("keyword");
201 0           return $k;
202             } elsif($c eq "'") {
203 0           $self->consume(1);
204 0           my $q = $self->lex();
205 0           return CljPerl::Atom->new("quotation", $q);
206             } elsif($c eq "`") {
207 0           $self->consume(1);
208 0           my $sq = $self->lex();
209 0           return CljPerl::Atom->new("syntaxquotation", $sq);
210             } elsif($c eq "~") {
211 0           $self->consume(1);
212 0           my $dq = $self->symbol();
213 0           $dq->type("dequotation");
214 0           return $dq;
215             #} elsif($c eq "@") {
216             # $self->consume(1);
217             # my $dr = $self->symbol();
218             #$dr->type("deref");
219             #return $dr;
220             } elsif($c eq ";") {
221 0           $self->consume(1);
222 0           $self->comment();
223 0           return undef;
224             } elsif(($c eq ')' or $c eq ']' or $c eq '}')
225             and $self->{nest} == 0) {
226 0           $self->error("unexpected " . $c);
227             } else {
228 0           return $self->symbol();
229             };
230             };
231 0           return undef;
232             }
233            
234             sub dispatch {
235 0     0 0   my $self = shift;
236 0           my $c = $self->peekc();
237 0 0         if(defined $c) {
238 0 0         if($c eq ":") {
    0          
    0          
239 0           $self->consume(1);
240 0           return CljPerl::Atom->new("accessor", $self->lex());
241             } elsif($c eq "!") {
242 0           $self->consume(1);
243 0           return CljPerl::Atom->new("sender", $self->lex());
244             } elsif($c eq '[') {
245 0           return $self->seq("xml", "[", "]");
246             } else {
247 0           $self->error("unsupport syntax for disptacher");
248             };
249             };
250 0           return undef;
251             };
252            
253             sub comment {
254 0     0 0   my $self = shift;
255 0           my $c = undef;
256 0           do {
257 0           $c = $self->readc();
258 0 0 0       if(defined $c and $c eq "\n"){
259 0           $c = undef;
260             }
261             } until ! defined $c;
262 0           $self->skip_blanks();
263            
264 0           return undef;
265             }
266            
267             sub string {
268 0     0 0   my $self = shift;
269 0           my $c = undef;
270 0           my $s = CljPerl::Atom->new("string");
271 0           $s->{pos} = {filename=>$self->filename(),
272             line=>$self->line(),
273             col=>$self->col()};
274 0           $self->consume(1);
275 0           do {
276 0           $c = $self->peekc();
277 0 0         if(defined $c){
278 0 0         if($c eq "\\") {
    0          
279 0           $self->consume(1);
280 0           my $nc = $self->peekc();
281 0 0         $self->error("unexpected eof") if !defined $nc;
282 0           $self->consume(1);
283 0           my $rc = $nc;
284 0 0         if($nc eq "a") {
    0          
    0          
    0          
    0          
    0          
    0          
285 0           $rc = "\a";
286             } elsif($nc eq "b") {
287 0           $rc = "\b";
288             } elsif($nc eq "e") {
289 0           $rc = "\e";
290             } elsif($nc eq "f") {
291 0           $rc = "\f";
292             } elsif($nc eq "n") {
293 0           $rc = "\n";
294             } elsif($nc eq "r") {
295 0           $rc = "\r";
296             } elsif($nc eq "t") {
297 0           $rc = "\t";
298             };
299 0           $s->{value} .= $rc;
300             } elsif($c ne '"') {
301 0           $s->{value} .= $c;
302 0           $self->consume(1);
303             } else {
304 0           $c = undef;
305             };
306             };
307             } until ! defined $c;
308 0           $c = $self->peekc();
309 0 0 0       if(defined $c and $c eq '"'){
310 0           $self->consume(1);
311             } else {
312 0           $self->error("expect \"");
313             }
314 0           $self->skip_blanks();
315 0           return $s;
316             }
317            
318             sub number {
319 0     0 0   my $self = shift;
320 0           my $c = undef;
321 0           my $n = CljPerl::Atom->new("number");
322 0           $n->{pos} = {filename=>$self->filename(),
323             line=>$self->line(),
324             col=>$self->col()};
325 0           do {
326 0           $c = $self->peekc();
327 0 0 0       if(defined $c
      0        
      0        
      0        
      0        
      0        
      0        
      0        
328             and $c =~ /\S/
329             and $c ne ";"
330             and $c ne '(' and $c ne ')'
331             and $c ne '[' and $c ne ']'
332             and $c ne '{' and $c ne '}') {
333 0 0         if($c =~ /[\+\-\d\.xXabcdefABCDEF\/\_]/) {
334 0           $self->consume(1);
335 0           $n->{value} .= $c;
336             } else {
337 0           $self->error("unexpect letter " . $c . " for number");
338             };
339             } else {
340 0           $c = undef;
341             };
342             } until ! defined $c;
343             local $SIG{__WARN__} = sub {
344 0     0     $n->error("invild number literal " . $n->{value});
345 0           };
346 0           $n->{value} = 0 + $n->{value};
347 0           delete $SIG{__WARN__};
348 0           $self->skip_blanks();
349 0           return $n;
350             }
351            
352             sub symbol {
353 0     0 0   my $self = shift;
354 0           my $c = undef;
355 0           my $sym = CljPerl::Atom->new("symbol");
356 0           $self->skip_blanks();
357 0           $sym->{pos} = {filename=>$self->filename(),
358             line=>$self->line(),
359             col=>$self->col()};
360 0           do {
361 0           $c = $self->peekc();
362 0 0         if(defined $c){
363 0 0 0       if($c =~ /\S/
      0        
      0        
      0        
      0        
      0        
      0        
364             and $c ne ';'
365             and $c ne '(' and $c ne ')'
366             and $c ne '[' and $c ne ']'
367             and $c ne '{' and $c ne '}') {
368 0 0         $self->error("unexpected letter " . $c . " for symbol")
369             if $c =~ /[^0-9a-zA-Z_!&\?\*\/\.\+\|=%\$<>#@\:\-\\]/;
370 0           $sym->{value} .= $c;
371 0           $self->consume(1);
372             } else {
373 0           $c = undef;
374             };
375             };
376             } until ! defined $c;
377 0           $self->skip_blanks();
378 0 0         if($sym->{value} eq "") {
379 0           return undef;
380             } else {
381 0           return $sym;
382             }
383             }
384            
385             sub seq {
386 0     0 0   my $self = shift;
387 0           my $type = shift;
388 0           my $begin = shift;
389 0           my $end = shift;
390 0 0         $type = "list" if !defined $type;
391 0 0         $begin = "(" if !defined $begin;
392 0 0         $end = ")" if !defined $end;
393 0           my $e = undef;
394 0           my $c = $self->peekc();
395 0 0 0       if(defined $c and $c eq $begin){
396 0           $self->consume(1);
397             } else {
398 0           $self->error("expect " . $begin);
399             };
400 0           $self->skip_blanks();
401 0           my $seq = CljPerl::Seq->new($type);
402 0           $seq->{pos} = {filename=>$self->filename(),
403             line=>$self->line(),
404             col=>$self->col()};
405 0           $self->{nest} += 1;
406 0           do {
407 0           $e = $self->lex();
408 0           $self->skip_blanks();
409 0 0         $seq->append($e) if defined $e;
410             } until ! defined $e;
411 0           $c = $self->peekc();
412 0 0 0       if(defined $c and $c eq $end){
413 0           $self->consume(1);
414 0           $self->{nest} -= 1;
415             } else {
416 0           $self->error("expect " . $end);
417             };
418 0           $self->skip_blanks();
419 0           return $seq;
420             }
421            
422             sub error {
423 0     0 0   my $self = shift;
424 0           my $msg = shift;
425 0           $msg .= " @[file: " . $self->filename();
426 0           $msg .= "; line: " . $self->line();
427 0           $msg .= "; col: " . $self->col() . "]";
428 0           CljPerl::Logger::error($msg);
429             }
430             1;