File Coverage

blib/lib/DFA/Kleene.pm
Criterion Covered Total %
statement 9 93 9.6
branch 0 30 0.0
condition 0 12 0.0
subroutine 3 8 37.5
pod 0 5 0.0
total 12 148 8.1


line stmt bran cond sub pod time code
1              
2             # Copyright (c) 1996, 1997 by Steffen Beyer. All rights reserved.
3             # This package is free software; you can redistribute it and/or
4             # modify it under the same terms as Perl itself.
5              
6             package DFA::Kleene; # DFA = Deterministic Finite Automaton
7              
8             # Other modules in this series (variants of Kleene's algorithm):
9             #
10             # Math::MatrixBool (see "Kleene()")
11             # Math::MatrixReal (see "kleene()")
12              
13 1     1   637 use strict;
  1         2  
  1         50  
14 1         201 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION
15             $number_of_states %alphabet
16             %accepting_states @delta
17 1     1   5 @words %language);
  1         2  
18              
19             require Exporter;
20              
21             @ISA = qw(Exporter);
22              
23             @EXPORT = qw();
24              
25             @EXPORT_OK = qw(initialize define_accepting_states define_delta kleene example);
26              
27             %EXPORT_TAGS = (all => [@EXPORT_OK]);
28              
29             $VERSION = '1.0';
30              
31 1     1   5 use Carp;
  1         10  
  1         1298  
32              
33             sub initialize
34             {
35 0 0   0 0   croak "Usage: DFA::Kleene::initialize(\$number_of_states,\$alphabet)"
36             if (@_ != 2);
37              
38 0           my($number,$alpha) = @_;
39 0           my($i,$j,$k);
40              
41 0 0         croak "DFA::Kleene::initialize(): number of states must be > 0"
42             if ($number <= 0);
43 0 0         croak "DFA::Kleene::initialize(): alphabet must comprise at least one character"
44             if (length($alpha) == 0);
45              
46 0           $number_of_states = $number;
47              
48 0           undef %alphabet;
49 0           undef %accepting_states;
50 0           undef @delta;
51 0           undef @words;
52 0           undef %language;
53              
54 0           for ( $i = 0; $i < length($alpha); $i++ )
55             {
56 0           $alphabet{substr($alpha,$i,1)} = 1;
57             }
58              
59 0           for ( $i = 1; $i <= $number_of_states; $i++ )
60             {
61 0           $delta[$i] = { };
62 0           $words[$i] = [ ];
63 0           for ( $j = 1; $j <= $number_of_states; $j++ )
64             {
65 0           $words[$i][$j] = [ ];
66 0           for ( $k = 0; $k <= $number_of_states; $k++ )
67             {
68 0           $words[$i][$j][$k] = { };
69             }
70             }
71             }
72             }
73              
74             sub define_accepting_states
75             {
76 0 0   0 0   croak "Usage: DFA::Kleene::define_accepting_states(\@accepting_states)"
77             if (@_ < 1);
78              
79 0           my(@final) = @_;
80 0           my($state);
81              
82 0           undef %accepting_states;
83 0           foreach $state (@final)
84             {
85 0 0 0       croak "DFA::Kleene::define_accepting_states(): state $state not in [1..$number_of_states]"
86             if (($state < 1) || ($state > $number_of_states));
87 0           $accepting_states{$state} = 1;
88             }
89             }
90              
91             sub define_delta
92             {
93 0 0   0 0   croak "Usage: DFA::Kleene::define_delta(\$state1,\$character,\$state2)"
94             if (@_ != 3);
95              
96 0           my($state1,$character,$state2) = @_;
97              
98 0 0 0       croak "DFA::Kleene::define_delta(): state $state1 not in [1..$number_of_states]"
99             if (($state1 < 1) || ($state1 > $number_of_states));
100 0 0 0       croak "DFA::Kleene::define_delta(): state $state2 not in [1..$number_of_states]"
101             if (($state2 < 1) || ($state2 > $number_of_states));
102 0 0         croak "DFA::Kleene::define_delta(): only single character or empty string permitted"
103             if (length($character) > 1);
104 0 0 0       croak "DFA::Kleene::define_delta(): character is not contained in alphabet"
105             if ($character && !($alphabet{$character}));
106 0 0         croak "DFA::Kleene::define_delta(): \$delta[$state1]{'$character'} already defined"
107             if (defined $delta[$state1]{$character});
108              
109 0           $delta[$state1]{$character} = $state2;
110             }
111              
112             sub kleene
113             {
114 0 0   0 0   croak "Usage: DFA::Kleene::kleene()"
115             if (@_ != 0);
116              
117 0           my($i,$j,$k);
118 0           my($state,$word,$word1,$word2,$word3);
119              
120 0           for ( $i = 1; $i <= $number_of_states; $i++ )
121             {
122 0           for ( $j = 1; $j <= $number_of_states; $j++ )
123             {
124 0           foreach $_ (keys %{$delta[$i]})
  0            
125             {
126 0 0         if ($delta[$i]{$_} == $j)
127             {
128 0           $words[$i][$j][0]{$_} = 1;
129             }
130             }
131 0 0         if ($i == $j) { $words[$i][$j][0]{''} = 1; }
  0            
132             }
133             }
134              
135 0           for ( $k = 1; $k <= $number_of_states; $k++ )
136             {
137 0           for ( $i = 1; $i <= $number_of_states; $i++ )
138             {
139 0           for ( $j = 1; $j <= $number_of_states; $j++ )
140             {
141 0           foreach $word (keys %{$words[$i][$j][$k-1]})
  0            
142             {
143 0           $words[$i][$j][$k]{$word} = 1;
144             }
145 0           foreach $word1 (keys %{$words[$i][$k][$k-1]})
  0            
146             {
147 0           foreach $word2 (keys %{$words[$k][$k][$k-1]})
  0            
148             {
149 0           foreach $word3 (keys %{$words[$k][$j][$k-1]})
  0            
150             {
151 0 0         if ($word2)
152 0           { $word = "${word1}(${word2})*${word3}"; }
153             else
154 0           { $word = "${word1}${word3}"; }
155 0           $words[$i][$j][$k]{$word} = 1;
156             }
157             }
158             }
159             }
160             }
161             }
162 0           undef %language;
163 0           foreach $state (keys %accepting_states)
164             {
165             # Note that the following assumes state #1 to be the "start" state:
166              
167 0           foreach $word (keys %{$words[1][$state][$number_of_states]})
  0            
168             {
169 0           $language{$word} = 1;
170             }
171             }
172 0           return( sort(keys %language) );
173             }
174              
175             sub example
176             {
177 0     0 0   &initialize(6,"ab");
178 0           &define_accepting_states(2,3,4,5);
179 0           &define_delta(1,'a',4);
180 0           &define_delta(1,'b',6);
181 0           &define_delta(2,'a',2);
182 0           &define_delta(2,'b',5);
183 0           &define_delta(3,'a',6);
184 0           &define_delta(3,'b',3);
185 0           &define_delta(4,'a',2);
186 0           &define_delta(4,'b',5);
187 0           &define_delta(5,'a',4);
188 0           &define_delta(5,'b',3);
189 0           &define_delta(6,'a',6);
190 0           &define_delta(6,'b',6);
191              
192             # should return something equivalent to:
193             # (a(a)*b)*a(a)*(b)*
194             # which is the same as ((a+)b)*(a+)b*
195              
196 0           foreach $_ ( &kleene() )
197             {
198 0           print "'$_'\n";
199             }
200             }
201              
202             1;
203              
204             __END__