File Coverage

blib/lib/Parse/Eyapp/YATW.pm
Criterion Covered Total %
statement 132 141 93.6
branch 27 40 67.5
condition 4 6 66.6
subroutine 21 23 91.3
pod 5 15 33.3
total 189 225 84.0


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::YATW;
3 64     64   360 use strict;
  64         146  
  64         2241  
4 64     64   378 use warnings;
  64         125  
  64         1938  
5 64     64   328 use Carp;
  64         141  
  64         3666  
6 64     64   59294 use Data::Dumper;
  64         384049  
  64         5279  
7 64     64   1180 use List::Util qw(first);
  64         151  
  64         131178  
8              
9             sub firstval(&@) {
10 0     0 0 0 my $handler = shift;
11            
12 0         0 return (grep { $handler->($_) } @_)[0]
  0         0  
13             }
14              
15             sub lastval(&@) {
16 40     40 0 50 my $handler = shift;
17            
18 40         66 return (grep { $handler->($_) } @_)[-1]
  147         214  
19             }
20              
21             sub valid_keys {
22 64     64 0 239 my %valid_args = @_;
23              
24 64         254 my @valid_args = keys(%valid_args);
25 64         171 local $" = ", ";
26 64         398 return "@valid_args"
27             }
28              
29             sub invalid_keys {
30 63     63 0 280 my $valid_args = shift;
31 63         114 my $args = shift;
32              
33 63     122   557 return (first { !exists($valid_args->{$_}) } keys(%$args));
  122         459  
34             }
35              
36              
37             our $VERSION = $Parse::Eyapp::Driver::VERSION;
38              
39             our $FILENAME=__FILE__;
40              
41             # TODO: Check args. Typical args:
42             # 'CHANGES' => 0,
43             # 'PATTERN' => sub { "DUMMY" },
44             # 'NAME' => 'fold',
45             # 'PATTERN_ARGS' => [],
46             # 'PENDING_TASKS' => {},
47             # 'NODE' => []
48              
49             my %_new_yatw = (
50             PATTERN => 'CODE',
51             NAME => 'STRING',
52             );
53              
54             my $validkeys = valid_keys(%_new_yatw);
55              
56             sub new {
57 63     63 1 130 my $class = shift;
58 63         264 my %args = @_;
59              
60 63 50       269 croak "Error. Expected a code reference when building a tree walker. " unless (ref($args{PATTERN}) eq 'CODE');
61 63 50       262 if (defined($a = invalid_keys(\%_new_yatw, \%args))) {
62 0         0 croak("Parse::Eyapp::YATW::new Error!: unknown argument $a. Valid arguments are: $validkeys")
63             }
64              
65              
66             # obsolete, I have to delete this
67             #$args{PATTERN_ARGS} = [] unless (ref($args{PATTERN_ARGS}) eq 'ARRAY');
68              
69             # Internal fields
70              
71             # Tell us if the node has changed after the visit
72 63         310 $args{CHANGES} = 0;
73            
74             # PENDING_TASKS is a queue storing the tasks waiting for a "safe time/node" to do them
75             # Usually that time occurs when visiting the father of the node who generated the job
76             # (when asap criteria is applied).
77             # Keys are node references. Values are array references. Each entry defines:
78             # [ the task kind, the node where to do the job, and info related to the particular job ]
79             # Example: @{$self->{PENDING_TASKS}{$father}}, ['insert_before', $node, ${$self->{NODE}}[0] ];
80 63         151 $args{PENDING_TASKS} = {};
81              
82             # NODE is a stack storing the ancestor of the node being visited
83             # Example: my $ancestor = ${$self->{NODE}}[$k]; when k=1 is the father, k=2 the grandfather, etc.
84             # Example: CORE::unshift @{$self->{NODE}}, $_[0]; Finished the visit so take it out
85 63         158 $args{NODE} = [];
86              
87 63         432 bless \%args, $class;
88             }
89              
90             sub buildpatterns {
91 30     30 1 104 my $class = shift;
92            
93 30         71 my @family;
94 30         237 while (my ($n, $p) = splice(@_, 0,2)) {
95 59         256 push @family, Parse::Eyapp::YATW->new(NAME => $n, PATTERN => $p);
96             }
97 30 50       469 return wantarray? @family : $family[0];
98             }
99              
100             ####################################################################
101             # Usage : @r = $b{$_}->m($t)
102             # See Simple4.eyp and m_yatw.pl in the examples directory
103             # Returns : Returns an array of nodes matching the treeregexp
104             # The set of nodes is a Parse::Eyapp::Node::Match tree
105             # showing the relation between the matches
106             # Parameters : The tree (and the object of course)
107             # depth is no longer used: eliminate
108             sub m {
109 9     9 0 25 my $p = shift(); # pattern YATW object
110 9         18 my $t = shift; # tree
111 9         28 my $pattern = $p->{PATTERN}; # CODE ref
112              
113             # References to the found nodes are stored in @stack
114 9         62 my @stack = ( Parse::Eyapp::Node::Match->new(node=>$t, depth=>0, dewey => "") );
115 9         14 my @results;
116 9         18 do {
117 98         122 my $n = CORE::shift(@stack);
118 98         392 my %n = %$n;
119              
120 98         173 my $dewey = $n->{dewey};
121 98         110 my $d = $n->{depth};
122 98 100       2691 if ($pattern->($n{node})) {
123 40         103 $n->{family} = [ $p ];
124 40         83 $n->{patterns} = [ 0 ];
125              
126             # Is at this time that I have to compute the father
127 40     147   178 my $f = lastval { $dewey =~ m{^$_->{dewey}}} @results;
  147         1188  
128 40         139 $n->{father} = $f;
129             # ... and children
130 40 100       107 push @{$f->{children}}, $n if defined($f);
  31         57  
131 40         67 push @results, $n;
132             }
133 98         122 my $k = 0;
134 89         180 CORE::unshift @stack,
135             map {
136 98         303 local $a;
137 89         364 $a = Parse::Eyapp::Node::Match->new(node=>$_, depth=>$d+1, dewey=>"$dewey.$k" );
138 89         119 $k++;
139 89         415 $a;
140             } $n{node}->children();
141             } while (@stack);
142              
143 9 50       83 return wantarray? @results : $results[0];
144             }
145              
146             ######################### getter-setter for YATW objects ###########################
147              
148             sub pattern {
149 989     989 0 1129 my $self = shift;
150 989 50       9225 $self->{PATTERN} = shift if (@_);
151 989         34645 return $self->{PATTERN};
152             }
153              
154             sub name {
155 0     0 0 0 my $self = shift;
156 0 0       0 $self->{NAME} = shift if (@_);
157 0         0 return $self->{NAME};
158             }
159              
160             #sub pattern_args {
161             # my $self = shift;
162             #
163             # $self->{PATTERN_ARGS} = @_ if @_;
164             # return @{$self->{PATTERN_ARGS}};
165             #}
166              
167             ########################## PENDING TASKS management ################################
168              
169             # Purpose : Deletes the node that matched from the list of children of its father.
170             sub delete {
171 36     36 1 170 my $self = shift;
172              
173 36         185 bless $self->{NODE}[0], 'Parse::Eyapp::Node::DELETE';
174             }
175            
176             sub make_delete_effective {
177 841     841 0 1004 my $self = shift;
178 841         1159 my $node = shift;
179              
180 841         2265 my $i = -1+$node->children;
181 841         2234 while ($i >= 0) {
182 876 100       9551 if (UNIVERSAL::isa($node->child($i), 'Parse::Eyapp::Node::DELETE')) {
183 36 50       42 $self->{CHANGES}++ if defined(splice(@{$node->{children}}, $i, 1));
  36         118  
184             }
185 876         2261 $i--;
186             }
187             }
188              
189             ####################################################################
190             # Usage : my $b = Parse::Eyapp::Node->new( 'NUM(TERMINAL)', sub { $_[1]->{attr} = 4 });
191             # $yatw_pattern->unshift($b);
192             # Parameters : YATW object, node to insert,
193             # ancestor offset: 0 = root of the tree that matched, 1 = father, 2 = granfather, etc.
194              
195             sub unshift {
196 3     3 1 19 my ($self, $node, $k) = @_;
197 3 50       10 $k = 1 unless defined($k); # father by default
198              
199 3         4 my $ancestor = ${$self->{NODE}}[$k];
  3         8  
200 3 50       9 croak "unshift: does not exist ancestor $k of node ".Dumper(${$self->{NODE}}[0]) unless defined($ancestor);
  0         0  
201              
202             # Stringification of $ancestor. Hope it works
203             # operation, node to insert,
204 3         4 push @{$self->{PENDING_TASKS}{$ancestor}}, ['unshift', $node ];
  3         20  
205             }
206              
207             sub insert_before {
208 1     1 1 8 my ($self, $node) = @_;
209              
210 1         2 my $father = ${$self->{NODE}}[1];
  1         4  
211 1 50       8 croak "insert_before: does not exist father of node ".Dumper(${$self->{NODE}}[0]) unless defined($father);
  0         0  
212              
213             # operation, node to insert, before this node
214 1         2 push @{$self->{PENDING_TASKS}{$father}}, ['insert_before', $node, ${$self->{NODE}}[0] ];
  1         6  
  1         9  
215             }
216              
217             sub _delayed_insert_before {
218 1     1   3 my ($father, $node, $before) = @_;
219              
220 1         396 my $i = 0;
221 1         8 for ($father->children()) {
222 3 100       10 last if ($_ == $before);
223 2         3 $i++;
224             }
225 1         2 splice @{$father->{children}}, $i, 0, $node;
  1         4  
226             }
227              
228             sub do_pending_tasks {
229 841     841 0 1126 my $self = shift;
230 841         1560 my $node = shift;
231              
232 841         1823 my $mytasks = $self->{PENDING_TASKS}{$node};
233 841   100     2697 while ($mytasks and (my $job = shift @{$mytasks})) {
  7         33  
234 4         15 my @args = @$job;
235 4         8 my $task = shift @args;
236              
237             # change this for a jump table
238 4 100       19 if ($task eq 'unshift') {
    50          
239 3         7 CORE::unshift(@{$node->{children}}, @args);
  3         7  
240 3         13 $self->{CHANGES}++;
241             }
242             elsif ($task eq 'insert_before') {
243 1         5 _delayed_insert_before($node, @args);
244 1         5 $self->{CHANGES}++;
245             }
246             }
247             }
248              
249             ####################################################################
250             # Parameters : pattern, node, father of the node, index of the child in the children array
251             # YATW object. Probably too many
252             sub s {
253 989     989 0 1277 my $self = shift;
254 989 50       7259 my $node = $_[0] or croak("Error. Method __PACKAGE__::s requires a node");
255 989         1014 CORE::unshift @{$self->{NODE}}, $_[0];
  989         2096  
256             # father is $_[1]
257 989         1270 my $index = $_[2];
258              
259             # If is not a reference or can't children then simply check the matching and leave
260 989 100 33     11690 if (!ref($node) or !UNIVERSAL::can($node, "children")) {
261            
262 148 100       367 $self->{CHANGES}++ if $self->pattern->(
263             $_[0], # Node being visited
264             $_[1], # Father of this node
265             $index, # Index of this node in @Father->children
266             $self, # The YATW pattern object
267             );
268 148         557 return;
269             };
270            
271             # Else, is not a leaf and is a regular Parse::Eyapp::Node
272             # Recursively transform subtrees
273 841         1025 my $i = 0;
274 841         918 for (@{$node->{children}}) {
  841         2247  
275 882         2131 $self->s($_, $_[0], $i);
276 882         2180 $i++;
277             }
278            
279 841         1448 my $number_of_changes = $self->{CHANGES};
280             # Now is safe to delete children nodes that are no longer needed
281 841         1708 $self->make_delete_effective($node);
282              
283             # Safely do pending jobs for this node
284 841         1731 $self->do_pending_tasks($node);
285              
286             #node , father, childindex, and ...
287             #Change YATW object to be the first argument?
288 841 100       2214 if ($self->pattern->($_[0], $_[1], $index, $self)) {
289 52         1540 $self->{CHANGES}++;
290             }
291 841         3017 shift @{$self->{NODE}};
  841         1962  
292             }
293              
294             1;
295