File Coverage

blib/lib/DFA/Simple.pm
Criterion Covered Total %
statement 9 98 9.1
branch 0 46 0.0
condition 0 6 0.0
subroutine 3 12 25.0
pod 7 9 77.7
total 19 171 11.1


line stmt bran cond sub pod time code
1             package DFA::Simple;
2            
3 1     1   543 use strict;
  1         2  
  1         29  
4 1     1   5 use warnings;
  1         2  
  1         25  
5 1     1   4 use Carp;
  1         2  
  1         947  
6            
7             our $VERSION = "0.34";
8            
9             # XXX: looks more like an instance variable
10             my $CurrentStateTable = [];
11            
12             =head1 NAME
13            
14             DFA::Simple - A Perl module to implement simple Discrete Finite Automata
15            
16             =head1 SYNOPSIS
17            
18             my $Obj = new DFA::Simple
19            
20             or
21            
22             my $Obj = new DFA::Simple $Transitions;
23            
24             or
25            
26             my $Obj = new DFA::Simple $Actions, $StateRules;
27            
28             $Obj->Actions = [...];
29             my $Trans = $LP->Actions;
30            
31             $Obj->StateRules = [...];
32             my $StateRules = $LP->StateRules;
33            
34            
35             =head1 DESCRIPTION
36            
37             my $Obj = new DFA::Simple $Actions,[States];
38            
39             This creates a simple automaton with a finite number of individual states.
40             The short version is that state numbers are just indices into the array.
41            
42             The state basically binds the rest of the machine together:
43            
44             =over 8
45            
46             =item 1. There might be something you want done whenever you enter a given state (Transition Table)
47            
48             =item 2. There might be something you want done whenever you leave a given state (Transition Table)
49            
50             =item 3. You can go to some states from the current state (Action table)
51            
52             =item 4. There are tests to decide whether you should go to that new state (Action table)
53            
54             =item 5. There are conditional tasks you can do while sitting in that new state (Action table)
55            
56             =back
57            
58             This structure may remind you of the SysV run-level concepts.
59             It is very similar.
60            
61             At run time you don't typically feed any state numbers to the finite machine;
62             you ignore them. Rather your program may read inputs or such. The tests for
63             the state transition would examine this input, or some other variables to
64             decide which new state to go to. Whenever your code has gotten enough input,
65             it would call the C method. This method runs through
66             the tests, and carries out the state transitions ("firing the rules").
67            
68             =head2 The State Definitions, Tests, and Transitions
69            
70             As for where the state definitions, tests, and transitions come from: you have
71             to define them yourself, or write a program to do that. There are techniques
72             for converting Phase Structure grammars into state machines (usually thru
73             converting it to Chomsky Normal form, and such), or by drawing bubble diagrams.
74             In the case of the bubble diagram, I usually just number each bubble
75             sequentially from left to right. The arc (and its condition) will tell me most
76             of how to build the Action Table. What the bubble is supposed to do will tell
77             me how to build the Transition Table and the last column of the Action Table.
78            
79             To support these, the object is composed of the following three things (with
80             methods to match):
81            
82             =over 1
83            
84             =item I
85            
86             The object has a particular state it is in; a specific state from a set of
87             possible states
88            
89             =item I
90            
91             The object when entering or leaving a state may perform some action.
92            
93             =item I
94            
95             The object has rules for determining what its next state should be, and how to
96             get there.
97            
98             =back
99            
100             =head2 Example
101            
102             Before we get into the deep details, I'll present a quick example. First,
103             here is the output:
104            
105             [randym@a Out]$ perl tmp.pl
106             Intro
107            
108             I will force us to silently go to state 1, then 2, then 3:
109             Greetings
110             Am Here (in state 1)
111             Bye
112             Am Here (in state 3)
113            
114             Resetting:
115             Intro
116             I will force us to fail to go to a new state:
117             Unusual circumstances?
118             at tmp.pl line 54
119            
120            
121             And here is the example code:
122            
123             use DFA::Simple;
124            
125             #A table of what to do when entering or leaving a state.
126             my $Transitions =[
127             #Say "Intro" when entering state; do nothing when leaving
128             [sub {print "Intro\n";}, undef],
129            
130             #Say "Greetings" when entering state, do nothing when leaving
131             [sub {print "Greetings\n";}, undef],
132            
133             #When entering, do nothing, when leaving do nothing
134             [undef,undef],
135            
136             #When entering say "Bye", when leaving do nothing
137             [sub {print "Bye\n";}, undef],
138             ];
139            
140            
141             # A global variable
142             my $BogusTest=0;
143            
144             # Our state table.
145             my $States =[
146             #State #0
147             [
148             #Next State, Test that must be true or return true if we are to go
149             #into that state, what we do while /in/ that state
150             [1, sub{$BogusTest}, sub{print "Am Here (in state 1)\n"}],
151            
152             #We can't go to any other state
153             ],
154            
155             #State 1
156             [
157             # We can go to state #2 from state #1 if the test succeeds, but we
158             # don't really do anything there
159             [2, sub{$BogusTest}, ],
160             ],
161            
162             #State 2
163             [
164             #We can go to state #1 again, but we do nothing
165             [1, sub{$BogusTest}, ],
166            
167             # If the above test(s) fail, the undef below will force us to go
168             # into state #3
169             [3, undef, sub {print "Am Here (in state 3)\n";}],
170             ],
171             ];
172            
173             my $F=new DFA::Simple $Transitions, $States;
174             $F->State(0);
175            
176             print "\nI will force us to silently go to state 1, then 2, then 3:\n";
177             $BogusTest=1;
178             #Drive the state machine thru one transition
179             $F->Check_For_NextState();
180             #Drive the state machine thru one transition
181             $F->Check_For_NextState();
182            
183             #Force us to go to state 3
184             $BogusTest=0;
185             #Drive the state machine thru one transition
186             $F->Check_For_NextState();
187            
188             print "\nReseting:\n";
189             $F->State(0);
190             print "I will force us to fail to go to a new state:\n";
191             $BogusTest=0;
192             $F->Check_For_NextState();
193            
194            
195             =head2 State
196            
197             C is a method that can get the current state or initiate a transition to
198             a new state.
199            
200             my $S = $Obj->State;
201            
202             $Obj->State($NewState);
203            
204             The last one leaves the current state and goes to the specified I.
205             If the current state is defined, its I will be called (see
206             below). Then the new states I will be called (if defined)
207             (see below). Caveat, no check is made to see if the new state is the same as
208             the old state; this can be used to `reset' the state.
209            
210             =head2 Actions
211            
212             C is a method that can set or get the objects list of actions to
213             perform when entering or leaving a particular state.
214            
215             my $Actions = $Obj->Actions;
216            
217             $Obj->Actions([
218             [StateEnterCodeRef, StateExitCodeRef],
219             ]);
220            
221            
222             I is an array reference describing what to do when entering and
223             leaving various states. When a state is entered, its I
224             will be called (if defined). When a state is left (as in going to a new
225             state) its I will be called (if defined).
226            
227            
228             =head2 StateRules
229            
230             my $StateRules = [
231             #Rules for state 0
232             [
233             [NextState, Test, Thing to do after getting there
234             ],
235            
236             #Rules for state 1
237             [
238             ...
239             ],
240             ];
241            
242             The I is a set of tables used to select the next state. For the
243             current state, each item in the table is sequentially examined. Each rule has
244             a test to see if we should perform that action. The test is considered to have
245             `passed' if it is undefined, or the coderef returns a true. The first rule
246             with a test that passes is used -- the state is changed, and the action is
247             carried out.
248            
249             The next section describes a different method of determining which rule to
250             employ.
251            
252             =head2 Running the machine
253            
254             To operate the state machine, first prime it:
255            
256             $Obj->State(0);
257            
258             Then tell it run a state transition:
259            
260             $Obj->Check_For_NextState();
261            
262            
263             =head1 AUGMENTED TRANSITION NETWORKS
264            
265             The state machine has a second mode of operation -- every rule with a test that
266             passes is considered. Since this is nondeterministic (we can't tell which rule
267             is the correct one), this machine also employs special I mechanisms
268             to undo choosing the wrong rule. This type of state machine is called an
269             'Augmented Transition Network.'
270            
271             For the most part, augmented transition networks are just like the state
272             machines described earlier, but they also have two more tables (and four more
273             registers).
274            
275             =over 1
276            
277             =item I
278            
279             You can push a stack onto the stack, or pop one off. The register frame is
280             saved and restored as well.
281            
282             =item I
283            
284             The object has the method for storing and retrieving information about its
285             processing. Everything that you may want to have undone should be stored here.
286             When the state machine decides it won't undo anything, then it can pass the
287             information to the rest of the system.
288            
289             =back
290            
291             =head2 The State Stack
292            
293             $Obj->Hold;
294             $Obj->Retrieve;
295             $Obj->Commit;
296            
297             The nondeterminancy is handled in a guess and back up fashion.
298             If more than one transition rule is possible, the current state (including
299             the registers) is saved. Each of the possible transition rules is run; if it
300             executes C, the current state will be retrieved, and the next eligible
301             transition will be attempted.
302            
303             =over 1
304            
305             =item C will save the current state of the automaton, including the
306             registers.
307            
308             =item C will restore the automaton's previously saved state and
309             registers. This is called by a state machine action when it realizes that it
310             is in the wrong state.
311            
312             =item C will indicate that the previous restore is no longer needed, no
313             more backtracks will be performed. It is called by a state machine action that
314             is confident that it is in the proper state.
315            
316             =back
317            
318             =head2 Register
319            
320             $Obj->Register->{'name'}='fred';
321            
322             C is a method that can set or get the objects register reference.
323             This is a information that the actions, conditions, or transitions can employ
324             in their processing. The reference can be anything.
325            
326             C is important, since it is the automatons mechanism for undoing
327             actions. The data is saved before a questionable action is carried out, and
328             tossed out when a C is called. It is otherwise not used by the
329             object implementation.
330            
331             =head1 DESIGNING RECURSIVE AND AUGMENTED TRANSITION NETWORKS
332            
333             There are several issues involved with designing ATNs:
334             * Input and Output
335            
336             =head2 Input
337            
338             All input should be carefully thought out in an ATN -- this is for two reasons:
339            
340             =over 1
341            
342             =item * ATNs can back-up and retry different states, and
343            
344             =item * In multithreaded environments, several branches of the ATN may be
345             simultaneously operating.
346            
347             =back
348            
349             Some things to watch out for: reading from files, popping stuff off of global
350             lists, things like that. The current file position may change unexpectedly.
351            
352            
353             =head2 Output
354            
355             All IO should be carefully thought out in an ATN -- this is because ATNs can
356             back-up and retry different states, possibly invaliding any of the ATNs
357             results.
358            
359             print or other file writes
360             any commands that affect the system (link, unlink, rename, etc.)
361             C or otherwise changing any Perl variable.
362            
363             All output should be an ATN decides to commit to a branch
364            
365             =head2 Following all paths: special issues
366            
367             If you choose the option of having all the possible paths taken, there are some special issues.
368             First: what will the new state and registers be?
369             In this case, the registers are must all be.
370            
371             Be careful in single commit ATNs, with several nested branches.
372             These can lead to very inefficient scenarios,
373             due to the difficulty stop all of the branches of investigation.
374            
375            
376             =head1 INSTALLATION
377            
378             Install this module using CPAN, cf. L
379            
380             =head1 AUTHOR
381            
382             Randall Maas
383            
384             Maintenance by Alexander Becker (L)
385            
386             =cut
387            
388             #The structure of the node is:
389             #[CurrentState,Flags,Transitions,States, ...]
390            
391             sub new
392             {
393 0     0 1   my $self = shift;
394 0   0       my $class = ref($self) || $self;
395            
396 0           my $B = [];
397            
398             #Preserve old state and such
399 0 0         if (ref $self)
400             {
401 0           @{$B}=@{$self};
  0            
  0            
402             }
403            
404 0 0         if (@_) {$B->[2]=shift;}
  0            
405 0 0         if (@_) {$B->[3]=shift;}
  0            
406 0 0         if (@_) {$B->[4]=shift;}
  0            
407 0           return bless( $B, $class );
408             } # /new
409            
410            
411            
412             sub Actions
413             {
414 0     0 1   my $self = shift;
415            
416 0 0         if (@_)
417             {
418             #Called to set the actions
419 0           $self->[2] = shift;
420             }
421            
422 0           $self->[2];
423             } # /Actions
424            
425             sub State
426             {
427 0     0 1   my $self=shift;
428            
429 0           my $CState=$self->[0];
430            
431 0 0         if (!@_)
432             {
433             #Caller is just getting some info;
434 0           return $CState;
435             }
436            
437 0           my $Acts = $self->Actions;
438 0 0         if (!defined $Acts)
439             {
440 0           croak "DFA::Simple: No transition actions!\n";
441             }
442            
443 0 0         if (!defined $self->[3])
444             {
445 0           croak "DFA::Simple: No states defined!\n";
446             }
447            
448 0           my $NS = shift;
449 0           $CurrentStateTable=$self->[3]->[$NS];
450 0           $self->[0]=$NS;
451            
452             #Handle the state exit rule
453 0 0 0       if (defined $CState && defined $Acts->[$CState])
454             {
455 0           my $A;
456 0 0         if (defined $Acts->[$CState]->[1])
    0          
457             {
458 0           $A = $Acts->[$CState]->[1];
459             }
460             elsif (defined $Acts->[$CState]->[2])
461             {
462 0           $A = $Acts->[$CState]->[2];
463             }
464 0 0         $A->($self) if defined $A;
465             }
466            
467             #Handle the transition rule...
468 0 0         if (defined $Acts->[$NS]->[0])
469             {
470 0           my $A = $Acts->[$NS]->[0];
471 0           &$A($self); # XXX: use $A->($self);?
472             }
473             }
474            
475             sub Check_For_NextState
476             {
477 0     0 0   my $self = shift;
478 0 0         if (!defined($self->[0]))
479             {
480 0           $self->State(0);
481             }
482            
483 0           foreach my $I (@{$CurrentStateTable})
  0            
484             {
485             #Perform the test
486 0 0         if (defined $I->[1])
487             {
488 0           my $CodeRef=$I->[1];
489 0 0         if (!&$CodeRef($self)) {next;}
  0            
490             }
491            
492             #Set up for the next state;
493 0 0         if ($self->[0] ne $I->[0])
494             {
495 0           $self->State($I->[0]);
496             }
497            
498             #Do the rules
499 0 0         if (defined $I->[2]) {
500 0           &{$I->[2]}();
  0            
501             }
502            
503 0           return;
504             }
505            
506 0           croak "Unusual circumstances?\n";
507             }
508            
509             #Child ATN, used to investigate possible branch paths
510             sub Child
511             {
512 0     0 0   my $self=shift;
513 0           my $ARef=shift;
514            
515             #Setup up pointer to where our results go
516 0           $self->[5]=shift;
517            
518             #Setup commit/rollback flags to indicate nothing yet
519 0           $self->[1] |= 12;
520            
521             #Check to see if the other side has comitted...
522            
523             #Set up for the next state
524 0           my $NState=shift;
525 0 0         if ($self->[0] ne $NState)
526             {
527 0           $self->State($NState);
528             }
529             #Carry out the action coderef;
530 0 0         if (defined $ARef) {
531 0           $ARef->($self);
532             }
533            
534             #Run the state machine
535 0           $self->NextState();
536            
537             #Return value
538             # 0 or undef if the "abort" (or retrieve previous state) flag is set
539             # otherwise, results are good
540 0 0         return 1 if ($self->[1] & 4);
541 0           return undef;
542             }
543            
544             sub Register
545             {
546 0     0 1   my $self = shift;
547            
548 0 0         if (@_)
549             {
550             #Called to set the actions
551 0           $self->[4] = shift;
552             }
553 0           $self->[4];
554             }
555            
556             sub Hold
557             {
558 0     0 1   my $self=shift;
559             #Save the state and frame
560 0           push @{$self->[5]}, $self->State, [@{$self->Register}];
  0            
  0            
561             }
562            
563             sub Retrieve
564             {
565 0     0 1   my $self=shift;
566             #Check the flags see if we are in threaded mode
567 0 0         if ($self->[1] & 1)
568             {
569             #Set the flags to indicate a "Retrieve" operation
570 0           $self->[1] &= ~4;
571 0           return;
572             }
573            
574             #Otherwise, we are in a mode where we explicitly handle saving and restoring
575             #state.
576 0           $self->Register = pop @{$self->[5]};
  0            
577 0           $self->State(pop @{$self->[5]});
  0            
578             }
579            
580             sub Commit
581             {
582 0     0 1   my $self=shift;
583 0           my $CtlVar=$self->[5];
584            
585             #Indicate that no more processing in this thread should be done
586 0           $self->[1] &= ~8;
587            
588             #Lock it to prevent someone else from getting there
589 0           lock($$CtlVar);
590            
591             #Set up the stuff
592 0           $CtlVar->[0] = $self->[0];
593 0           $CtlVar->[1] = $self->[4];
594             }
595            
596             1;