File Coverage

blib/lib/Graph/Easy/StateMachine.pm
Criterion Covered Total %
statement 111 128 86.7
branch 25 38 65.7
condition 5 18 27.7
subroutine 20 28 71.4
pod 0 3 0.0
total 161 215 74.8


line stmt bran cond sub pod time code
1             package Graph::Easy::StateMachine;
2              
3 1     1   197183 use 5.006002;
  1         3  
  1         38  
4 1     1   5 use strict;
  1         2  
  1         33  
5 1     1   5 use warnings;
  1         6  
  1         26  
6 1     1   5 use Carp;
  1         2  
  1         169  
7              
8             our $VERSION = '0.07';
9             # use Class::ISA;
10             #--------------------------------------------------------------------------
11             sub self_and_super_path {
12             # Assumption: searching is depth-first.
13             # Assumption: '' (empty string) can't be a class package name.
14             # Note: 'UNIVERSAL' is not given any special treatment.
15 4 50   4 0 13 return () unless @_;
16              
17 4         9 my @out = ();
18              
19 4         9 my @in_stack = ($_[0]);
20 4         17 my %seen = ($_[0] => 1);
21              
22 4         5 my $current;
23 4         13 while(@in_stack) {
24 5 50 33     33 next unless defined($current = shift @in_stack) && length($current);
25 5         8 push @out, $current;
26 1     1   6 no strict 'refs';
  1         1  
  1         174  
27 1         3 unshift @in_stack,
28             map
29 5         32 { my $c = $_; # copy, to avoid being destructive
30 1 50       22 substr($c,0,2) = "main::" if substr($c,0,2) eq '::';
31             # Canonize the :: -> main::, ::foo -> main::foo thing.
32             # Should I ever canonize the Foo'Bar = Foo::Bar thing?
33 1 50       9 $seen{$c}++ ? () : $c;
34             }
35 5         5 @{"$current\::ISA"}
36             ;
37             # I.e., if this class has any parents (at least, ones I've never seen
38             # before), push them, in order, onto the stack of classes I need to
39             # explore.
40             }
41              
42 4         17 return @out;
43             }
44             # end routine taken from Class::ISA version 0.33
45              
46             our $base;
47             sub template($$$){
48 21     21 0 132 my ($source, $dest, $edgelabel) = @_;
49 21         70 "sub $source\::$edgelabel { bless \$_[0], '$dest' }"
50             };
51              
52 1     1   5 use Graph::Easy;
  1         1  
  1         413  
53              
54             sub Graph::Easy::as_FSA {
55 5     5 0 6653 my $graph = shift;
56 5         35 my %attr = (base => (scalar caller()), BASESTATE => 'BASE', @_);
57 5         10 my $base = $attr{base};
58 5         8 my $BASE = $attr{BASESTATE};
59 5         8 my @LOC;
60             my %BaseTransitions;
61 0         0 my %Transitions;
62 5         21 for my $node ( $graph->nodes )
63             {
64 19         3131 my $statename = $node->name;
65 19 100       1040 $statename eq $BASE or
66             push @LOC, "push \@$base\::$statename\::ISA, qw( $base );";
67              
68 19         390 for my $edge ( $node->edges )
69             {
70 43 100       2055 $edge->from->name eq $statename or next;
71 22         172 my $from = $statename;
72 22         64 my $to = $edge->to->name;
73 22         132 my $frompack;
74 22   66     54 my $methodname = $edge->name || $to;
75 22 100       148 if( $from eq $BASE )
76             {
77 2         3 $frompack = $base;
78 2         4 $BaseTransitions{ $methodname } = 1;
79             }else{
80 20         38 $frompack = "$base\::$from";
81             };
82 22 50       48 my $topack = ( $to eq $BASE ? $base : "$base\::$to" );
83 22 100       297 $Transitions{ $methodname }->{$from}++ and Carp::croak "ambiguous declaration of $methodname from $from";
84              
85 21   66     55 push @LOC, template $frompack, $topack, $edge->name || $to;
86 21 50       58 if ($edge->bidirectional)
87             {
88 0 0 0     0 $Transitions{ $edge->name || $from }->{$to}++
89             and Carp::croak "ambiguous declaration of $methodname from $from";
90 0   0     0 push @LOC, template $topack, $frompack, $edge->name || $from;
91 0 0 0     0 $to eq $BASE and
92             $BaseTransitions{ $edge->name || $from } = 1;
93             }
94             };
95            
96             };
97 4         45 for my $node ( $graph->nodes )
98             {
99 18         97 my $statename = $node->name;
100 18 100       88 $statename eq $BASE and next;
101 16         29 for my $method ( keys %BaseTransitions )
102             {
103 6 50       13 $Transitions{ $method }->{$statename} and next;
104 6         24 push @LOC,
105             "sub $base\::$statename\::$method { my (\$p,\$f,\$l) = caller; die qq{invalid state transition $statename\->$method at \$f line \$l\n} }"
106             };
107             # inherit methods from parent states
108 16         27 my %Seen = ();
109 1     1   5 no strict 'refs';
  1         2  
  1         315  
110 16         15 my @MoreIsas;
111 4         12 @MoreIsas = map {"$_\::$statename"} grep {
  5         34  
112             ! $Seen{$_}++ and
113 5 50       15 scalar %{"$_\::$statename\::"}
114 16         14 } do {
115 16         18 my @IsaList = @{"$base\::ISA"};
  16         52  
116 16         16 my %seen; my $sawnew;
117 16         15 while(1){
118 21         28 my @copy = @IsaList;
119 21         20 $sawnew = 0;
120 21         56 for (@copy){
121 10 100       24 $seen{$_}++ and next;
122 5         6 push @IsaList, @{"$_\::ISA"};
  5         11  
123 5         9 $sawnew++;
124             };
125 21 100       43 $sawnew or last;
126             };
127            
128 16         27 @IsaList;
129             };
130             # warn "adding to \@$base\::$statename\::ISA these: @MoreIsas";
131 16 100       39 @MoreIsas and push @{"$base\::$statename\::ISA"},@MoreIsas;
  4         51  
132             };
133 4         74 join "\n", @LOC, '1;';
134             }
135              
136             our %GraphsByPackage;
137             sub import {
138 4     4   207 shift; # lose package
139 4         20 my ($caller, $file, $line) = caller;
140 1     1   5 no strict 'refs';
  1         1  
  1         41  
141 4         6 push @{$GraphsByPackage{$caller}}, @_;
  4         13  
142 1     1   4 no warnings;
  1         2  
  1         144  
143 9         26 my @graphs = map {
144 4         13 @{$GraphsByPackage{$_}}
  9         10  
145             } reverse (self_and_super_path($caller), 'UNIVERSAL');
146              
147 4         85 eval join "\n", ( map {
148 4 50   1   56 my $g = Graph::Easy->new( $_ );
  1     1   8  
  1     1   4  
  1     0   12  
  1     0   402  
  0     0   0  
  0     0   0  
  0     0   0  
  0     1   0  
  0     1   0  
  0     0   0  
  0     1   0  
  0     0   0  
  0     1   0  
  0     0   0  
  1         3642  
  1         9  
  0         0  
  1         4  
  0         0  
  1         7  
  0            
149 4         18242 $g->as_FSA(base => $caller)
150             } @graphs),';1' or die "FSA parse failure from $file line $line:\n$@";
151             };
152              
153             1;
154             __END__