File Coverage

blib/lib/Devel/MAT/Graph.pm
Criterion Covered Total %
statement 53 62 85.4
branch 6 10 60.0
condition 5 7 71.4
subroutine 16 17 94.1
pod 7 7 100.0
total 87 103 84.4


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2014-2016 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Graph 0.49;
7              
8 9     9   108 use v5.14;
  9         27  
9 9     9   39 use warnings;
  9         13  
  9         351  
10              
11 9     9   43 use Struct::Dumb 0.07 'readonly_struct';
  9         121  
  9         46  
12              
13             =head1 NAME
14              
15             C - a set of references between related SVs
16              
17             =head1 DESCRIPTION
18              
19             Instances of this class represent an entire graph of references between
20             related SVs, as a helper method for return values from various L
21             methods, which might be used for some sort of screen layout or other analysis
22             tasks.
23              
24             =cut
25              
26             =head1 CONSTRUCTOR
27              
28             =cut
29              
30             =head2 new
31              
32             $graph = Devel::MAT::Graph->new( $dumpfile )
33              
34             Constructs a new C instance backed by the given dumpfile
35             (which is only actually used to make the C<< $node->sv >> method work).
36              
37             =cut
38              
39             sub new
40             {
41 2     2 1 6 my $class = shift;
42 2         6 my ( $df ) = @_;
43              
44 2         21 bless {
45             df => $df,
46              
47             edges_from => {},
48             edges_to => {},
49              
50             roots_from => {},
51             }, $class;
52             }
53              
54             =head1 MUTATION METHODS
55              
56             =cut
57              
58             =head2 add_sv
59              
60             $graph->add_sv( $sv )
61              
62             Makes the graph aware of the given L. This is not strictly
63             necessary before calling C or C, but ensures that C
64             will return true immediately after it, and so can be used as a sentinel for
65             recursion control.
66              
67             =cut
68              
69             sub add_sv
70             {
71 6767     6767 1 22405 my $self = shift;
72 6767         10359 my ( $sv ) = @_;
73              
74 6767   100     31868 $self->{edges_from}{$sv->addr} ||= [];
75              
76 6767         12365 return $self;
77             }
78              
79             =head2 add_ref
80              
81             $graph->add_ref( $from_sv, $to_sv, $desc )
82              
83             Adds an edge to the graph, from and to the given SVs, with the given
84             description.
85              
86             =cut
87              
88             sub add_ref
89             {
90 8461     8461 1 35322 my $self = shift;
91 8461         13316 my ( $from_sv, $to_sv, $desc ) = @_;
92              
93 8461         14146 my $from_addr = $from_sv->addr;
94 8461         13539 my $to_addr = $to_sv->addr;
95              
96 8461         9866 push @{ $self->{edges_from}{$from_addr} }, [ $to_addr, $desc ];
  8461         45516  
97 8461         10808 push @{ $self->{edges_to} {$to_addr} }, [ $from_addr, $desc ];
  8461         27635  
98              
99 8461         20719 return $self;
100             }
101              
102             =head2 add_root
103              
104             $graph->add_root( $from_sv, $desc )
105              
106             Adds a root edge to the graph, at the given SV with the given description.
107              
108             =cut
109              
110             sub add_root
111             {
112 1684     1684 1 3673 my $self = shift;
113 1684         4437 my ( $from_sv, $desc ) = @_;
114              
115 1684         3806 push @{ $self->{roots_from}{$from_sv->addr} }, $desc;
  1684         16031  
116              
117 1684         4214 return $self;
118             }
119              
120             =head1 QUERY METHODS
121              
122             =cut
123              
124             =head2 has_sv
125              
126             $bool = $graph->has_sv( $sv )
127              
128             Returns true if the graph has edges or roots for the given SV, or it has at
129             least been given to C.
130              
131             =cut
132              
133             sub has_sv
134             {
135 8461     8461 1 36775 my $self = shift;
136 8461         13923 my ( $sv ) = @_;
137              
138 8461         27022 my $addr = $sv->addr;
139              
140             return !!( $self->{edges_from}{$addr} ||
141             $self->{edges_to} {$addr} ||
142 8461   33     50380 $self->{roots_from}{$addr} );
143             }
144              
145             =head2 get_sv_node
146              
147             $node = $graph->get_sv_node( $sv )
148              
149             Returns a C object for the given SV.
150              
151             =cut
152              
153             sub get_sv_node
154             {
155 3385     3385 1 6177 my $self = shift;
156 3385         5711 my ( $sv ) = @_;
157              
158 3385 50       12255 my $addr = ref $sv ? $sv->addr : $sv;
159              
160 3385         10706 return Devel::MAT::Graph::Node->new(
161             graph => $self,
162             addr => $addr,
163             );
164             }
165              
166             =head2 get_root_nodes
167              
168             @desc_nodes = $graph->get_root_nodes
169              
170             Returns an even-sized list of pairs, containing root descriptions and the
171             nodes having those roots, in no particular order.
172              
173             =cut
174              
175             sub get_root_nodes
176             {
177 0     0 1 0 my $self = shift;
178             return map {
179 0         0 my $node = $self->get_sv_node( $_ );
180 0         0 map { $_, $node } @{ $self->{roots_from}{$_} }
  0         0  
  0         0  
181 0         0 } keys %{ $self->{roots_from} };
  0         0  
182             }
183              
184             package Devel::MAT::Graph::Node 0.49;
185              
186             =head1 NODE OBJECTS
187              
188             The values returned by C respond to the following methods:
189              
190             =cut
191              
192 3413     3413   6085 sub new { my $class = shift; bless { @_ }, $class }
  3413         24496  
193              
194             =head2 graph
195              
196             $graph = $node->graph
197              
198             Returns the containing C instance.
199              
200             =head2 addr
201              
202             $addr = $node->addr
203              
204             Returns the address of the SV represented by this node.
205              
206             =cut
207              
208 53     53   118 sub graph { $_[0]->{graph} }
209 34     34   125 sub addr { $_[0]->{addr} }
210              
211             =head2 sv
212              
213             $sv = $node->sv
214              
215             Returns the SV object itself, as taken from the dumpfile instance.
216              
217             =cut
218              
219 6     6   845 sub sv { $_[0]->graph->{df}->sv_at( $_[0]->addr ) }
220              
221             =head2 roots
222              
223             @roots = $node->roots
224              
225             Returns any root descriptions given (by calls to C<< $graph->add_root >> for
226             the SV at this node.
227              
228             $graph->add_root( $sv, $desc );
229              
230             ( $desc, ... ) = $graph->get_sv_node( $sv )->roots
231              
232             =cut
233              
234             sub roots
235             {
236 8     8   13 my $self = shift;
237 8   100     12 return @{ $self->graph->{roots_from}{$self->addr} // [] };
  8         19  
238             }
239              
240             =head2 edges_out
241              
242             @edges = $node->edges_out
243              
244             Returns an even-sized list of any edge descriptions and more C objects
245             given as references (by calls to C<< $graph->add_ref >>) from the SV at this
246             node.
247              
248             $graph->add_ref( $from_sv, $to_sv, $desc )
249              
250             ( $desc, $to_edge, ... ) = $graph->get_sv_node( $from_sv )->edges_out
251              
252             =head2 edges_out (scalar)
253              
254             $n_edges = $node->edges_out
255              
256             In scalar context, returns the I that exist; i.e. half the
257             size of the pairlist that would be returned in list context.
258              
259             =cut
260              
261             sub edges_out
262             {
263 4     4   7 my $self = shift;
264              
265 4 50       6 return unless my $edges = $self->graph->{edges_from}{$self->addr};
266 4 50       19 return scalar @$edges unless wantarray;
267             return map {
268 0         0 $_->[1], ( ref $self )->new( graph => $self->graph, addr => $_->[0] )
  0         0  
269             } @$edges;
270             }
271              
272             =head2 edges_in
273              
274             @edges = $node->edges_in
275              
276             Similar to C, but returns edges in the opposite direction; i.e.
277             edges of references to this node.
278              
279             $graph->add_ref( $from_sv, $to_sv, $desc )
280              
281             ( $desc, $from_edge, ... ) = $graph->get_sv_node( $to_sv )->edges_in
282              
283             =head2 edges_in (scalar)
284              
285             $n_edges = $node->edges_out
286              
287             In scalar context, returns the I that exist; i.e. half the
288             size of the pairlist that would be returned in list context.
289              
290             =cut
291              
292             sub edges_in
293             {
294 7     7   1290 my $self = shift;
295              
296 7 100       17 return unless my $edges = $self->graph->{edges_to}{$self->addr};
297 6 50       20 return scalar @$edges unless wantarray;
298             return map {
299 6         15 $_->[1], ( ref $self )->new( graph => $self->graph, addr => $_->[0] )
  28         62  
300             } @$edges;
301             }
302              
303             =head1 AUTHOR
304              
305             Paul Evans
306              
307             =cut
308              
309             0x55AA;