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; |