File Coverage

blib/lib/FLAT/Legacy/FA/NFA.pm
Criterion Covered Total %
statement 236 375 62.9
branch 41 84 48.8
condition 6 18 33.3
subroutine 21 31 67.7
pod 0 26 0.0
total 304 534 56.9


line stmt bran cond sub pod time code
1             # $Revision: 1.4 $ $Date: 2006/03/02 21:00:28 $ $Author: estrabd $
2              
3             package FLAT::Legacy::FA::NFA;
4              
5 3     3   26 use base 'FLAT::Legacy::FA';
  3         4  
  3         179  
6 3     3   12 use strict;
  3         3  
  3         48  
7 3     3   10 use Carp;
  3         2  
  3         128  
8              
9 3     3   12 use FLAT::Legacy::FA::DFA;
  3         4  
  3         69  
10 3     3   16 use Data::Dumper;
  3         4  
  3         10428  
11              
12             sub new {
13 2277     2277 0 1823 my $class = shift;
14 2277         9264 bless {
15             _START_STATE => undef, # start states -> only 1!
16             _STATES => [], # Set of all states
17             _FINAL_STATES => [], # Set of final states, subset of _STATES
18             _SYMBOLS => [], # Symbols
19             _TRANSITIONS => {}, # Transition table
20             _EPSILON => 'epsilon', # how an epsilon transition is represented
21             }, $class;
22             }
23              
24             sub jump_start {
25 1177     1177 0 976 my $self = shift;
26 1177         1734 my $NFA = FLAT::Legacy::FA::NFA->new();
27 1177         1294 my $symbol = shift;
28 1177 50       1638 if (!defined($symbol)) {
29 0         0 $symbol = $NFA->get_epsilon_symbol();
30             } else {
31 1177         1187 chomp($symbol);
32             }
33 1177         23277 my $newstart = crypt(rand 8,join('',[rand 8, rand 8]));
34 1177         14277 my $newfinal = crypt(rand 8,join('',[rand 8, rand 8]));
35             # add states
36 1177         2987 $NFA->add_state($newstart,$newfinal);
37             # add symbol
38 1177         1839 $NFA->add_symbol($symbol);
39             # set start and final
40 1177         1980 $NFA->set_start($newstart);
41 1177         1783 $NFA->add_final($newfinal);
42             # add single transition
43 1177         1985 $NFA->add_transition($newstart,$symbol,$newfinal);
44             #return $NFA
45 1177         2216 return $NFA;
46             }
47              
48             sub load_file {
49 0     0 0 0 my $self = shift;
50 0         0 my $file = shift;
51 0 0       0 if (-e $file) {
52 0         0 open (NFA,"<$file");
53 0         0 my $string = undef;
54 0         0 while () {
55 0         0 $string .= $_;
56             }
57 0         0 close (NFA);
58 0         0 $self->load_string($string);
59             }
60             }
61              
62             sub load_string {
63 0     0 0 0 my $self = shift;
64 0         0 my $string = shift;
65 0         0 my @lines = split("\n",$string);
66 0         0 my $CURR_STATE = undef;
67 0         0 foreach (@lines) {
68             # strip comments
69 0         0 $_ =~ s/\s*#.*$//;
70             # check if line is a state, transition, or keyword
71 0 0 0     0 if (m/^\s*([\w\d]*)\s*:\s*$/) {
    0 0        
    0          
72             #print STDERR "Found transitions for state $1\n";
73 0         0 $self->add_state($1);
74 0         0 $CURR_STATE = $1;
75             } elsif (m/^\s*([\w\d]*)\s*([\w\d,]*)\s*$/ && ! m/^$/) {
76             # treat as transition
77             #print STDERR "Input: '$1' goes to $2\n";
78 0         0 my @s = split(',',$2);
79 0         0 $self->add_transition($CURR_STATE,$1,@s);
80 0         0 $self->add_symbol($1);
81             } elsif (m/^\s*([\w\d]*)\s*::\s*([\w\d,]*)\s*$/ && ! m/^$/) {
82             # Check for known header keywords
83 0         0 my $val = $2;
84 0 0       0 if ($1 =~ m/START/i) {
    0          
    0          
85 0         0 $self->set_start($val);
86             } elsif ($1 =~ m/FINAL/i) {
87 0         0 my @s = split(',',$val);
88 0         0 $self->add_final(@s);
89             } elsif ($1 =~ m/EPSILON/i) {
90 0         0 $self->set_epsilon($val);
91             } else {
92 0         0 print STDERR "WARNING: $1 is not a valid header...\n";
93             }
94             }
95             }
96             }
97              
98             sub clone {
99 1022     1022 0 741 my $self = shift;
100 1022         1448 my $NFA = FLAT::Legacy::FA::NFA->new();
101 1022         1902 $NFA->add_state($self->get_states());
102 1022         2010 $NFA->add_final($self->get_final());
103 1022         1859 $NFA->add_symbol($self->get_symbols());
104 1022         1898 $NFA->set_start($self->get_start());
105 1022         1633 $NFA->set_epsilon($self->get_epsilon_symbol);
106 1022         1695 foreach my $state ($self->get_states()) {
107 3881         2575 foreach my $symbol (keys %{$self->{_TRANSITIONS}{$state}}) {
  3881         8720  
108 2838         1937 $NFA->add_transition($state,$symbol,@{$self->{_TRANSITIONS}{$state}{$symbol}});
  2838         4410  
109             }
110             }
111 1022         1308 return $NFA;
112             }
113              
114             sub append_nfa {
115 1022     1022 0 1006 my $self = shift;
116 1022         751 my $NFA = shift;
117             # clone $NFA
118 1022         1482 my $NFA1 = $NFA->clone();
119             # pinch off self - ensures a single final state
120 1022         1623 $self->pinch();
121             # pinch off $NFA1 to ensure a single final state
122 1022         1328 $NFA1->pinch();
123             # ensure unique state names
124 1022         20049 $self->ensure_unique_states($NFA1,crypt(rand 8,join('',[rand 8, rand 8])));
125             # sychronize epsilon symbol
126 1022 50       1785 if ($NFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
127 0         0 $NFA1->rename_symbol($NFA1->get_epsilon_symbol(),$self->get_epsilon_symbol());
128             }
129             # add new states from NFA1
130 1022         1864 foreach my $state ($NFA1->get_states()) {
131 3902         5667 $self->add_state($state);
132             };
133             # add new symbols from NFA1
134 1022         2242 foreach my $symbol ($NFA1->get_symbols()) {
135 1201         1839 $self->add_symbol($symbol);
136             }
137             # add transitions from NFA1
138 1022         993 foreach my $state (keys %{$NFA1->{_TRANSITIONS}}) {
  1022         2567  
139 2880         1811 foreach my $symbol (keys %{$NFA1->{_TRANSITIONS}{$state}}) {
  2880         5744  
140 2880         2079 $self->add_transition($state,$symbol,@{$NFA1->{_TRANSITIONS}{$state}{$symbol}});
  2880         4714  
141             }
142             }
143             # remove current final state and saves it for future reference
144 1022         1016 my $oldfinal = pop(@{$self->{_FINAL_STATES}});
  1022         1428  
145             # add new epsilon transition from the old final state of $self to the start state of NFA1
146 1022         1557 $self->add_transition($oldfinal,$self->get_epsilon_symbol(),$NFA1->get_start());
147             # mark the final state of NFA1 as the final state of $self
148 1022         1942 $self->add_final($NFA1->get_final());
149             # states not renumbered - can done explicity by user
150 1022         5293 return;
151             }
152              
153             sub prepend_nfa {
154 0     0 0 0 my $self = shift;
155 0         0 my $NFA = shift;
156             # clone $NFA
157 0         0 my $NFA1 = $NFA->clone();
158             # pinch off self - ensures a single final state
159             # pinch off self - ensures a single final state
160 0         0 $self->pinch();
161             # pinch off $NFA1 to ensure a single final state
162 0         0 $NFA1->pinch();
163             # ensure unique state names
164 0         0 $self->ensure_unique_states($NFA1,crypt(rand 8,join('',[rand 8, rand 8])));
165             # sychronize epsilon symbol
166 0 0       0 if ($NFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
167 0         0 $NFA1->rename_symbol($NFA1->get_epsilon_symbol(),$self->get_epsilon_symbol());
168             }
169             # add new states from NFA1
170 0         0 foreach my $state ($NFA1->get_states()) {
171 0         0 $self->add_state($state);
172             };
173             # add new symbols from NFA1
174 0         0 foreach my $symbol ($NFA1->get_symbols()) {
175 0         0 $self->add_symbol($symbol);
176             }
177             # add transitions from NFA1
178 0         0 foreach my $state (keys %{$NFA1->{_TRANSITIONS}}) {
  0         0  
179 0         0 foreach my $symbol (keys %{$NFA1->{_TRANSITIONS}{$state}}) {
  0         0  
180 0         0 $self->add_transition($state,$symbol,@{$NFA1->{_TRANSITIONS}{$state}{$symbol}});
  0         0  
181             }
182             }
183             # remove current final state of $NFA1 and saves it for future reference
184 0         0 my $oldfinal = pop(@{$NFA1->{_FINAL_STATES}});
  0         0  
185             # add new epsilon transition from the old final state of $NFA1 to the start state of $self
186 0         0 $self->add_transition($oldfinal,$self->get_epsilon_symbol(),$self->get_start());
187             # mark the final state of NFA1 as the final state of $self
188 0         0 $self->set_start($NFA1->get_start());
189             # states not renumbered - can done explicity by user
190 0         0 return;
191             }
192              
193             sub or_nfa {
194 112     112 0 125 my $self = shift;
195 112         125 my $NFA1 = shift;
196             # pinch off self - ensures a single final state
197 112         218 $self->pinch();
198             # pinch off $NFA1 to ensure a single final state
199 112         192 $NFA1->pinch();
200             # ensure unique state names
201 112         2671 $self->ensure_unique_states($NFA1,crypt(rand 8,join('',[rand 8, rand 8])));
202             # sychronize epsilon symbol
203 112 50       233 if ($NFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
204 0         0 $NFA1->rename_symbol($NFA1->get_epsilon_symbol(),$self->get_epsilon_symbol());
205             }
206             # add new states from NFA1
207 112         261 foreach my $state ($NFA1->get_states()) {
208 2154         3086 $self->add_state($state);
209             };
210             # add new symbols from NFA1
211 112         415 foreach my $symbol ($NFA1->get_symbols()) {
212 273         498 $self->add_symbol($symbol);
213             }
214             # add transitions from NFA1
215 112         127 foreach my $state (keys %{$NFA1->{_TRANSITIONS}}) {
  112         720  
216 2042         1322 foreach my $symbol (keys %{$NFA1->{_TRANSITIONS}{$state}}) {
  2042         4206  
217 2042         1348 $self->add_transition($state,$symbol,@{$NFA1->{_TRANSITIONS}{$state}{$symbol}});
  2042         3191  
218             }
219             }
220             # save old start states
221 112         388 my $start1 = $self->get_start();
222 112         211 my $start2 = $NFA1->get_start();
223             # create new start state
224 112         4508 my $newstart = crypt(rand 8,join('',[rand 8, rand 8]));
225 112         338 $self->add_state($newstart);
226             # set this new state as the start
227 112         264 $self->set_start($newstart);
228             # add the final state from NFA1
229 112         321 $self->add_final($NFA1->get_final());
230             # create transitions to old start states from new start state
231 112         279 $self->add_transition($newstart,$self->get_epsilon_symbol(),$start1);
232 112         238 $self->add_transition($newstart,$self->get_epsilon_symbol(),$start2);
233             # the result is not pinched, but it is in PFA->or_pfa because the epsilon
234             # transition is required to convert a PFA properly to an NFA...a similar
235             # restriction may occur here...in this case, uncomment the following line
236             #$self->pinch();
237 112         299 return;
238             }
239              
240             sub kleene {
241 132     132 0 149 my $self = shift;
242 132         1963 my $newstart = crypt(rand 8,join('',[rand 8, rand 8]));
243 132         1525 my $newfinal = crypt(rand 8,join('',[rand 8, rand 8]));
244             # pinch off self - ensures a single final state
245 132         276 $self->pinch();
246 132         259 my $oldstart = $self->get_start();
247 132         117 my $oldfinal = pop(@{$self->{_FINAL_STATES}});
  132         238  
248             # add new states
249 132         274 $self->add_state($newstart,$newfinal);
250             # set start
251 132         249 $self->set_start($newstart);
252             # set final
253 132         255 $self->add_final($newfinal);
254             # $oldfinal->$oldstart
255 132         301 $self->add_transition($oldfinal,$self->get_epsilon_symbol(),$oldstart);
256             # $newstart->$oldstart
257 132         224 $self->add_transition($newstart,$self->get_epsilon_symbol(),$oldstart);
258             # $oldfinal->$newstart
259 132         227 $self->add_transition($oldfinal,$self->get_epsilon_symbol(),$newfinal);
260             # $newstart->$newfinal
261 132         267 $self->add_transition($newstart,$self->get_epsilon_symbol(),$newfinal);
262 132         213 return;
263             }
264              
265             sub pinch {
266 2400     2400 0 2036 my $self = shift;
267             # do only if there is more than one final state
268 2400         1615 my $newfinal = join(',',@{$self->{_FINAL_STATES}});
  2400         3895  
269 2400         3832 $self->add_state($newfinal);
270 2400         1864 while (@{$self->{_FINAL_STATES}}) {
  4884         6887  
271             # design decision - remove all final states so that the common
272             # one is the only final state and all former final states have an
273             # epsilon transition to it - could prove costly for NFA->to_dfa, so
274             # this could change
275 2484         1770 my $state = pop(@{$self->{_FINAL_STATES}});
  2484         2699  
276             # add new transition unless it is to the final state itself
277 2484 100       4326 if ($state ne $newfinal) {
278 168         320 $self->add_transition($state,$self->get_epsilon_symbol(),$newfinal)
279             }
280             }
281 2400         3600 $self->add_final($newfinal);
282             # FA->number_states() could be used here, but the user may not
283             # want the states renamed, so it can be used explicitly
284 2400         1817 return;
285             }
286              
287             sub reverse {
288 0     0 0 0 my $self = shift;
289             # pinch
290 0         0 $self->pinch();
291             # make a distinct copy of self
292 0         0 my $NFA1 = $self->clone();
293             # reset out $self->{_TRANSITIONS}
294 0         0 $self->{_TRANSITIONS} = {};
295             # swap start and final states
296 0         0 my $start = $self->get_start();
297 0         0 $self->set_start(pop(@{$self->{_FINAL_STATES}}));
  0         0  
298 0         0 $self->add_final($start);
299             # cycle through transitions and reverse arcs
300 0         0 foreach my $state (keys %{$NFA1->{_TRANSITIONS}}) {
  0         0  
301 0         0 foreach my $symbol (keys %{$NFA1->{_TRANSITIONS}{$state}}) {
  0         0  
302 0         0 foreach my $destination (@{$NFA1->{_TRANSITIONS}{$state}{$symbol}}) {
  0         0  
303 0         0 $self->add_transition($destination,$symbol,$state);
304             }
305             }
306             }
307 0         0 return;
308             }
309              
310             sub rename_state {
311 1     1 0 3 my $self = shift;
312 1         3 my $oldname = shift;
313 1         2 my $newname = shift;
314             # make sure $oldname is an actual state in this FA
315 1 50       7 if (!$self->is_state($newname)) {
316 1 50       5 if ($self->is_state($oldname)) {
317             # replace name in _STATES array
318 1         2 my $i = 0;
319 1         6 foreach ($self->get_states()) {
320 11 100       19 if ($_ eq $oldname) {
321 1         4 $self->{_STATES}[$i] = $newname;
322 1         4 last;
323             }
324 10         13 $i++;
325             }
326             # replace name if start state
327 1 50       17 if ($self->is_start($oldname)) {
328 0         0 $self->set_start($newname);
329             }
330             # replace transitions
331 1         2 foreach my $state (keys %{$self->{_TRANSITIONS}}) {
  1         20  
332 37         30 foreach my $symbol (keys %{$self->{_TRANSITIONS}{$state}}) {
  37         96  
333 37         29 my $j = 0;
334 37         27 foreach my $next (@{$self->{_TRANSITIONS}{$state}{$symbol}}) {
  37         62  
335             # rename destination states
336 43 100       70 if ($self->{_TRANSITIONS}{$state}{$symbol}[$j] eq $oldname) {
337 1         3 $self->{_TRANSITIONS}{$state}{$symbol}[$j] = $newname;
338             }
339 43         38 $j++;
340             }
341             # rename start state
342 37 100       64 if ($state eq $oldname) {
343 1         5 $self->add_transition($newname,$symbol,@{$self->{_TRANSITIONS}{$state}{$symbol}});
  1         7  
344             }
345             }
346 37 100       63 if ($state eq $oldname) {
347             # delete all transitions of old state
348 1         6 delete($self->{_TRANSITIONS}{$state});
349             }
350             }
351             # replace final states
352 1         4 $i = 0;
353 1         6 foreach ($self->get_final()) {
354 1 50       4 if ($_ eq $oldname) {
355 0         0 $self->{_FINAL_STATES}[$i] = $newname;
356             }
357 1         2 $i++;
358             }
359             #
360             } else {
361 0         0 print STDERR "Warning: $oldname is not a current state\n";
362             }
363             } else {
364 0         0 print STDERR "Warning: $newname is a current state\n";
365             }
366 1         5 return;
367             }
368              
369             # renames symbol
370             sub rename_symbol {
371 0     0 0 0 my $self = shift;
372 0         0 my $oldsymbol = shift;
373 0         0 my $newsymbol = shift;
374             # make sure $oldsymbol is a symbol and do not bother if
375             # $newsymbol ne $oldsymbol
376 0 0 0     0 if ($self->is_symbol($oldsymbol) && $newsymbol ne $oldsymbol) {
377             # change in $self->{_SYMBOLS}
378 0         0 my $i = 0;
379 0         0 foreach ($self->get_symbols()) {
380 0 0       0 if ($_ eq $oldsymbol) {
381 0         0 $self->{_SYMBOLS}[$i] = $newsymbol;
382 0         0 last;
383             }
384 0         0 $i++;
385             }
386             # change in $self->{_TRANSITIONS}
387             # replace transition symbols
388 0         0 foreach my $state (keys %{$self->{_TRANSITIONS}}) {
  0         0  
389 0         0 foreach my $symbol (keys %{$self->{_TRANSITIONS}{$state}}) {
  0         0  
390 0 0       0 if ($symbol eq $oldsymbol) {
391 0         0 $self->add_transition($state,$newsymbol,@{$self->{_TRANSITIONS}{$state}{$symbol}});
  0         0  
392 0         0 delete($self->{_TRANSITIONS}{$state}{$symbol});
393             }
394             }
395             }
396             # also look at $self->{_EPSILON}
397 0 0       0 if ($self->get_epsilon_symbol() eq $oldsymbol) {
398 0         0 $self->set_epsilon($newsymbol);
399             }
400             }
401 0         0 return;
402             }
403              
404             sub add_transition {
405 19652     19652 0 15232 my $self = shift;
406 19652         14751 my $state = shift;
407 19652         13308 my $symbol = shift;
408             # adds state if not already added
409 19652         30671 $self->add_state($state);
410             # adds symbol if not already added
411 19652         31180 $self->add_symbol($symbol);
412 19652         16735 foreach my $next (@_) {
413 20729 50       13448 if (!$self->is_member($next,@{$self->{_TRANSITIONS}{$state}{$symbol}})) {
  20729         56727  
414 20729         13543 push (@{$self->{_TRANSITIONS}{$state}{$symbol}},$next);
  20729         38064  
415             }
416             }
417 19652         34844 return;
418             }
419              
420             sub get_transition_on {
421 26950     26950 0 18933 my $self = shift;
422 26950         18260 my $state = shift;
423 26950         19223 my $symbol = shift;
424 26950         25857 my @ret = undef;
425 26950 50 33     43994 if ($self->is_state($state) && $self->is_symbol($symbol)) {
426 26950 100       59444 if (defined($self->{_TRANSITIONS}{$state}{$symbol})) {
427 10725         7583 @ret = @{$self->{_TRANSITIONS}{$state}{$symbol}};
  10725         28960  
428             }
429             }
430 26950         270943 return @ret;
431             }
432              
433             sub set_epsilon {
434 1099     1099 0 953 my $self = shift;
435 1099         949 my $epsilon = shift;
436 1099         1041 $self->{_EPSILON} = $epsilon;
437 1099         903 return;
438             }
439              
440             sub get_epsilon_symbol {
441 43938     43938 0 33637 my $self = shift;
442 43938         89787 return $self->{_EPSILON};
443             }
444              
445             sub get_epsilon_transitions {
446 19713     19713 0 14968 my $self = shift;
447 19713         14124 my $state = shift;
448 19713         15743 my @ret = ();
449 19713 50       31596 if ($self->is_state($state)) {
450 19713 100       40584 if (defined($self->{_TRANSITIONS}{$state}{$self->get_epsilon_symbol()})) {
451 12903         9011 @ret = @{$self->{_TRANSITIONS}{$state}{$self->get_epsilon_symbol()}};
  12903         16291  
452             }
453             }
454 19713         202450 return @ret;
455             }
456              
457             sub delete_epsilon {
458 0     0 0 0 my $self = shift;
459 0         0 delete($self->{_EPSILON});
460 0         0 return;
461             }
462              
463             sub to_dfa {
464 120     120 0 795 my $self = shift;
465 120         255 my @Dstates = (); # stack of new states to find transitions for
466             # New DFA object reference
467 120         648 my $DFA = FLAT::Legacy::FA::DFA->new();
468             # Initialize DFA start state by performing e-closure on the NFA start state
469 120         446 my @Start = $self->epsilon_closure($self->get_start());
470             # Add this state to Dstates - subsets stored as anonymous arrays (no faking here!)
471 120         405 push(@Dstates,[sort(@Start)]);
472             # Serialize subset into new state name - i.e, generate string-ified name
473 120         306 my $ns = join('_',@Start);
474             # Add start state to DFA (placeholder Dtran not used)
475 120         372 $DFA->set_start($ns);
476             # Add new state (serialized name) to DFA state array
477 120         264 $DFA->add_state($ns);
478             # Check if start state is also final state, if so add
479 120         232 foreach my $s (@Start) {
480 537 100 66     898 if ($self->is_final($s) && !$DFA->is_final($ns)) {
481 4         13 $DFA->add_final($ns);
482             }
483             }
484             # Loop until Dstate stack is exhausted
485 120         293 while (@Dstates) {
486             # pop next state off to check
487 2034         1828 my @T = @{pop @Dstates};
  2034         4602  
488             # Serialize subset into a string name
489 2034         5338 my $CURR_STATE = join('_',@T);
490             # loop over each input symbol
491 2034         4471 foreach my $symbol ($self->get_symbols()) {
492             # Obviously do not add the epsilon symbol to the dfa
493 6090 100       7813 if ($symbol ne $self->get_epsilon_symbol()) {
494             # Add symbol - add_symbol ensures set of symbols is unique
495 4056         7377 $DFA->add_symbol($symbol);
496             # Get new subset of transition states
497 4056         6593 my @new = $self->epsilon_closure($self->move($symbol,(@T)));
498             # Serialize name of new state
499 4056         11946 $ns = join('_',@new);
500             # Add transition as long as $ns is not empty string
501 4056 100       13955 if ($ns !~ m/^$/) {
502 2541         7501 $DFA->add_transition($CURR_STATE,$symbol,$ns);
503             # Do only if this is a new state and it is not an empty string
504 2541 100       4790 if (!$DFA->is_state($ns)) {
505             # add subset to @Dstates as an anonymous array
506 1914         4384 push(@Dstates,[@new]);
507 1914         4164 $DFA->add_state($ns);
508             # check to see if any NFA final states are in
509             # the new DFA states
510 1914         2029 foreach my $s (@new) {
511 12966 100 100     18712 if ($self->is_final($s) && !$DFA->is_final($ns)) {
512 333         798 $DFA->add_final($ns);
513             }
514             }
515             }
516             }
517             }
518             }
519             }
520 120         884 return $DFA;
521             }
522              
523             sub move {
524 4056     4056 0 3590 my $self = shift;
525 4056         3007 my $symbol = shift;
526 4056         7443 my @subset = @_; # could be one state, could be a sub set of states...
527 4056         3977 my @T = ();
528             # Loop over subset until exhausted
529 4056         5796 while (@subset) {
530             # get a state from the subset
531 26950         24782 my $state = pop @subset;
532             # get all transitions for $t, and put the in @u
533 26950         36005 my @u = $self->get_transition_on($state,$symbol);
534 26950         31395 foreach (@u) {
535 30013 100       57436 if (defined($_)) {
536             # Add to new subset if not there already
537 13788 100       26635 if (!$self->is_member($_,@T)) {
538 10667         22044 push(@T,$_);
539             }
540             }
541             }
542             }
543             # Returns ref to sorted subset array instead of list to preserve subset
544 4056         12219 return sort(@T);
545             }
546              
547             sub epsilon_closure {
548 4176     4176 0 3760 my $self = shift;
549 4176         5173 my @subset = @_; # could be one state, could be a sub set of states...
550             # initialize @closure with provided sub set
551 4176         4817 my @closure = @subset;
552             # loop over subset until exhausted
553 4176         6242 while (@subset) {
554             # get a state from the subset
555 19713         19262 my $state = pop @subset;
556             # get all epsilon transitions for $state, and put the in @u
557 19713         25561 my @u = $self->get_epsilon_transitions($state);
558             # Loop over subset
559 19713         30268 foreach (@u) {
560 19068 50       26380 if (defined($_)) {
561 19068 100       36888 if (!$self->is_member($_,@closure)) {
562 8926         8406 push(@closure,$_);
563             # Add state to states that must be checked for e-closure
564 8926         15571 push(@subset,$_);
565             }
566             }
567             }
568             }
569             # Returns ref to sorted subset array instead of list to preserve subset
570 4176         15011 return sort(@closure);
571             }
572              
573             sub info {
574 0     0 0   my $self = shift;
575 0           my $out = '';
576 0           $out .= sprintf ("States : ");
577 0           foreach ($self->get_states()) {
578 0           $out .= sprintf "'$_' ";
579             }
580 0           $out .= sprintf ("\nStart State : '%s'\n",$self->get_start());
581 0           $out .= sprintf ("Final State(s) : ");
582 0           foreach ($self->get_final()) {
583 0           $out .= sprintf "'$_' ";
584             }
585 0           $out .= sprintf ("\nAlphabet : ");
586 0           foreach ($self->get_symbols()) {
587 0           $out .= sprintf "'$_' ";
588             }
589 0 0         if (defined($self->get_epsilon_symbol())) {
590 0           $out .= sprintf("\nEPSILON Symbol : '%s'",$self->get_epsilon_symbol());
591             }
592 0           $out .= sprintf ("\nTransitions :\n");
593 0           foreach my $state ($self->get_states()) {
594 0           foreach my $symbol (keys %{$self->{_TRANSITIONS}{$state}}) {
  0            
595 0 0         if ($symbol ne $self->get_epsilon_symbol()) {
596 0           $out .= sprintf("\t('%s'),'%s' --> '%s'\n",$state,$symbol,join('\',\'',$self->get_transition_on($state,$symbol)));
597             } else {
598 0           $out .= sprintf("\t('%s'),'%s' --> '%s'\n",$state,$symbol,join('\',\'',$self->get_epsilon_transitions($state)));
599             }
600             }
601             }
602 0           return $out;
603             }
604              
605             sub serialize {
606 0     0 0   my $self = shift;
607 0           my $out = '';
608 0           $out .= sprintf("START :: %s\n",$self->get_start());
609 0           $out .= sprintf("FINAL :: %s\n",join(',',$self->get_final()));
610 0 0         if (defined($self->get_epsilon_symbol())) {
611 0           $out .= sprintf("EPSILON :: %s\n",$self->get_epsilon_symbol());
612             }
613 0           $out .= "\n";
614 0           foreach my $state ($self->get_states()) {
615 0           $out .= sprintf("%s:\n",$state);
616 0           foreach my $symbol (keys %{$self->{_TRANSITIONS}{$state}}) {
  0            
617 0 0         if ($symbol ne $self->get_epsilon_symbol()) {
618 0           $out .= sprintf("$symbol %s\n",join(',',$self->get_transition_on($state,$symbol)));
619             } else {
620 0           $out .= sprintf("$symbol %s\n",join(',',$self->get_epsilon_transitions($state)));
621             }
622             }
623 0           $out .= sprintf("\n");
624             }
625 0           return $out;
626             }
627              
628             sub generate_random {
629 0     0 0   my $self = shift;
630             }
631              
632             sub generate_from_strings {
633 0     0 0   my $self = shift;
634 0           return "Coming soon!";
635             }
636              
637             1;
638              
639             __END__