File Coverage

blib/lib/Algorithm/ConstructDFA/XS.pm
Criterion Covered Total %
statement 98 104 94.2
branch 23 32 71.8
condition 14 25 56.0
subroutine 12 13 92.3
pod 1 1 100.0
total 148 175 84.5


line stmt bran cond sub pod time code
1             package Algorithm::ConstructDFA::XS;
2            
3 4     4   158247 use 5.012000;
  4         15  
  4         148  
4 4     4   24 use strict;
  4         8  
  4         136  
5 4     4   25 use warnings;
  4         21  
  4         117  
6 4     4   2388 use Data::AutoBimap;
  4         1445  
  4         170  
7 4     4   8751 use Storable;
  4         11741  
  4         5764  
8            
9             require Exporter;
10            
11             our @ISA = qw(Exporter);
12            
13             our %EXPORT_TAGS = ( 'all' => [ qw(
14             construct_dfa_xs
15             ) ] );
16            
17             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18            
19             our @EXPORT = qw(
20             construct_dfa_xs
21             );
22            
23             our $VERSION = '0.23';
24            
25             require XSLoader;
26             XSLoader::load('Algorithm::ConstructDFA::XS', $VERSION);
27            
28             sub construct_dfa_xs {
29 90     90 1 12151272 my (%o) = @_;
30            
31 90 50       668 die unless ref $o{is_nullable};
32 90 50 33     486 die unless ref $o{is_accepting} or exists $o{final};
33 90 50 66     542 die unless ref $o{successors} or ref $o{edges_from};
34 90 50 66     454 die unless ref $o{get_label} or ref $o{edges_from};
35 90 50 33     326 die unless exists $o{start} or exists $o{many_start};
36 90 50 33     637 die if ref $o{is_accepting} and exists $o{final};
37 90 50 66     500 die if ref $o{successors} and exists $o{edges_from};
38 90 50 66     421 die if ref $o{get_label} and ref $o{edges_from};
39            
40 90         168 my $class = 'Algorithm::ConstructDFA::XS::Synth';
41            
42 90 100       248 if (exists $o{edges_from}) {
43            
44 30         71 my $old_accepting = $o{is_accepting};
45             $o{is_accepting} = sub {
46 234     234   1221 my @config = grep { ref $_ ne $class } @_;
  5252         34548  
47 234         641 return $old_accepting->(@config);
48 30         167 };
49            
50             $o{get_label} = sub {
51 615     615   1364 my ($src) = @_;
52 615 100       1583 return unless ref $src eq $class;
53 398         858 return (Storable::thaw($$src))->[1];
54 30         141 };
55            
56 30         57 my $old_nullable = $o{is_nullable};
57             $o{is_nullable} = sub {
58 615     615   1019 my ($src) = @_;
59            
60 615 100       1922 if (ref $src eq $class) {
61 398         478 my $deref = $$src;
62 398         932 my $thawed = Storable::thaw $deref;
63 398         7750 return not defined $thawed->[1];
64             }
65            
66 217         891 $old_nullable->($src);
67 30         145 };
68            
69 30         61 my $old_edges_from = $o{edges_from};
70            
71             $o{successors} = sub {
72 615     615   1850 my ($src) = @_;
73            
74 615 100       1586 if (ref $src eq $class) {
75 398         933 return (Storable::thaw $$src)->[2];
76             }
77            
78 217         307 my @successors;
79            
80 217         512 for my $edge ($old_edges_from->($src)) {
81 398         10255 my ($dst, $label) = @$edge;
82            
83             # TODO: theoretically there could be name clashes between the
84             # artificial vertex created here and vertices in the original
85             # unwrapped input which can interfere with the bimaps mapping
86             # stringified vertices to numbers.
87 398         1329 push @successors, bless \(Storable::freeze([$src, $label, $dst])),
88             $class;
89             }
90            
91 217         8916 return @successors;
92 30         271 };
93            
94             }
95            
96 90 50       270 if (exists $o{final}) {
97 0         0 my %in_final = map { $_ => 1 } @{ $o{final} };
  0         0  
  0         0  
98             $o{is_accepting} = sub {
99 0     0   0 grep { $in_final{$_} } @_
  0         0  
100 0         0 };
101             }
102            
103 90   50     514 $o{many_start} //= [$o{start}];
104            
105 90         427 my $dfa = _construct_dfa_xs($o{many_start}, $o{get_label},
106             $o{is_nullable}, $o{successors}, $o{is_accepting});
107            
108 90 100       489 if (exists $o{edges_from}) {
109 30         85 for (values %$dfa) {
110 5252         11801 $_->{Combines} = [ grep {
111 234         478 ref $_ ne $class;
112 234         276 } @{ $_->{Combines} } ];
113             }
114             }
115            
116 90         1071 return $dfa;
117             }
118            
119             sub _construct_dfa_xs {
120 90     90   176 my ($roots, $labelf, $nullablef, $successorsf, $acceptingf) = @_;
121            
122 90         194 my @todo = map { @$_ } @$roots;
  90         342  
123 90         150 my %seen;
124             my @args;
125 90         543 my $sm = Data::AutoBimap->new;
126 90         1873 my $rm = Data::AutoBimap->new;
127 90         866 my %is_start;
128            
129 90         305 for (my $ix = 0; $ix < @$roots; ++$ix) {
130 90         135 for my $v (@{ $roots->[$ix] }) {
  90         250  
131 113         159 push @{ $is_start{$v} }, $ix + 1;
  113         650  
132             }
133             }
134            
135 90         232 while (@todo) {
136 2677         4257 my $c = pop @todo;
137            
138 2677 100       8490 next if $seen{$c}++;
139            
140 1456         3072 my $is_nullable = !!$nullablef->($c);
141 1456         81246 my $label = $labelf->($c);
142 1456 100       78655 my $label_x = defined $label ? $rm->s2n($label) : undef;
143            
144             # [vertex, label, nullable, start, successors...]
145 1456   100     10080 my @data = ($sm->s2n($c), $label_x, !!$is_nullable, $is_start{$c} // []);
146            
147 1456         31707 for ($successorsf->($c)) {
148 2564         63805 push @data, $sm->s2n($_);
149 2564         29437 push @todo, $_;
150             }
151            
152 1456         15454 push @args, \@data;
153             }
154            
155             my %h = _internal_construct_dfa_xs(sub {
156 1115     1115   221989 !!$acceptingf->(map { $sm->n2s($_) } @_)
  12511         83072  
157 90         29186 }, \@args);
158            
159 90         54509 for (values %h) {
160 1038         17501 $_->{Combines} = [ map { $sm->n2s($_) } @{ $_->{Combines} } ];
  12402         74583  
  1038         2203  
161             }
162            
163 90         913 for my $v (values %h) {
164 1038         1584 my $over = {};
165 1038         1366 $over->{ $rm->n2s($_) } = $v->{NextOver}{$_} for keys %{ $v->{NextOver} };
  1038         5202  
166 1038         21482 $v->{NextOver} = $over;
167             }
168            
169 90         2545 return \%h;
170             }
171            
172             1;
173            
174             __END__