File Coverage

blib/lib/Parse/Eyapp/Node.pm
Criterion Covered Total %
statement 252 419 60.1
branch 83 190 43.6
condition 18 80 22.5
subroutine 37 58 63.7
pod 19 28 67.8
total 409 775 52.7


line stmt bran cond sub pod time code
1             # (c) Parse::Eyapp Copyright 2006-2008 Casiano Rodriguez-Leon, all rights reserved.
2             package Parse::Eyapp::Node;
3 64     64   3414 use strict;
  64         140  
  64         2779  
4 64     64   376 use Carp;
  64         130  
  64         7132  
5 64     64   394 no warnings 'recursion';
  64         145  
  64         3476  
6              
7 64     64   57907 use Parse::Eyapp::YATW;
  64         224  
  64         8393  
8 64     64   638 use List::Util qw(first);
  64         151  
  64         6538  
9 64     64   374 use Data::Dumper;
  64         140  
  64         103563  
10              
11             our $FILENAME=__FILE__;
12              
13             sub firstval(&@) {
14 2     2 0 3 my $handler = shift;
15            
16 2         7 return (grep { $handler->($_) } @_)[0]
  1         3  
17             }
18              
19             sub lastval(&@) {
20 7     7 0 10 my $handler = shift;
21            
22 7         11 return (grep { $handler->($_) } @_)[-1]
  5         12  
23             }
24              
25             ####################################################################
26             # Usage :
27             # line: %name PROG
28             # exp <%name EXP + ';'>
29             # { @{$lhs->{t}} = map { $_->{t}} ($lhs->child(0)->children()); }
30             # ;
31             # Returns : The array of children of the node. When the tree is a
32             # translation scheme the CODE references are also included
33             # Parameters : the node (method)
34             # See Also : Children
35              
36             sub children {
37 1933     1933 1 9119 my $self = CORE::shift;
38            
39 1933 50       6601 return () unless UNIVERSAL::can($self, 'children');
40 1933 50       4351 @{$self->{children}} = @_ if @_;
  0         0  
41 1933         3071 @{$self->{children}}
  1933         8317  
42             }
43              
44             ####################################################################
45             # Usage : line: %name PROG
46             # (exp) <%name EXP + ';'>
47             # { @{$lhs->{t}} = map { $_->{t}} ($_[1]->Children()); }
48             #
49             # Returns : The true children of the node, excluding CODE CHILDREN
50             # Parameters : The Node object
51              
52             sub Children {
53 128     128 1 168 my $self = CORE::shift;
54            
55 128 50       395 return () unless UNIVERSAL::can($self, 'children');
56              
57 128 50       271 @{$self->{children}} = @_ if @_;
  0         0  
58 128         143 grep { !UNIVERSAL::isa($_, 'CODE') } @{$self->{children}}
  386         1728  
  128         240  
59             }
60              
61             ####################################################################
62             # Returns : Last non CODE child
63             # Parameters : the node object
64              
65             sub Last_child {
66 1     1 1 15 my $self = CORE::shift;
67              
68 1 50 33     8 return unless UNIVERSAL::can($self, 'children') and @{$self->{children}};
  1         5  
69 1         3 my $i = -1;
70 1   33     13 $i-- while defined($self->{children}->[$i]) and UNIVERSAL::isa($self->{children}->[$i], 'CODE');
71 1         8 return $self->{children}->[$i];
72             }
73              
74             sub last_child {
75 0     0 1 0 my $self = CORE::shift;
76              
77 0 0 0     0 return unless UNIVERSAL::can($self, 'children') and @{$self->{children}};
  0         0  
78 0         0 ${$self->{children}}[-1];
  0         0  
79             }
80              
81             ####################################################################
82             # Usage : $node->child($i)
83             # my $transform = Parse::Eyapp::Treeregexp->new( STRING => q{
84             # commutative_add: PLUS($x, ., $y, .)
85             # => { my $t = $x; $_[0]->child(0, $y); $_[0]->child(2, $t)}
86             # }
87             # Purpose : Setter-getter to modify a specific child of a node
88             # Returns : Child with index $i. Returns undef if the child does not exists
89             # Parameters : Method: the node and the index of the child. The new value is used
90             # as a setter.
91             # Throws : Croaks if the index parameter is not provided
92             sub child {
93 1291     1291 1 2689 my ($self, $index, $value) = @_;
94            
95             #croak "$self is not a Parse::Eyapp::Node" unless $self->isa('Parse::Eyapp::Node');
96 1291 50       12565 return undef unless UNIVERSAL::can($self, 'child');
97 1291 50       2556 croak "Index not provided" unless defined($index);
98 1291 50       2568 $self->{children}[$index] = $value if defined($value);
99 1291         23337 $self->{children}[$index];
100             }
101              
102             sub descendant {
103 0     0 1 0 my $self = shift;
104 0         0 my $coord = shift;
105              
106 0         0 my @pos = split /\./, $coord;
107 0         0 my $t = $self;
108 0         0 my $x = shift(@pos); # discard the first empty dot
109 0         0 for (@pos) {
110 0 0 0     0 croak "Error computing descendant: $_ is not a number\n"
111             unless m{\d+} and $_ < $t->children;
112 0         0 $t = $t->child($_);
113             }
114 0         0 return $t;
115             }
116              
117             ####################################################################
118             # Usage : $node->s(@transformationlist);
119             # Example : The following example simplifies arithmetic expressions
120             # using method "s":
121             # > cat Timeszero.trg
122             # /* Operator "and" has higher priority than comma "," */
123             # whatever_times_zero: TIMES(@b, NUM($x) and { $x->{attr} == 0 }) => { $_[0] = $NUM }
124             #
125             # > treereg Timeszero
126             # > cat arrays.pl
127             # !/usr/bin/perl -w
128             # use strict;
129             # use Rule6;
130             # use Parse::Eyapp::Treeregexp;
131             # use Timeszero;
132             #
133             # my $parser = new Rule6();
134             # my $t = $parser->Run;
135             # $t->s(@Timeszero::all);
136             #
137             #
138             # Returns : Nothing
139             # Parameters : The object (is a method) and the list of transformations to apply.
140             # The list may be a list of Parse::Eyapp:YATW objects and/or CODE
141             # references
142             # Throws : No exceptions
143             # Comments : The set of transformations is repeatedly applied to the node
144             # until there are no changes.
145             # The function may hang if the set of transformations
146             # matches forever.
147             # See Also : The "s" method for Parse::Eyapp::YATW objects
148             # (i.e. transformation objects)
149              
150             sub s {
151 24     24 0 747 my @patterns = @_[1..$#_];
152              
153             # Make them Parse::Eyapp:YATW objects if they are CODE references
154 24 100       75 @patterns = map { ref($_) eq 'CODE'?
  41         208  
155             Parse::Eyapp::YATW->new(
156             PATTERN => $_,
157             #PATTERN_ARGS => [],
158             )
159             :
160             $_
161             }
162             @patterns;
163 24         51 my $changes;
164 24         52 do {
165 50         81 $changes = 0;
166 50         109 foreach (@patterns) {
167 87         213 $_->{CHANGES} = 0;
168 87         324 $_->s($_[0]);
169 87         420 $changes += $_->{CHANGES};
170             }
171             } while ($changes);
172             }
173              
174              
175             ####################################################################
176             # Usage : ????
177             # Purpose : bud = Bottom Up Decoration: Decorates the tree with flowers :-)
178             # The purpose is to decorate the AST with attributes during
179             # the context-dependent analysis, mainly type-checking.
180             # Returns : ????
181             # Parameters : The transformations.
182             # Throws : no exceptions
183             # Comments : The tree is traversed bottom-up. The set of
184             # transformations is applied to each node in the order
185             # supplied by the user. As soon as one succeeds
186             # no more transformations are applied.
187             # See Also : n/a
188             # To Do : Avoid closure. Save @patterns inside the object
189             {
190             my @patterns;
191              
192             sub bud {
193 0     0 1 0 @patterns = @_[1..$#_];
194              
195 0 0       0 @patterns = map { ref($_) eq 'CODE'?
  0         0  
196             Parse::Eyapp::YATW->new(
197             PATTERN => $_,
198             #PATTERN_ARGS => [],
199             )
200             :
201             $_
202             }
203             @patterns;
204 0         0 _bud($_[0], undef, undef);
205             }
206              
207             sub _bud {
208 0     0   0 my $node = $_[0];
209 0         0 my $index = $_[2];
210              
211             # Is an odd leaf. Not actually a Parse::Eyapp::Node. Decorate it and leave
212 0 0 0     0 if (!ref($node) or !UNIVERSAL::can($node, "children")) {
213 0         0 for my $p (@patterns) {
214 0 0       0 return if $p->pattern->(
215             $_[0], # Node being visited
216             $_[1], # Father of this node
217             $index, # Index of this node in @Father->children
218             $p, # The YATW pattern object
219             );
220             }
221             };
222              
223             # Recursively decorate subtrees
224 0         0 my $i = 0;
225 0         0 for (@{$node->{children}}) {
  0         0  
226 0         0 $_->_bud($_, $_[0], $i);
227 0         0 $i++;
228             }
229              
230             # Decorate the node
231             #Change YATW object to be the first argument?
232 0         0 for my $p (@patterns) {
233 0 0       0 return if $p->pattern->($_[0], $_[1], $index, $p);
234             }
235             }
236             } # closure for @patterns
237              
238             ####################################################################
239             # Usage :
240             # @t = Parse::Eyapp::Node->new( q{TIMES(NUM(TERMINAL), NUM(TERMINAL))},
241             # sub {
242             # our ($TIMES, @NUM, @TERMINAL);
243             # $TIMES->{type} = "binary operation";
244             # $NUM[0]->{type} = "int";
245             # $NUM[1]->{type} = "float";
246             # $TERMINAL[1]->{attr} = 3.5;
247             # },
248             # );
249             # Purpose : Multi-Constructor
250             # Returns : Array of pointers to the objects created
251             # in scalar context a pointer to the first node
252             # Parameters : The class plus the string description and attribute handler
253              
254             {
255              
256             my %cache;
257              
258             sub m_bless {
259              
260 0     0 0 0 my $key = join "",@_;
261 0         0 my $class = shift;
262 0 0       0 return $cache{$key} if exists $cache{$key};
263              
264 0         0 my $b = bless { children => \@_}, $class;
265 0         0 $cache{$key} = $b;
266              
267 0         0 return $b;
268             }
269             }
270              
271             sub _bless {
272 152     152   195 my $class = shift;
273              
274 152         601 my $b = bless { children => \@_ }, $class;
275 152         336 return $b;
276             }
277              
278             sub hexpand {
279 0     0 1 0 my $class = CORE::shift;
280              
281 0 0       0 my $handler = CORE::pop if ref($_[-1]) eq 'CODE';
282 0         0 my $n = m_bless(@_);
283              
284 0         0 my $newnodeclass = CORE::shift;
285              
286 64     64   505 no strict 'refs';
  64         136  
  64         69085  
287 0 0       0 push @{$newnodeclass."::ISA"}, 'Parse::Eyapp::Node' unless $newnodeclass->isa('Parse::Eyapp::Node');
  0         0  
288              
289 0 0 0     0 if (defined($handler) and UNIVERSAL::isa($handler, "CODE")) {
290 0         0 $handler->($n);
291             }
292              
293 0         0 $n;
294             }
295              
296             sub hnew {
297 0     0 1 0 my $blesser = \&m_bless;
298              
299 0         0 return _new($blesser, @_);
300             }
301              
302             # Regexp for a full Perl identifier
303             sub _new {
304 61     61   90 my $blesser = CORE::shift;
305 61         86 my $class = CORE::shift;
306 61         93 local $_ = CORE::shift; # string: tree description
307 61 100       213 my $handler = CORE::shift if ref($_[0]) eq 'CODE';
308              
309              
310 61         80 my %classes;
311             my $b;
312             #TODO: Shall I receive a prefix?
313              
314 0         0 my (@stack, @index, @results, %results, @place, $open);
315             #skip white spaces
316 61         145 s{\A\s+}{};
317 61         136 while ($_) {
318             # If is a leaf is followed by parenthesis or comma or an ID
319             s{\A([A-Za-z_][A-Za-z0-9_:]*)\s*([),])}
320             {$1()$2} # ... then add an empty pair of parenthesis
321 551 100       1725 and do {
322 51         115 next;
323             };
324              
325             # If is a leaf is followed by an ID
326             s{\A([A-Za-z_][A-Za-z0-9_:]*)\s+([A-Za-z_])}
327             {$1()$2} # ... then add an empty pair of parenthesis
328 500 50       1237 and do {
329 0         0 next;
330             };
331              
332             # If is a leaf at the end
333             s{\A([A-Za-z_][A-Za-z0-9_:]*)\s*$}
334             {$1()} # ... then add an empty pair of parenthesis
335 500 100       1385 and do {
336 18         43 $classes{$1} = 1;
337 18         41 next;
338             };
339              
340             # Is an identifier
341             s{\A([A-Za-z_][A-Za-z0-9_:]*)}{}
342 482 100       1287 and do {
343 152         352 $classes{$1} = 1;
344 152         313 CORE::push @stack, $1;
345 152         330 next;
346             };
347              
348             # Open parenthesis: mark the position for when parenthesis closes
349             s{\A[(]}{}
350 330 100       913 and do {
351 152         198 my $pos = scalar(@stack);
352 152         240 CORE::push @index, $pos;
353 152         248 $place[$pos] = $open++;
354              
355             # Warning! I don't know what I am doing
356 152         357 next;
357             };
358              
359             # Skip commas
360 178 100       742 s{\A,}{} and next;
361              
362             # Closing parenthesis: time to build a node
363 163 100       566 s{\A[)]}{} and do {
364 152 50       319 croak "Syntax error! Closing parenthesis has no left partner!" unless @index;
365 152         193 my $begin = pop @index; # check if empty!
366 152         695 my @children = splice(@stack, $begin);
367 152         183 my $class = pop @stack;
368 152 50 33     924 croak "Syntax error! Any couple of parenthesis must be preceded by an identifier"
369             unless (defined($class) and $class =~ m{^[a-zA-Z_][\w:]*$});
370              
371 152         412 $b = $blesser->($class, @children);
372              
373 152         193 CORE::push @stack, $b;
374 152         226 $results[$place[$begin]] = $b;
375 152         157 CORE::push @{$results{$class}}, $b;
  152         348  
376 152         416 next;
377             };
378              
379 11 50       22 last unless $_;
380              
381             #skip white spaces
382 11 50       55 croak "Error building Parse::Eyapp::Node tree at '$_'." unless s{\A\s+}{};
383             } # while
384 61 50       143 croak "Syntax error! Open parenthesis has no right partner!" if @index;
385             {
386 64     64   472 no strict 'refs';
  64         3286  
  64         246225  
  61         88  
387 61         164 for (keys(%classes)) {
388 144 100       1319 push @{$_."::ISA"}, 'Parse::Eyapp::Node' unless $_->isa('Parse::Eyapp::Node');
  16         293  
389             }
390             }
391 61 100 66     277 if (defined($handler) and UNIVERSAL::isa($handler, "CODE")) {
392 10         38 $handler->(@results);
393             }
394 61 100       967 return wantarray? @results : $b;
395             }
396              
397             sub new {
398 61     61 1 13741 my $blesser = \&_bless;
399              
400 61         168 _new($blesser, @_);
401             }
402              
403             ## Used by _subtree_list
404             #sub compute_hierarchy {
405             # my @results = @{shift()};
406             #
407             # # Compute the hierarchy
408             # my $b;
409             # my @r = @results;
410             # while (@results) {
411             # $b = pop @results;
412             # my $d = $b->{depth};
413             # my $f = lastval { $_->{depth} < $d} @results;
414             #
415             # $b->{father} = $f;
416             # $b->{children} = [];
417             # unshift @{$f->{children}}, $b;
418             # }
419             # $_->{father} = undef for @results;
420             # bless $_, "Parse::Eyapp::Node::Match" for @r;
421             # return @r;
422             #}
423              
424             # Matches
425              
426             sub m {
427 3     3 0 23 my $self = shift;
428 3 50       12 my @patterns = @_ or croak "Expected a pattern!";
429             croak "Error in method m of Parse::Eyapp::Node. Expected Parse::Eyapp:YATW patterns"
430 3 50   3   19 unless $a = first { !UNIVERSAL::isa($_, "Parse::Eyapp:YATW") } @_;
  3         23  
431              
432             # array context: return all matches
433 3         9 local $a = 0;
434 3         5 my %index = map { ("$_", $a++) } @patterns;
  9         31  
435 3         19 my @stack = (
436             Parse::Eyapp::Node::Match->new(
437             node => $self,
438             depth => 0,
439             dewey => "",
440             patterns =>[]
441             )
442             );
443 3         4 my @results;
444 3         6 do {
445 27         31 my $mn = CORE::shift(@stack);
446 27         104 my %n = %$mn;
447              
448             # See what patterns do match the current $node
449 27         42 for my $pattern (@patterns) {
450 81 100       1326 push @{$mn->{patterns}}, $index{$pattern} if $pattern->{PATTERN}($n{node});
  16         96  
451             }
452 27         212 my $dewey = $n{dewey};
453 27 100       25 if (@{$mn->{patterns}}) {
  27         60  
454 7         13 $mn->{family} = \@patterns;
455              
456             # Is at this time that I have to compute the father
457 7     5   25 my $f = lastval { $dewey =~ m{^$_->{dewey}}} @results;
  5         39  
458 7         23 $mn->{father} = $f;
459             # ... and children
460 7 100       80 push @{$f->{children}}, $mn if defined($f);
  4         10  
461 7         8 CORE::push @results, $mn;
462             }
463 27         37 my $childdepth = $n{depth}+1;
464 27         23 my $k = -1;
465 24         25 CORE::unshift @stack,
466             map
467             {
468 27         73 $k++;
469 24         81 Parse::Eyapp::Node::Match->new(
470             node => $_,
471             depth => $childdepth,
472             dewey => "$dewey.$k",
473             patterns => []
474             )
475             } $n{node}->children();
476             } while (@stack);
477              
478 3 50       21 wantarray? @results : $results[0];
479             }
480              
481             #sub _subtree_scalar {
482             # # scalar context: return iterator
483             # my $self = CORE::shift;
484             # my @patterns = @_ or croak "Expected a pattern!";
485             #
486             # # %index gives the index of $p in @patterns
487             # local $a = 0;
488             # my %index = map { ("$_", $a++) } @patterns;
489             #
490             # my @stack = ();
491             # my $mn = { node => $self, depth => 0, patterns =>[] };
492             # my @results = ();
493             #
494             # return sub {
495             # do {
496             # # See if current $node matches some patterns
497             # my $d = $mn->{depth};
498             # my $childdepth = $d+1;
499             # # See what patterns do match the current $node
500             # for my $pattern (@patterns) {
501             # push @{$mn->{patterns}}, $index{$pattern} if $pattern->{PATTERN}($mn->{node});
502             # }
503             #
504             # if (@{$mn->{patterns}}) { # matched
505             # CORE::push @results, $mn;
506             #
507             # # Compute the hierarchy
508             # my $f = lastval { $_->{depth} < $d} @results;
509             # $mn->{father} = $f;
510             # $mn->{children} = [];
511             # $mn->{family} = \@patterns;
512             # unshift @{$f->{children}}, $mn if defined($f);
513             # bless $mn, "Parse::Eyapp::Node::Match";
514             #
515             # # push children in the stack
516             # CORE::unshift @stack,
517             # map { { node => $_, depth => $childdepth, patterns => [] } }
518             # $mn->{node}->children();
519             # $mn = CORE::shift(@stack);
520             # return $results[-1];
521             # }
522             # # didn't match: push children in the stack
523             # CORE::unshift @stack,
524             # map { { node => $_, depth => $childdepth, patterns => [] } }
525             # $mn->{node}->children();
526             # $mn = CORE::shift(@stack);
527             # } while ($mn); # May be the stack is empty now, but if $mn then there is a node to process
528             # # reset iterator
529             # my @stack = ();
530             # my $mn = { node => $self, depth => 0, patterns =>[] };
531             # return undef;
532             # };
533             #}
534              
535             # Factorize this!!!!!!!!!!!!!!
536             #sub m {
537             # goto &_subtree_list if (wantarray());
538             # goto &_subtree_scalar;
539             #}
540              
541             ####################################################################
542             # Usage : $BLOCK->delete($ASSIGN)
543             # $BLOCK->delete(2)
544             # Purpose : deletes the specified child of the node
545             # Returns : The deleted child
546             # Parameters : The object plus the index or pointer to the child to be deleted
547             # Throws : If the object can't do children or has no children
548             # See Also : n/a
549              
550             sub delete {
551 3     3 1 52 my $self = CORE::shift; # The tree object
552 3         10 my $child = CORE::shift; # index or pointer
553              
554 3 50 33     32 croak "Parse::Eyapp::Node::delete error, node:\n"
555             .Parse::Eyapp::Node::str($self)."\ndoes not have children"
556             unless UNIVERSAL::can($self, 'children') and ($self->children()>0);
557 3 50       17 if (ref($child)) {
558 3         6 my $i = 0;
559 3         10 for ($self->children()) {
560 6 100       23 last if $_ == $child;
561 3         7 $i++;
562             }
563 3 50       415 if ($i == $self->children()) {
564 0         0 warn "Parse::Eyapp::Node::delete warning: node:\n".Parse::Eyapp::Node::str($self)
565             ."\ndoes not have a child like:\n"
566             .Parse::Eyapp::Node::str($child)
567             ."\nThe node was not deleted!\n";
568 0         0 return $child;
569             }
570 3         8 splice(@{$self->{children}}, $i, 1);
  3         14  
571 3         12 return $child;
572             }
573 0         0 my $numchildren = $self->children();
574 0 0 0     0 croak "Parse::Eyapp::Node::delete error: expected an index between 0 and ".
575             ($numchildren-1).". Got $child" unless ($child =~ /\d+/ and $child < $numchildren);
576 0         0 splice(@{$self->{children}}, $child, 1);
  0         0  
577 0         0 return $child;
578             }
579              
580             ####################################################################
581             # Usage : $BLOCK->shift
582             # Purpose : deletes the first child of the node
583             # Returns : The deleted child
584             # Parameters : The object
585             # Throws : If the object can't do children
586             # See Also : n/a
587              
588             sub shift {
589 0     0 0 0 my $self = CORE::shift; # The tree object
590              
591 0 0       0 croak "Parse::Eyapp::Node::shift error, node:\n"
592             .Parse::Eyapp::Node->str($self)."\ndoes not have children"
593             unless UNIVERSAL::can($self, 'children');
594              
595 0         0 return CORE::shift(@{$self->{children}});
  0         0  
596             }
597              
598             sub unshift {
599 0     0 1 0 my $self = CORE::shift; # The tree object
600 0         0 my $node = CORE::shift; # node to insert
601              
602 0         0 CORE::unshift @{$self->{children}}, $node;
  0         0  
603             }
604              
605             sub push {
606 0     0 1 0 my $self = CORE::shift; # The tree object
607             #my $node = CORE::shift; # node to insert
608              
609             #CORE::push @{$self->{children}}, $node;
610 0         0 CORE::push @{$self->{children}}, @_;
  0         0  
611             }
612              
613             sub insert_before {
614 2     2 1 22 my $self = CORE::shift; # The tree object
615 2         5 my $child = CORE::shift; # index or pointer
616 2         4 my $node = CORE::shift; # node to insert
617              
618 2 50 33     19 croak "Parse::Eyapp::Node::insert_before error, node:\n"
619             .Parse::Eyapp::Node::str($self)."\ndoes not have children"
620             unless UNIVERSAL::can($self, 'children') and ($self->children()>0);
621              
622 2 50       399 if (ref($child)) {
623 2         5 my $i = 0;
624 2         10 for ($self->children()) {
625 6 100       18 last if $_ == $child;
626 4         5 $i++;
627             }
628 2 50       10 if ($i == $self->children()) {
629 0         0 warn "Parse::Eyapp::Node::insert_before warning: node:\n"
630             .Parse::Eyapp::Node::str($self)
631             ."\ndoes not have a child like:\n"
632             .Parse::Eyapp::Node::str($child)."\nThe node was not inserted!\n";
633 0         0 return $child;
634             }
635 2         5 splice(@{$self->{children}}, $i, 0, $node);
  2         7  
636 2         14 return $node;
637             }
638 0         0 my $numchildren = $self->children();
639 0 0 0     0 croak "Parse::Eyapp::Node::insert_before error: expected an index between 0 and ".
640             ($numchildren-1).". Got $child" unless ($child =~ /\d+/ and $child < $numchildren);
641 0         0 splice(@{$self->{children}}, $child, 0, $node);
  0         0  
642 0         0 return $child;
643             }
644              
645             sub insert_after {
646 0     0 1 0 my $self = CORE::shift; # The tree object
647 0         0 my $child = CORE::shift; # index or pointer
648 0         0 my $node = CORE::shift; # node to insert
649              
650 0 0 0     0 croak "Parse::Eyapp::Node::insert_after error, node:\n"
651             .Parse::Eyapp::Node::str($self)."\ndoes not have children"
652             unless UNIVERSAL::can($self, 'children') and ($self->children()>0);
653              
654 0 0       0 if (ref($child)) {
655 0         0 my $i = 0;
656 0         0 for ($self->children()) {
657 0 0       0 last if $_ == $child;
658 0         0 $i++;
659             }
660 0 0       0 if ($i == $self->children()) {
661 0         0 warn "Parse::Eyapp::Node::insert_after warning: node:\n"
662             .Parse::Eyapp::Node::str($self).
663             "\ndoes not have a child like:\n"
664             .Parse::Eyapp::Node::str($child)."\nThe node was not inserted!\n";
665 0         0 return $child;
666             }
667 0         0 splice(@{$self->{children}}, $i+1, 0, $node);
  0         0  
668 0         0 return $node;
669             }
670 0         0 my $numchildren = $self->children();
671 0 0 0     0 croak "Parse::Eyapp::Node::insert_after error: expected an index between 0 and ".
672             ($numchildren-1).". Got $child" unless ($child =~ /\d+/ and $child < $numchildren);
673 0         0 splice(@{$self->{children}}, $child+1, 0, $node);
  0         0  
674 0         0 return $child;
675             }
676              
677             { # $match closure
678              
679             my $match;
680              
681             sub clean_tree {
682 1     1 0 19 $match = pop;
683 1 50 33     10 croak "clean tree: a node and code reference expected" unless (ref($match) eq 'CODE') and (@_ > 0);
684 1         9 $_[0]->_clean_tree();
685             }
686              
687             sub _clean_tree {
688 6     6   7 my @children;
689            
690 6         28 for ($_[0]->children()) {
691 16 100 66     317 next if (!defined($_) or $match->($_));
692            
693 5         69 $_->_clean_tree();
694 5         37 CORE::push @children, $_;
695             }
696 6         43 $_[0]->{children} = \@children; # Bad code
697             }
698             } # $match closure
699              
700             ####################################################################
701             # Usage : $t->str
702             # Returns : Returns a string describing the Parse::Eyapp::Node as a term
703             # i.e., s.t. like: 'PROGRAM(FUNCTION(RETURN(TERMINAL,VAR(TERMINAL))))'
704             our @PREFIXES = qw(Parse::Eyapp::Node::);
705             our $INDENT = 0; # -1 new 0 = compact, 1 = indent, 2 = indent and include Types in closing parenthesis
706             our $STRSEP = ',';
707             our $DELIMITER = '[';
708             our $FOOTNOTE_HEADER = "\n---------------------------\n";
709             our $FOOTNOTE_SEP = ")\n";
710             our $FOOTNOTE_LEFT = '^{';
711             our $FOOTNOTE_RIGHT = '}';
712             our $LINESEP = 4;
713             our $CLASS_HANDLER = sub { type($_[0]) }; # What to print to identify the node
714              
715             my %match_del = (
716             '[' => ']',
717             '{' => '}',
718             '(' => ')',
719             '<' => '>'
720             );
721              
722             my $pair;
723             my $footnotes = '';
724             my $footnote_label;
725              
726             sub str {
727              
728 9     9 1 79 my @terms;
729              
730             # Consume arg only if called as a class method Parse::Eyap::Node->str($node1, $node2, ...)
731 9 50       38 CORE::shift unless ref($_[0]);
732              
733 9         33 for (@_) {
734 9         14 $footnote_label = 0;
735 9         36 $footnotes = '';
736             # Set delimiters for semantic values
737 9 50 33     393 if (defined($DELIMITER) and exists($match_del{$DELIMITER})) {
738 9         24 $pair = $match_del{$DELIMITER};
739             }
740             else {
741 0         0 $DELIMITER = $pair = '';
742             }
743 9         31 CORE::push @terms, _str($_).$footnotes;
744             }
745 9 50       88 return wantarray? @terms : $terms[0];
746             }
747              
748             sub _str {
749 98     98   123 my $self = CORE::shift; # root of the subtree
750 98   100     218 my $indent = (CORE::shift or 0); # current depth in spaces " "
751              
752 98         178 my @children = Parse::Eyapp::Node::children($self);
753 98         190 my @t;
754              
755             my $res;
756 98         112 my $fn = $footnote_label;
757 98 50 33     545 if ($INDENT >= 0 && UNIVERSAL::can($self, 'footnote')) {
758 0         0 $res = $self->footnote;
759 0 0       0 $footnotes .= $FOOTNOTE_HEADER.$footnote_label++.$FOOTNOTE_SEP.$res if $res;
760             }
761              
762             # recursively visit nodes
763 98         154 for (@children) {
764 89 50       293 CORE::push @t, Parse::Eyapp::Node::_str($_, $indent+2) if defined($_);
765             }
766 98         145 local $" = $STRSEP;
767 98         157 my $class = $CLASS_HANDLER->($self);
768 98         443 $class =~ s/^$_// for @PREFIXES;
769 98         124 my $information;
770 98 100 66     618 $information = $self->info if ($INDENT >= 0 && UNIVERSAL::can($self, 'info'));
771 98 100       554 $class .= $DELIMITER.$information.$pair if defined($information);
772 98 50 33     344 if ($INDENT >= 0 && $res) {
773 0         0 $class .= $FOOTNOTE_LEFT.$fn.$FOOTNOTE_RIGHT;
774             }
775              
776 98 50       158 if ($INDENT > 0) {
777 0         0 my $w = " "x$indent;
778 0         0 $class = "\n$w$class";
779 0 0       0 $class .= "(@t\n$w)" if @children;
780 0 0 0     0 $class .= " # ".$CLASS_HANDLER->($self) if ($INDENT > 1) and ($class =~ tr/\n/\n/>$LINESEP);
781             }
782             else {
783 98 100       317 $class .= "(@t)" if @children;
784             }
785 98         369 return $class;
786             }
787              
788             sub _dot {
789 0     0   0 my ($root, $number) = @_;
790              
791 0         0 my $type = $root->type();
792              
793 0         0 my $information;
794 0 0 0     0 $information = $root->info if ($INDENT >= 0 && $root->can('info'));
795 0         0 my $class = $CLASS_HANDLER->($root);
796 0 0       0 $class = qq{$class$DELIMITER$information$pair} if defined($information);
797              
798 0         0 my $dot = qq{ $number [label = <$class>];\n};
799              
800 0         0 my $k = 0;
801 0         0 my @dots = map { $k++; $_->_dot("$number$k") } $root->children;
  0         0  
  0         0  
802              
803 0         0 for($k = 1; $k <= $root->children; $k++) {;
804 0         0 $dot .= qq{ $number -> $number$k;\n};
805             }
806              
807 0         0 return $dot.join('',@dots);
808             }
809              
810             sub dot {
811 0     0 0 0 my $dot = $_[0]->_dot('0');
812 0         0 return << "EOGRAPH";
813             digraph G {
814             ordering=out
815              
816             $dot
817             }
818             EOGRAPH
819             }
820              
821             sub fdot {
822 0     0 0 0 my ($self, $file) = @_;
823              
824 0 0       0 if ($file) {
825 0 0       0 $file .= '.dot' unless $file =~ /\.dot$/;
826             }
827             else {
828 0         0 $file = $self->type().".dot";
829             }
830 0         0 open my $f, "> $file";
831 0         0 print $f $self->dot();
832 0         0 close($f);
833             }
834              
835             BEGIN {
836 64     64   915 my @dotFormats = qw{bmp canon cgimage cmap cmapx cmapx_np eps exr fig gd gd2 gif gv imap imap_np ismap jp2 jpe jpeg jpg pct pdf pict plain plain-ext png ps ps2 psd sgi svg svgz tga tif tiff tk vml vmlz vrml wbmp x11 xdot xlib};
837              
838 64         215 for my $format (@dotFormats) {
839            
840 64     64   817 no strict 'refs';
  64         155  
  64         11981  
841 2752         134217 *{'Parse::Eyapp::Node::'.$format} = sub {
842 0     0   0 my ($self, $file) = @_;
843            
844 0 0       0 $file = $self->type() unless defined($file);
845            
846 0         0 $self->fdot($file);
847            
848 0         0 $file =~ s/\.(dot|$format)$//;
849 0         0 my $dotfile = "$file.dot";
850 0         0 my $pngfile = "$file.$format";
851 0         0 my $err = qx{dot -T$format $dotfile -o $pngfile 2>&1};
852 0         0 return ($err, $?);
853             }
854 2752         13093 }
855             }
856              
857             sub translation_scheme {
858 258     258 1 995 my $self = CORE::shift; # root of the subtree
859 258         899 my @children = $self->children();
860 258         545 for (@children) {
861 371 100       2196 if (ref($_) eq 'CODE') {
    50          
862 126         559 $_->($self, $self->Children);
863             }
864             elsif (defined($_)) {
865 245         587 translation_scheme($_);
866             }
867             }
868             }
869              
870             sub type {
871 219     219 1 1416 my $type = ref($_[0]);
872              
873 219 50       419 if ($type) {
874 219 100       459 if (defined($_[1])) {
875 88         131 $type = $_[1];
876 88         411 Parse::Eyapp::Driver::BeANode($type);
877 88         170 bless $_[0], $type;
878             }
879 219         803 return $type
880             }
881 0         0 return 'Parse::Eyapp::Node::STRING';
882             }
883              
884             { # Tree "fuzzy" equality
885              
886             ####################################################################
887             # Usage : $t1->equal($t2, n => sub { return $_[0] == $_[1] })
888             # Purpose : Checks the equality between two AST
889             # Returns : 1 if equal, 0 if not 'equal'
890             # Parameters : Two Parse::Eyapp:Node nodes and a hash of comparison handlers.
891             # The keys of the hash are the attributes of the nodes. The value is
892             # a comparator function. The comparator for key $k receives the attribute
893             # for the nodes being visited and rmust return true if they are considered similar
894             # Throws : exceptions if the parameters aren't Parse::Eyapp::Nodes
895              
896             my %handler;
897              
898             # True if the two trees look similar
899             sub equal {
900 2 50   2 1 13 croak "Parse::Eyapp::Node::equal error. Expected two syntax trees \n" unless (@_ > 1);
901              
902 2         7 %handler = splice(@_, 2);
903 2         3 my $key = '';
904 2 50   1   10 defined($key=firstval {!UNIVERSAL::isa($handler{$_},'CODE') } keys %handler)
  1         7  
905             and
906             croak "Parse::Eyapp::Node::equal error. Expected a CODE ref for attribute $key\n";
907 2         11 goto &_equal;
908             }
909              
910             sub _equal {
911 4     4   5 my $tree1 = CORE::shift;
912 4         5 my $tree2 = CORE::shift;
913              
914             # Same type
915 4 50       14 return 0 unless ref($tree1) eq ref($tree2);
916              
917             # Check attributes via handlers
918 4         10 for (keys %handler) {
919             # Check for existence
920 1 50 33     11 return 0 if (exists($tree1->{$_}) && !exists($tree2->{$_}));
921 0 0 0     0 return 0 if (exists($tree2->{$_}) && !exists($tree1->{$_}));
922              
923             # Check for definition
924 0 0 0     0 return 0 if (defined($tree1->{$_}) && !defined($tree2->{$_}));
925 0 0 0     0 return 0 if (defined($tree2->{$_}) && !defined($tree1->{$_}));
926              
927             # Check for equality
928 0 0       0 return 0 unless $handler{$_}->($tree1->{$_}, $tree2->{$_});
929             }
930              
931             # Same number of children
932 3         4 my @children1 = @{$tree1->{children}};
  3         7  
933 3         4 my @children2 = @{$tree2->{children}};
  3         5  
934 3 50       7 return 0 unless @children1 == @children2;
935              
936             # Children must be similar
937 3         5 for (@children1) {
938 2         4 my $ch2 = CORE::shift @children2;
939 2 50       9 return 0 unless _equal($_, $ch2);
940             }
941 3         15 return 1;
942             }
943             }
944              
945             1;
946              
947             package Parse::Eyapp::Node::Match;
948             our @ISA = qw(Parse::Eyapp::Node);
949              
950             # A Parse::Eyapp::Node::Match object is a reference
951             # to a tree of Parse::Eyapp::Nodes that has been used
952             # in a tree matching regexp. You can think of them
953             # as the equivalent of $1 $2, ... in treeregexeps
954              
955             # The depth of the Parse::Eyapp::Node being referenced
956              
957             sub new {
958 125     125   150 my $class = shift;
959              
960 125         382 my $matchnode = { @_ };
961 125         237 $matchnode->{children} = [];
962 125         549 bless $matchnode, $class;
963             }
964              
965             sub depth {
966 0     0   0 my $self = shift;
967              
968 0         0 return $self->{depth};
969             }
970              
971             # The coordinates of the Parse::Eyapp::Node being referenced
972             sub coord {
973 0     0   0 my $self = shift;
974              
975 0         0 return $self->{dewey};
976             }
977              
978              
979             # The Parse::Eyapp::Node being referenced
980             sub node {
981 31     31   38 my $self = shift;
982              
983 31         91 return $self->{node};
984             }
985              
986             # The Parse::Eyapp::Node:Match that references
987             # the nearest ancestor of $self->{node} that matched
988             sub father {
989 31     31   129 my $self = shift;
990              
991 31         161 return $self->{father};
992             }
993            
994             # The patterns that matched with $self->{node}
995             # Indexes
996             sub patterns {
997 0     0     my $self = shift;
998              
999 0 0         @{$self->{patterns}} = @_ if @_;
  0            
1000 0           return @{$self->{patterns}};
  0            
1001             }
1002            
1003             # The original list of patterns that produced this match
1004             sub family {
1005 0     0     my $self = shift;
1006              
1007 0 0         @{$self->{family}} = @_ if @_;
  0            
1008 0           return @{$self->{family}};
  0            
1009             }
1010            
1011             # The names of the patterns that matched
1012             sub names {
1013 0     0     my $self = shift;
1014              
1015 0           my @indexes = $self->patterns;
1016 0           my @family = $self->family;
1017              
1018 0 0         return map { $_->{NAME} or "Unknown" } @family[@indexes];
  0            
1019             }
1020            
1021             sub info {
1022 0     0     my $self = shift;
1023              
1024 0           my $node = $self->node;
1025 0           my @names = $self->names;
1026 0           my $nodeinfo;
1027 0 0         if (UNIVERSAL::can($node, 'info')) {
1028 0           $nodeinfo = ":".$node->info;
1029             }
1030             else {
1031 0           $nodeinfo = "";
1032             }
1033 0           return "[".ref($self->node).":".$self->depth.":@names$nodeinfo]"
1034             }
1035              
1036             1;
1037              
1038