File Coverage

blib/lib/OGDL/Parser.pm
Criterion Covered Total %
statement 213 296 71.9
branch 84 132 63.6
condition 11 24 45.8
subroutine 20 29 68.9
pod 0 9 0.0
total 328 490 66.9


line stmt bran cond sub pod time code
1             # Ogdl.pm
2             # author: R.Veen
3             # license: same as the rest of OGDL (zlib)
4             # see: www.ogdl.org
5             # date: 12 june 2003
6              
7             # 2010-11-11 Hui Zhou:
8             # Comma (,) only resets one level up
9             # Semicolon (;) resets to beginning of the line (or group)
10             # Semicolon is the new metachar that we have discussed in OGDL mailinglist but never got agreed.
11              
12             package OGDL::Parser;
13              
14 2     2   50755 use strict;
  2         5  
  2         90  
15              
16             our $VERSION = '0.02';
17              
18 2     2   1301 use OGDL::Graph;
  2         12681  
  2         9129  
19              
20             sub LoadGraph{
21 0     0 0 0 my ($name)=@_;
22 0 0       0 if(-T "$name.gm"){
23 0         0 return m4ToGraph("$name.gm");
24             }
25 0 0       0 if(-T "$name.g"){
26 0         0 return fileToGraph("$name.g");
27             }
28 0 0       0 if(-T $name){
29 0         0 return fileToGraph($name);
30             }
31 0 0       0 if(-d $name){
32 0         0 return dirToGraph($name);
33             }
34 0         0 return undef;
35             }
36              
37             sub dirToGraph{
38 0     0 0 0 my ($dir)=@_;
39 0         0 my $topdir=`pwd`;
40 0 0       0 if(-d $dir){
41 0         0 chdir $dir;
42 0         0 my $g=OGDL::Graph->new($dir);
43 0         0 my @dirlist=sort glob("*");
44 0         0 foreach(@dirlist){
45 0 0       0 if(-d $_){
46 0         0 my $subg=dirToGraph($_);
47 0         0 $g->addNode($subg);
48             }
49             else{
50 0         0 $g->addGraph($_);
51             }
52             }
53             # print "chdir $cwd";
54 0         0 chdir "..";
55 0         0 return $g;
56             }
57 0         0 return undef;
58             }
59              
60             sub fileToGraph
61             {
62 3 50   3 0 4270 open my $input, $_[0] or return undef;
63 3         40 my $parser=OGDL::Parser->new($_[0]);
64 3         19 $parser->read($input);
65 3         16 my $g=$parser->parse;
66 3         184 return $g;
67             }
68              
69             sub m4ToGraph{
70 0 0   0 0 0 open my $input, "m4 $_[0] |" or return undef;
71 0         0 my $parser=OGDL::Parser->new($_[0]);
72 0         0 $parser->read($input);
73 0         0 my $g=$parser->parse;
74 0         0 return $g;
75             }
76              
77             # this routine accepts string
78             sub stringToGraph
79             {
80 0     0 0 0 my $parser=OGDL::Parser->new("string");
81 0         0 $parser->append($_[0]);
82 0         0 my $g=$parser->parse;
83 0         0 return $g;
84             }
85              
86             # $ogdl->read(*FILEHANDL)
87             sub read{
88 3     3 0 7 my $r_ogdl= $_[0];
89 3         7 my $input =$_[1];
90 3         9 my $lines="";
91             # my $tabexpand=' 'x8;
92 3         9 my $tabwidth=8;
93 3         5 my $i;
94 3         109 while(<$input>){
95             # print;
96             # s/#.*$/\n/; # Remove any trailing comments
97 150         229 s/\r\n/\n/; #convert the newline
98 150         1057 s/\s*$/\n/; # Remove any trailing spaces
99             # next if ( /^$/ ); # Skip empty lines
100             # s/\t/$tabexpand/g; # Expand tabs
101 150         219 $i=index $_, "\t";
102 150         313 while($i>=0){
103 6         9 my $n=$tabwidth-$i%$tabwidth;
104 6         12 substr($_,$i,1)=' 'x$n;
105 6         9 $i+=$n;
106 6         16 $i=index $_, ' ',$i;
107             }
108 150         433 $lines = $lines . $_;
109             }
110 3         52 $$r_ogdl{"text"}=$lines;
111             }
112              
113             sub append{
114 0     0 0 0 my $r_ogdl=$_[0];
115 0         0 $$r_ogdl{"text"}=$$r_ogdl{"text"}.$_[1];
116             }
117              
118             sub new
119             {
120 3     3 0 12 my ($class,$rootname)=@_;
121 3         6 my $tempg;
122 3         34 $tempg=OGDL::Graph->new($rootname);
123 3         48 my $p = {
124             text => "",
125             ix => 0,
126             level => 0,
127             indentation => [ () ],
128             line_level => -1,
129             groups => [ () ],
130             ixgroup => 0,
131             # allowgoback =>0, #temperary hack to decide whether comar will go back one indent level, not necessary with opposite precedence
132             g => undef
133             };
134 3         14 $p->{g}[0]=$tempg;
135 3         13 return bless $p, $class;
136             }
137              
138             sub parse
139             {
140 3     3 0 88 my $p = shift;
141 3         16 while (_line($p)) { }
142 3         11 return $p->{g}[0];
143             }
144              
145             sub _line
146             {
147 103     103   124 my $p = shift;
148              
149             # print "[" . substr ($p->{text},$p->{ix},20) . "]\n" ;
150 103         153 my $i = _space($p);
151 103 100       193 if (_newline($p)) { return 1 };
  5         15  
152 98 100       173 if (_eos($p)) { return 0 };
  3         9  
153             #print "line: lev(at entry) " . $p->{line_level} . "\n";
154             #print "line ind[]= ";
155             #$ind = $p->{indentation};
156             #for (@$ind) {
157             #print $_ . " ";
158             #}
159             #print ", i=$i, current ind=$p->{indentation}[$p->{line_level}];, line_level=$p->{line_level}\n";
160              
161              
162 95 100       297 if ( $p->{line_level} == -1 ) { #start
    100          
163 3         11 $p->{indentation}[0] = $i;
164 3         8 $p->{line_level} = 0;
165             }
166             elsif ( $i > $p->{indentation}[$p->{line_level}] ) { #indentation increased
167 35         972 $p->{line_level}++;
168 35         67 $p->{indentation}[$p->{line_level}] = $i;
169             }
170             else {
171 57 100       147 if ( $i < $p->{indentation}[$p->{line_level}] ) {#indentation decreased
172 24         66 while ( $p->{line_level} > 0) { #find the parent or brother line_level
173 46 100       106 if ( $i >= $p->{indentation}[$p->{line_level}] ) {
174 16         26 last;
175             }
176 30         64 $p->{line_level}--;
177             }
178             }
179             }
180 95         240 $p->{level} = $p->{line_level}; #current level
181 95         145 $p->{groups}[$p->{ixgroup}]=$p->{level};
182 95         422 $p->{allowgoback}=0;
183              
184             # print "\ncurrent level is $p->{level}\n";
185              
186              
187             # print "line: lev(after) " . $p->{line_level} . "\n";
188              
189 95         172 while ( _node($p) ) {
190 166         279 _space($p); #Remove the trailing spaces
191             }
192            
193 95 50       157 return _eos($p) ? 0:1;
194             }
195              
196             sub _node
197             {
198             # print "get node\n";
199 261     261   276 my $p = shift;
200 261 50       354 if ( _eos($p) ) { return undef; }
  0         0  
201 261         278 my $s=undef;
202 261         361 my $c=_peek($p);
203 261 100 66     2461 if($c eq '('){
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    50          
204 6         8 _getc($p);
205 6         15 $p->{ixgroup}++;
206 6         9 $p->{groups}[$p->{ixgroup}] = $p->{level};
207             # $p->{allowgoback}=0;
208 6         15 return 1;
209             }
210             elsif($c eq ')'){
211 6         10 _getc($p);
212 6         14 $p->{level}=$p->{groups}[$p->{ixgroup}]+1;
213             # $p->{allowgoback}=1;
214 6         8 $p->{ixgroup}--;
215 6 50       12 if ($p->{ixgroup} < 0)
216 0         0 { _fatal("Unmatched ')'"); }
217 6         14 return 1;
218             }
219             elsif($c eq ';'){
220 5         16 _getc($p);
221 5         10 $p->{level} = $p->{groups}[$p->{ixgroup}];
222             # if($p->{allowgoback}){
223             # $p->{level}--; #exception: , at beginning
224             # $p->{allowgoback}=0;
225             # }
226 5         11 return 1;
227             }
228             elsif($c eq ','){
229 18         31 _getc($p);
230 18         18 $p->{level}--; #exception: , at beginning
231 18         37 return 1;
232             }
233             elsif($c eq "'" || $c eq '"'){
234 3         8 $s=_quoted($p, '\'');
235             }
236             elsif($c eq '\\'){
237 1         3 $s=_block($p);
238             }
239             elsif($c eq "\n"){
240 95         174 $s=_newline($p);
241 95         253 return 0;
242             }
243             elsif($c eq '#'){
244 0         0 $s=_comment($p);
245 0         0 return 0;
246             }
247             elsif($c eq 'w'){
248 127         204 $s = _word($p);
249             }
250             else{
251             # print "($c)" if defined $c;
252 0         0 return undef;
253             }
254              
255 131 50       274 if (defined $s) { # what if $s=""?
256             # print $s;
257 131         218 _addNode($p,$s);
258 131         148 $p->{level}++;
259             # $p->{allowgoback}=1;
260 131         362 return 1;
261             }
262            
263             #should never reach here.
264 0 0       0 if(! _newline($p)){
265 0         0 print "Strange? not newline, what else could it be?\n";
266             }
267 0         0 return 0;
268             }
269              
270             sub _newline
271             {
272 204     204   232 my $p = shift;
273 204         291 my $c = _getc($p);
274             # print "newline $p->{ix} [$c]\n";
275 204 100       417 if (!defined $c ) { return 0; };
  3         8  
276 201 100       374 if ( $c eq "\n" ) { return 1; }
  104         214  
277 97         155 _ungetc($p);
278 97         209 return 0;
279             }
280              
281             sub _eos
282             {
283 472     472   494 my $p = shift;
284 472 100       904 if ($p->{ix} == -1) { return 1; }
  3         9  
285 469         1132 return 0;
286             }
287              
288             sub _space
289             {
290 284     284   296 my $p = shift;
291 284         356 my $lines = $p->{text};
292 284         332 my $ix = $p->{ix};
293 284         276 my $c;
294 284         266 my $n=0;
295            
296 284         469 while (_isSpaceChar($c = _getc($p))) {
297 638         1182 $n++;
298             }
299            
300 284         491 _ungetc($p);
301              
302             # print "space [$n]\n";
303 284         522 return $n;
304             }
305              
306              
307             sub _comment{#it will take out the new line as well
308             # print "trying comment\n";
309 0     0   0 my $p=$_[0];
310 0 0       0 if(_eos($p)){return undef;}
  0         0  
311 0         0 my $s="";
312 0         0 my $c=_getc($p);
313 0 0       0 if($c eq '#'){
314 0         0 $c=_getc($p);
315 0 0 0     0 if($c ne ' ' && $c ne "\t"){_ungetc($p);_ungetc($p);return undef;}
  0         0  
  0         0  
  0         0  
316 0         0 while(defined $c){
317 0 0       0 if($c eq "\n"){
318             # _ungetc($p);
319 0         0 last;
320             }
321             else{
322 0         0 $s=$s.$c;
323             }
324 0         0 $c=_getc($p);
325             }
326             }
327 0         0 else{ _ungetc($p);return undef;}
  0         0  
328             # print $s;
329 0         0 return $s;
330             }
331             #start with a quote with the beginning quote stripped
332             sub _quoted
333             {
334 3     3   6 my ($p, $term) = @_;
335 3         6 my $lines = $p->{text};
336 3         7 my $ix = $p->{ix};
337 3         4 my $s="";
338 3         4 my $cprev;
339             my $c;
340            
341 3         8 $term=_getc($p);
342             #Omited checking. Make sure its a quote before entering.
343             # what is the last current line indentation
344 3         8 my $i = $p->{indentation}[$p->{line_level}];
345 3         3 $i++;#the '"' itself increases the increases by 1
346 3         4 my $strip=$i;
347 3   33     17 while ( ($c = _getc($p)) || ($c eq '0')) {
348 2503 100 100     5870 if ( ($c eq $term) && !($cprev eq '\\')) {
349 3         8 last;
350             }
351             else {
352 2500 100       4251 if($c eq $term){chop $s;} # chop off the '\'
  6         10  
353 2500         2346 $cprev=$c;
354 2500 100 100     6474 if($strip<$i && _isSpaceChar($c)){ #continue strip indentation of $s
    100          
    50          
355 154         285 $strip++;
356             }
357             elsif($strip==$i){ #finished strip, use both $s and $s2
358 2343         3191 $s=$s.$c;
359 2343 100       5643 if($c eq "\n"){#new line, start strip for the next line
360 33         32 $strip=0;
361 33 50       84 if($cprev eq '\\'){chop;chop;} #the line continuation
  0         0  
  0         0  
362             }
363             }
364             elsif($strip<$i){ #indentation decreased
365             # print "indentation reduced from $i to $strip, current s=[$s]\n";
366 3 50       9 if($c ne "\n"){$i=$strip;}
  0         0  
  3         7  
367             else{$strip=0;}
368 3         13 $s=$s.$c;
369             }
370             }
371             }
372 3         15 return $s;
373             }
374              
375             sub _block
376             {
377 1     1   1 my $p = $_[0];
378 1         2 my $c;
379            
380             # what is the last current line indentation
381 1         2 my $i = $p->{indentation}[$p->{line_level}];
382 1         2 my $s;
383 1         1 my $is=-1;
384 1         1 my $j;
385 1         3 _getc($p);_newline($p); #omited checking. Make sure it is a block before entering.
  1         2  
386 1         2 while (1) {
387 15         24 $j = _space($p);
388             # print "j=$j [" . substr ($p->{text},$p->{ix},5) . "]" ;
389             # if less indented and not empty, exit
390 15 100       31 if ($j <= $i) {
391 3 50       7 if ( _eos($p) ) { last; }
  0         0  
392 3 100       6 if ( ! _newline($p) ) { last; }
  1         2  
393 2         60 else { _ungetc($p); } # in CRLF combinations the CR is lost: that's ok !
394             }
395            
396 14 100       18 if ($is == -1) { $is = $j; }
  1         1  
397 14 50       23 if ( $j > $is ) { $p->{ix} -= ($j-$is); } # XXX ungetting extra spaces
  0         0  
398            
399 14         20 while ( $c = _getc($p) ) { #get a line, that is ended in \n
400 782 50       1289 if ($c ne "\r") { $s = $s . $c; }
  782         720  
401 782 100       1517 if ($c eq "\n") { last; }
  14         25  
402             }
403              
404 14 50       19 if ( _eos($p) ) { last; }
  0         0  
405             }
406              
407 1 50       9 if ( ! _eos($p) ){
408             # $p->{ix} -= $j; # XXX unget spaces
409 1         3 while (! _newline($p)){_ungetc($p);}_ungetc($p);#return to the end of last line so _node can return properly
  1         3  
  1         3  
410             }
411 1         3 $c=chop $s;
412 1         4 while($c=~/[\n \t]/){$c=chop $s;}
  2         6  
413 1         2 $s=$s.$c;
414 1         3 return $s;
415             }
416              
417             sub _nextSpace{
418 0     0   0 my $p=shift;
419 0         0 my $next=substr($p->{"text"},$p->{"ix"},1);
420 0 0 0     0 if(defined $next && $next eq ' ' || $next eq '\t'){
      0        
421 0         0 return 1;
422             }
423             else {
424 0         0 return 0;
425             }
426             }
427             sub _nextWord{
428 369     369   418 my $p=shift;
429 369         598 my $next=substr($p->{"text"},$p->{"ix"},2);
430 369 100 66     1744 if(defined $next && $next!~/^([ \t\r\n\(\);,]|# )/ ){
431 242         569 return 1;
432             }
433             else {
434 127         319 return 0;
435             }
436             }
437             sub _peek{
438 261     261   247 my $p=shift;
439 261         541 my $next=substr($p->{"text"},$p->{"ix"},2);
440 261 50       472 if(defined $next){
441 261 100       679 if($next=~/^(['"\(\);,]|# |\\[\r\n])/ ){
442 39         91 return substr($next,0,1);
443             }
444 222 50       455 if($next=~/^[ \t]/ ){
445 0         0 return " ";
446             }
447 222 100       463 if($next=~/^[\r\n]/ ){
448 95         205 return "\n";
449             }
450             else{
451 127         256 return 'w';
452             }
453             }
454 0         0 return undef;
455             }
456              
457             sub _word
458             {
459 127     127   130 my $p = shift;
460 127         179 my $lines = $p->{text};
461 127         141 my $ix = $p->{ix};
462 127         161 my $c;
463 127         146 my $s="";
464            
465             # print "word at ix=$ix?\n";
466 127         192 while(_nextWord($p)){
467 242         393 $s=$s. _getc($p);
468             }
469             # while (_isWordChar($c = _getc($p))) {
470             # $s = $s . $c;
471             # }
472             # _ungetc($p);
473 127         283 return $s;
474             }
475              
476             sub _isSpaceChar
477             {
478 1079 100   1079   2002 if(defined $_[0]){
479 1076 100       2693 if ($_[0] =~ /[ \t]/) { return 1; }
  792         1950  
480             }
481 287         648 return 0;
482             }
483              
484             sub _isWordChar
485             {
486 0 0   0   0 if(defined $_[0]){
487 0 0       0 if ($_[0] =~ /[ \t\n\r\(\),;]/) { return 0; }
  0         0  
488             }
489             else{
490 0         0 return 0;
491             }
492 0         0 return 1;
493             }
494              
495             sub _getc
496             {
497 4692     4692   4899 my $p = shift;
498 4692         5571 my $lines = $p->{text};
499 4692         5253 my $ix = $p->{ix};
500            
501 4692 100       7689 if ($ix == -1) { return undef; }
  3         5  
502            
503 4689 100       7693 if ($ix >= length($lines) ) {
504 3         6 $p->{ix} = -1;
505 3         7 return undef;
506             }
507              
508 4686         5466 my $c = substr($lines,$ix,1);
509 4686         4912 $p->{ix}++;
510             # print $c;
511 4686         12941 return $c;
512             }
513              
514             sub _ungetc
515             {
516 385     385   439 my $p = shift;
517 385 100       765 if ( $p->{ix} == -1 ) { return; }
  3         5  
518 382         575 $p->{ix}--;
519             # print "ungetc " . $p->{ix} . "\n";
520             }
521              
522             sub _fatal
523             {
524 0     0   0 print $_[0];
525 0         0 exit(1);
526             }
527              
528             sub _addNode
529             {
530 131     131   150 my $p = shift;
531 131         158 my $s = shift;
532              
533 131 50       268 unless ( $p->{g} ) {
534 0         0 $p->{g}[0] = OGDL::Graph->new("stream");
535             }
536            
537 131         371 my $g2 = OGDL::Graph->new($s);
538 131         440 $p->{g}[$p->{level}]->addNode($g2);
539 131         297 $p->{g}[$p->{level}+1] = $g2;
540             }
541              
542             1;
543             __END__