line
stmt
bran
cond
sub
pod
time
code
1
# Original Author: Dale M. Amon
2
# $Id: FSM.pm 561 2022-12-29 18:54:15Z whynot $
3
# Copyright 2012, 2013, 2022 Eric Pozharski
4
# GNU LGPLv3
5
# AS-IS, NO-WARRANTY, HOPE-TO-BE-USEFUL
6
7
25
25
3543296
use strict;
25
435
25
836
8
25
25
150
use warnings;
25
60
25
656
9
25
25
670
use 5.010;
25
90
10
11
package Acme::FSM;
12
25
25
3564
use version 0.77; our $VERSION = version->declare( v2.3.5 );
25
16682
25
289
13
25
25
3039
use feature qw| switch |;
25
69
25
4256
14
15
25
25
185
use Carp qw| croak |;
25
78
25
84455
16
17
# TODO:202212202012:whynot: L<|/Linter>
18
19
=head1 NAME
20
21
Acme::FSM - pseudo Finite State Machine.
22
23
=cut
24
25
=head1 SYNOPSIS
26
27
my $bb = Acme::FSM->connect( { %options }, { %fst } );
28
$bb->process;
29
exit 0;
30
31
=cut
32
33
=head1 DESCRIPTION
34
35
B<(disclaimer)>
36
B is currently in proof-of-concept state.
37
There's a plan to accompany it with B with all diagnostics
38
avoided and run time checks in place.
39
And then with B with run time checks stripped.
40
Also, see L later in this POD|/BUGS AND CAVEATS>.
41
42
=head2 Concerning Inheritance
43
44
Through out code only methods are used to access internal state.
45
Supposedly, that will enable scaling some day later.
46
The only exception is L|/connect()> for obvious reasons.
47
48
Through out code neither internal state nor FST records are cached ever.
49
The only one seamingly inconsistent fragment is inside main loop when next
50
I<$state> is already found but not yet entered.
51
I<$action> is processed when the next I<$state> is set
52
(though, in some sense, not yet entered).
53
(If it doesn't make sense it's fine, refer to
54
L method|/process()> description later in this POD.)
55
56
This notice seems to be useles.
57
Instead, it might come handy someday.
58
59
=cut
60
61
=head2 Terminology
62
63
There're some weird loaded words in this POD,
64
most probably, poorly choosen
65
(but those are going to stay anyway).
66
So here comes disambiguation list.
67
68
=over
69
70
=item I<$action>
71
72
Special flag associated with next I<{state}> in a I<[turn]>
73
(covered in details in L|/process()> method description).
74
B<(note)> It may be applied to a I<[turn]> of I<{fst}> or I<{state}>.
75
76
=item blackboard
77
78
Shamelesly stolen from B.
79
Originally, it meant some opaque HASH passed around in B
80
where
81
user-code could store it's own ideas and such.
82
Now it's an object blessed into B
83
(for user-code still, I<{fsm}> has nothing to do with it).
84
85
=item entry
86
87
Layout of state records (see below) isn't particularly complicated.
88
However it's far from being uniform.
89
I is a placeholder name for any component of (unspecified) state record
90
when any semantics is irrelevant.
91
See also I<[turn]> and B below.
92
93
=item Finite State Machine
94
95
B.
96
97
=item Finite State Table
98
99
=item I<{fst}>
100
101
Collection of state records.
102
103
=item FSM
104
105
Acronym for I.
106
See above.
107
108
=item FST
109
110
Acronym for I.
111
See above.
112
113
=item internal state
114
115
=item I<{fsm}>
116
117
Some open-ended set of parameters dictating FSM's behaviour.
118
119
=item item
120
121
=item I<$item>
122
123
Something that makes sense for user-code.
124
B treats it as scalar with no internals
125
(mostly;
126
one exception is covered in L method|/diag()> description).
127
128
=item I<$namespace>
129
130
B uses elaborate schema to reach various callbacks
131
(three of them, at the time of writing).
132
This optional parameter is in use by this schema.
133
L method|/query()> has more.
134
135
=item I<$rule>
136
137
A scalar identifing a I<[turn]>.
138
One of opaque scalars returned by B callback
139
(the other is processed (modified or not) I<$item>).
140
L method|/process()> description has more.
141
142
=item B
143
144
Special piece of user-code
145
(it's required at construction (L method|/connect()>),
146
L method|/query_source()> describes how FSM reaches it).
147
Whatever it returns (in explicit scalar context) becomes I<$item>.
148
149
=item I<$state>
150
151
A scalar identifing a I<{state}> (see below) or parameter of I<{fsm}> (see
152
above).
153
154
=item state flow
155
156
Just like control flow but for I<$state>s.
157
158
=item state record
159
160
=item I<{state}>
161
162
One record in I<{fst}>.
163
The record consists of entries (see above).
164
L|/process()> method description has more.
165
Should be noted, in most cases "I<{state}>" should be read as
166
"I<$state> I<{state}>" instead,
167
but that starts to smell bufallo.
168
169
=item B
170
171
A mandatory callback associated with every I<{state}>.
172
L method|/process()> and
173
L method|/query_switch()>
174
descriptions have more.
175
176
=item I<$turn>
177
178
No such thing.
179
It's I<$rule> instead (see above).
180
181
=item I<[turn]>
182
183
Specially crafted entry in I<{state}>
184
(covered in details in L method|/process()> description).
185
Such entry describes what next I<$state> should be picked in state flow
186
and what to do with I<$item>.
187
188
=item turn map
189
190
This idiom is used in place of "C I<$rule> of I<[turn]>".
191
192
=back
193
194
=cut
195
196
=head1 B
197
198
$bb1 = Acme::FSM->connect( { %options1 }, %fst1 );
199
$bb2 = Acme::FSM->connect( { %options2 }, { %fst2 } );
200
$bb3 = $bb2->connect( { %options3 } );
201
202
Creates a blackboard object.
203
Blackboard isa HASH, it's free to use except special C<_> key;
204
that key is for I<{fsm}> exclusively.
205
First parameter isa I<%$options>, it's required
206
(pass empty HASH if nothing to say).
207
Defined keys are:
208
209
=over
210
211
=item I
212
213
(positive integer)
214
Sets a diagnostic threshold.
215
It's meaning is covered in L method|/diag()> documentation.
216
If C then set to C<1> (C<0> is B).
217
218
=item I
219
220
(scalar or C)
221
B operates on arbitrary items and there's a diagnostic service that
222
sometimes insists on somehow showing those arbitrary items.
223
It's up to user's code to process that arbitrary data and yield some scalar
224
represantation.
225
Refer to L method|/query_dumper()> documentation for
226
details.
227
Optional.
228
Simple stringifier is provided by L method|/query_dumper()>
229
itself.
230
231
=item I
232
233
(scalar or object(!) B[) ]
234
Sets a context for various parts of I<{fsm}> and services would be resolved.
235
No defaults.
236
Refer to L method|/query()> documentation for details.
237
238
=item I
239
240
(scalar or C)
241
Sets a source of items to process to be queried.
242
Required.
243
Refer to L method|/query_source()> documentation for
244
details.
245
246
=back
247
248
Second is FST (Finite State Table).
249
It's required for class construction and ignored (if any) for object
250
construction.
251
Difference between list and HASH is the former is copied into HASH internally;
252
the latter HASH is just saved as reference.
253
The FST is just borrowed from source object during object construction.
254
Thus, in the synopsis, I<$bb3> and I<$bb2> use references to HASH
255
I<%$fst2>.
256
An idea behind this is to minimize memory footprint.
257
OTOH, maninpulations with one HASH are effectevely manipulations with FST of
258
any other copy-object that borrowed that FST.
259
260
IOW, anything behind FST HASH of class construction or options HASH of object
261
construction isa trailer and Bed.
262
Obviously, there's no trailer in class construction with list FST.
263
264
=cut
265
266
my @options = qw| diag_level namespace source dumper |;
267
sub connect {
268
770
770
0
758115
my( $self, $opts ) = ( shift @_, shift @_ );
269
770
100
2916
ref $opts eq q|HASH| or croak sprintf
270
q|[connect]: {options} HASH is required, got (%s) instead|, ref $opts;
271
768
1355
my( $trailer, $leader );
272
768
100
2159
if( ref $self eq '' ) {
273
748
1451
$trailer = ref $_[0] eq q|HASH|;
274
748
100
2844
$self = bless { _ => { fst => $trailer ? shift @_ : { @_ }}}, $self;
275
748
1438
$leader = q|clean init with| }
276
else {
277
20
37
$trailer = @_;
278
20
38
$self = bless { _ => { %{$self->{_}} }}, ref $self;
20
158
279
20
48
$leader = q|stealing| }
280
768
100
8235
$self->{_}{$_} = delete $opts->{$_} // $self->{_}{$_} foreach @options;
281
768
100
2212
$self->{_}{diag_level} //= 1;
282
$self->diag( 3, q|%s (%i) items in FST|,
283
768
1276
$leader, scalar keys %{$self->{_}{fst}} );
768
3157
284
285
768
2470
$self->carp( qq|($_): unknown option, ignored| ) foreach keys %$opts;
286
768
100
2482
$self->carp( q|(source): unset| ) unless defined $self->{_}{source};
287
768
100
9301
$trailer = ref $_[0] eq q|HASH| ? keys %{$_[0]} : @_ if $trailer;
2
100
7
288
768
100
1590
$self->carp( qq|ignoring ($trailer) FST items in trailer| ) if $trailer;
289
$self->carp( q|FST has no {START} state| ) unless
290
768
100
2151
exists $self->{_}{fst}{START};
291
$self->carp( q|FST has no {STOP} state| ) unless
292
768
100
5817
exists $self->{_}{fst}{STOP};
293
768
20050
@{$self->{_}}{qw| state action |} = qw| START VOID |;
768
2221
294
768
2281
return $self }
295
296
=head1 B
297
298
$bb = Acme::FSM->connect( { }, \%fst );
299
$rc = $bb->process;
300
301
This is heart, brains, liver, and so on of B.
302
B is what does actual state flow.
303
That state flow is steered by records in I<%fst>.
304
Each record consists of:
305
306
=over
307
308
=item B callback
309
310
This is some user supplied code that
311
always consumes whatever B callback returns (at some point in past),
312
(optionally) regurgitates said input,
313
and decides what I<[turn]> to go next.
314
Except when in special I<{state}> (see below) it's supplied with one argument:
315
I<$item>.
316
In special states I<$item> is missing.
317
B returns two controls: I<$rule> and processed I<$item>.
318
What to do with returned I<$item> is determined by I<$action> (see below).
319
320
=item various I<[turn]>s
321
322
Each I<[turn]> spec is an ARRAY with two elements (trailing elements are
323
ignored).
324
First element is I<$state> to change to.
325
Second is I<$action> that sets what to do with I<$item> upon changing to named
326
I<$state>.
327
Here are known I<[turn]>s in order of logical treating decreasing.
328
329
=over
330
331
=item C
332
333
That I<[turn]> will be choosen by FSM itself when I<$item> at hands is
334
C
335
(as returned by B).
336
I isn't invoked -- I<$item> is void, there's nothing to call
337
I with.
338
However, I<$action> may change I<$item>.
339
340
=item C
341
342
That I<[turn]> will be choosen by FSM if I<$rule> is C
343
(as returned by B).
344
Idea behind this is to give an FST an option to bailout.
345
In original B that's not possible (except Bing, you know).
346
Also, see L|/Perl FALSE, undef, and uturn>.
347
348
=item C and/or C
349
350
If any (hence 'or') is present then I<$rule> returned by B is
351
treated as Perl boolean, except C it's handled by C.
352
That's how it is in original B.
353
If B always returns TRUE (or FALSE) then C (or C) can
354
be missing.
355
Also, see L|/tturn, fturn, and switch()>.
356
357
=item C
358
359
If neither C nor C is present then whatever I<$rule> would be
360
returned by B is treated as string.
361
That string is supposed to be a key in I<%$turns>.
362
I<$rule> returned is forced to be string (so if you dare to return objects,
363
such objects should have C<""> overloaded).
364
Also, see L|/Default For Turn Map>.
365
366
=back
367
368
I<$state> is treated as a key in FST hash, except when that's a special state.
369
Special states (in alphabetical order):
370
371
=over
372
373
=item C
374
375
Basically, it's just like C I<$state>.
376
All comments there (see below) apply.
377
It's added to enable break out from loop, do something, then get back in loop.
378
I<$state> is changed to C just before returning to caller
379
(I is called in C state).
380
The choice where to implicitly change state to C has been completely
381
arbitrary;
382
probably wrong.
383
384
=item C
385
386
Just like C state (see below, all comments apply).
387
While C is turned to C implicitly no other handling is made.
388
389
=item C
390
391
It's I<$state> set by B.
392
I<$action> (it's also set by B) is ignored
393
(if I<$action> is C then silently replaces with empty string),
394
B is invoked with no arguments
395
(there's not anything to process yet).
396
Whatever I<$item> could be returned is ignored.
397
I<$rule> is followed.
398
Thus C can't be followed.
399
See also L|/Special handling of START and CONTINUE>.
400
401
=item C
402
403
It's last state in the state flow.
404
I<$action> is retained
405
(that's what B will return)
406
(however, because it's processed before C state is acknowledged it must
407
not be C)
408
but otherwise ignored.
409
B is invoked with no arguments
410
(B of previous I<{state}> should have took care).
411
Whatever I<$item> could be returned is ignored.
412
Whatever I<$rule> could be returned is reported (at I<(basic trace)> level)
413
but otherwise ignored.
414
Then I<$action> is returned and state flow terminates.
415
416
=back
417
418
Supported I<$action>s (in alphabetical order):
419
420
=over
421
422
=item C
423
424
Drop whatever I<$item> at hands.
425
Request another.
426
427
If FST has such record:
428
429
somestate => { eturn => [ somestate => 'NEXT' ] }
430
431
then FSM will stay in C as long as I callback returns
432
C.
433
Thus consuming all resources available.
434
No options provided to limit that consumption.
435
436
=item C
437
438
Retains I<$item> uncoditionally.
439
That is, even if I<$item> isn't B it's kept anyway.
440
441
B, if FST has such record:
442
443
somestate => { eturn => [ somestate => 'SAME' ] }
444
445
then FSM will cycle here forever.
446
That is, since I isn't queried for other I<$item>
447
(what's the purpose of this action is anyway)
448
there's no way to get out.
449
450
=item C
451
452
Check if I<$item> is B, then go as with C or C otherwise.
453
That actually makes sense.
454
455
I<(note)> This action name is legacy of B;
456
Possibly, that's C something;
457
Someone can just speculate what C could mean.
458
459
=back
460
461
=back
462
463
=cut
464
465
sub process {
466
690
690
0
44287
my $self = shift @_;
467
690
1183
my( $branch, $turn );
468
469
# XXX:202201072033:whynot: C and C being handled specially is a side-effect of this extra sequence. Should be moved in the main loop with special handling. This results in there-be-dragons uncertainty.
470
690
1621
$self->diag( 3, q|{%s}(%s): entering|, $self->state, $self->action );
471
690
1527
$branch = $self->query_switch;
472
672
1322
$turn = $self->turn( $self->state, $branch );
473
580
1546
$self->diag( 5, q|{%s}(%s): switch returned: (%s)|, @$turn, $branch );
474
580
1270
$self->state( $turn->[0] );
475
580
1281
$self->action( $turn->[1] );
476
477
580
1083
my( $item, $dump ) = $self->query_source;
478
580
1661
$self->diag( 3, q|{%s}(%s): %s: going with|, @$turn, $dump );
479
480
# No one gets out of this loop without the state tables permission!
481
580
863
while ( 1 ) {
482
# We should never see an undefined state unless we've made a mistake.
483
# NOTE:202201072131:whynot: As a matter of fact, we don't now.
484
195461
320060
$self->verify( $self->fst( $self->state ),
485
$self->state, '', q|record|, q|HASH| );
486
487
195379
343449
( $branch, $item ) = $self->query_switch( $item );
488
195364
459361
$self->diag( 5, q|{%s}(%s): switch returned: (%s)|, @$turn, $branch );
489
195364
313101
$dump = $self->query_dumper( $item );
490
195361
559508
$turn = $self->turn( $self->state, $branch );
491
195276
449763
$self->diag( 3, q|{%s}(%s): %s: turning with|,
492
$turn->[0], $branch, $dump );
493
195276
400237
$self->state( $turn->[0] );
494
195274
398691
$self->action( $turn->[1] );
495
496
195272
390674
$self->diag( 5, q|{%s}(%s): going for|, @$turn );
497
195272
100
351906
$turn->[0] eq q|STOP| and last;
498
195033
100
322116
$turn->[0] eq q|BREAK| and last;
499
194896
100
347134
$turn->[1] eq q|SAME| and redo;
500
99082
100
187561
$turn->[1] eq q|NEXT| and next;
501
9334
100
100
26672
$turn->[1] eq q|TSTL| && defined $item and redo;
502
9316
100
17184
$turn->[1] eq q|TSTL| and next;
503
14
1197
croak sprintf q|[process]: {%s}(%s): unknown action|, @$turn }
504
continue {
505
99050
165228
( $item, $dump ) = $self->query_source;
506
99049
216182
$self->diag( 5, q|{%s}(%s): %s: going with|, @$turn, $dump ) }
507
508
376
842
$self->diag( 3, q|{%s}(%s): leaving|, @$turn );
509
# XXX:20121231215139:whynot: Nothing to B, leaving anyway.
510
376
658
$branch = $self->query_switch;
511
359
939
$self->diag( 5, q|{%s}(%s): switch returned: (%s)|, @$turn, $branch );
512
359
100
840
$self->diag( 3, q|{%s}(%s): changing state: (CONTINUE)|, @$turn )
513
->state( q|CONTINUE| ) if $turn->[0] eq q|BREAK|;
514
359
624
return $self->action }
515
516
517
=head1 METHODS AND STUFF
518
519
Access and utility methods to deal with various moves while doing The State
520
Flow.
521
These aren't forbidden for use from outside,
522
while being quite internal nevertheles.
523
524
=over
525
526
=cut
527
528
=item B
529
530
$rc = $self->query_rc( @args );
531
$rc = $self->verify( $rc, $state, $tag, $subject, $test );
532
533
Here comes rationale.
534
Writing (or should I say "composing"?) correct {fst} B style is hard
535
(I know what I'm talking about, I've made a dozen already).
536
The purpose of B is to check if the I<{fst}> at hands isn't fubar.
537
Nothing more, nothing less.
538
B is a placeholder for one of B methods,
539
I<$test> will be matched against C[. ]
540
Other arguments are to fill diagnostic output (if any).
541
I<$state> hints from what I<{state}> I<$rc> has been queried.
542
I<$subject> and I<$tag> are short descriptive name and actual value of I<$rc>.
543
Yup, dealing with B might be fubar too.
544
545
I<$rc> is passed through (or not).
546
This Bs if I<$rc> isn't B or C[ doesn't match ]
547
I<$test>.
548
549
=cut
550
551
# TODO:202202150137:whynot: Replace C with B, plz.
552
sub verify {
553
1487241
1487241
1
1893734
my $self = shift @_;
554
# XXX:202202092101:whynot: Nope, needs I<$state> because sometimes I<{state}> isn't entered yet.
555
1487239
2497269
my( $entry, $state, $what, $manifest, $test ) = @_;
556
1487239
100
2422160
defined $entry or croak sprintf q|[verify]: {%s}(%s): %s !isa defined|,
557
$state, $what, $manifest;
558
1486967
100
2406532
ref $entry eq $test or croak sprintf
559
q|[verify]: {%s}(%s): %s isa (%s), should be (%s)|,
560
$state, $what, $manifest, ref $entry, $test;
561
1486900
2331110
return $entry }
562
563
=item B
564
565
$bb->state eq 'something' and die;
566
$state = $bb->state( $new_state );
567
568
Queries and sets state of B instance.
569
Modes:
570
571
=over
572
573
=item no argument
574
575
Returns state of instance.
576
Note, Perl FALSE B parameter.
577
578
=item lone scalar
579
580
Sets I<$state> of instance.
581
Returns previous I<$state>.
582
583
=back
584
585
=cut
586
587
sub state {
588
1605548
1605548
1
2185622
my $self = shift @_;
589
1605547
100
2408622
unless( @_ ) {
100
590
1409563
2948855
return $self->{_}{state} }
591
0
0
elsif( 1 == @_ ) {
592
195983
294172
my $backup = $self->state;
593
195983
430399
$self->diag( 5, q|changing state: (%s) (%s)|, $backup, $_[0] );
594
195982
291443
$self->{_}{state} = shift @_;
595
195982
260276
return $backup }
596
else {
597
1
7
$self->carp( sprintf q|too many args (%i)|, scalar @_ );
598
1
39
return undef }}
599
600
=item B
601
602
%state = %{ $bb->fst( $state ) };
603
%state = %{ $bb->fst( $state => \%new_state ) };
604
$value = $bb->fst( $state => $entry );
605
$value = $bb->fst( $state => $entry => $new_value );
606
607
Queries and sets records and entries in I<{fst}>.
608
That is, not only entire I<{state}>s
609
but components of I<{state}> are reachable too.
610
Modes:
611
612
=over
613
614
=item query specific I<{state}> of specific I<$state>
615
616
Executed if one scalar is passed in.
617
Returns a I<{state}> reference with whatever entries are set.
618
Silently returns C if I<$state> is missing from I<{fst}>.
619
620
=item set I<{state}> of specific I<$state>
621
622
Executed if one scalar and HASH are passed in.
623
Sets a I<{state}> with key/value pairs from HASH,
624
creating one if necessary.
625
Created record isa copy of HASH, not a reference
626
(not a true deep copy though)
627
(empty I<\%new_state> is fine too)
628
(copying isn't by design, it's implementation's quirk).
629
Returns record as it was before setting
630
(C is returned if there were no such I<$state> before).
631
632
=item query specific I<$entry> of specific I<$state>
633
634
Executed if two scalars are passed in.
635
Returns an entry from named state record.
636
637
=item set specific I<$entry> of specific I<$state>
638
639
Executed if two scalars and anything else are passed in
640
(no implicit intelligence about third parameter).
641
Sets an entry in named state record,
642
creating one (entry) if necessary.
643
State record must exist beforehand.
644
Entry isa exact value of least argument, not a copy.
645
Returns whatever value I<$entry> just had
646
(C is returned if there were none such I<$entry> before).
647
648
=back
649
650
None checks are made, except record must exist (for two latter uses).
651
652
=cut
653
654
sub fst {
655
262513
262513
1
382558
my $self = shift @_;
656
262512
100
967723
unless( @_ ) {
100
100
100
100
100
657
1
4
$self->carp( q|no args| );
658
1
27
return undef }
659
0
100
0
elsif( 2 == @_ && ref $_[1] eq q|HASH| ) {
660
2
12
my $backup = $self->fst( $_[0] );
661
2
50
13
$self->diag( 3, q|%s {%s} record|,
662
( $backup ? q|updating| : q|creating| ), $_[0] );
663
# XXX:202202150056:whynot: Copy is a side-effect instead.
664
2
5
$self->{_}{fst}{shift @_} = {%{ pop @_ }};
2
8
665
2
7
return $backup }
666
0
0
elsif( !exists $self->{_}{fst}{$_[0]} ) {
667
91
345
$self->carp( qq|($_[0]): no such {fst} record| );
668
91
2300
return undef }
669
0
0
elsif( 1 == @_ ) {
670
195387
420929
return $self->{_}{fst}{shift @_} }
671
0
0
elsif( 2 == @_ ) {
672
67026
167457
return $self->{_}{fst}{shift @_}{shift @_} }
673
0
0
elsif( 3 == @_ ) {
674
3
11
my $backup = $self->fst( $_[0] => $_[1] );
675
3
100
15
$self->diag( 3, q|%s {%s}{%s} entry|,
676
( $backup ? q|updating| : q|creating| ), @_[0,1] );
677
3
9
$self->{_}{fst}{shift @_}{shift @_} = pop @_;
678
3
10
return $backup }
679
else {
680
1
8
$self->carp( sprintf q|too many args (%i)|, scalar @_ );
681
1
44
return undef }}
682
683
=item B
684
685
$bb->turn( $state ) eq '' or die;
686
$bb->turn( $state => 'uturn' )->[1] eq 'NEXT' or die;
687
688
Queries I<[turn]>s of arbitrary I<{state}>s.
689
B doesn't manipulate entries, use L method|/fst()> instead
690
L.
691
Modes:
692
693
=over
694
695
=item query expected behaviour
696
697
This mode is entered if there is lone scalar.
698
Such scalar is believed to be I<$state>.
699
Returns something that describes what kind of least special states are
700
present.
701
Namely:
702
703
=over
704
705
=item *
706
707
C is returned if I<$state> isn't present in the I<{fst}>
708
(also Bs).
709
Also see below.
710
711
=item *
712
713
Empty string is returned if there're I and/or I turns.
714
I hash is ignored in that case.
715
716
=item *
717
718
C is returned if there's turn map
719
(and neither I nor I is present).
720
B<(note)> In that case, B checks for I is indeed a HASH,
721
nothing more
722
(however B if that's not the case);
723
It may as well be empty;
724
Design legacy.
725
726
=item *
727
728
Returns C for C and C I<$state>s without any further
729
processing
730
(For those I<$state>s any I<$rule> is ignored and C enables I
731
callbacks to give more informative logs
732
(while that information is mangled anyway);
733
Probably bad idea).
734
735
=item *
736
737
C is returned if there's nothing to say --
738
neither I, nor I, nor turn map --
739
this record is kind of void.
740
The record should be studied to find out why.
741
Bs in that case.
742
743
=back
744
745
=item query specific I<[turn]>
746
747
Two scalars are I<$state> and specially encoded I<$rule>
748
(refer to L method|/query_switch()> about encoding).
749
If I<$rule> can't be decoded then Bs.
750
Returns (after verification) requested I<$rule> as ARRAY.
751
While straightforward I<[turn]>s (such as C, C, and such) could
752
be in fact queried through L method|/fst()> turn map needs bit more
753
sophisticated handling;
754
and that's what B does;
755
in fact asking for C will result in B.
756
I<$action> of C and C special states suffer implicit
757
defaulting to empty string.
758
759
=item anything else
760
761
No arguments or more then two is an non-fatal error.
762
Returns C (with B).
763
764
=back
765
766
=cut
767
768
# TODO:202202172011:whynot: As soon as supported perl is young enough change it to smartmatch, plz.
769
my %special_turns = map { $_ => 1 } qw| eturn uturn tturn fturn |;
770
# TODO:202202162030:whynot: Consider more elaborate (informative) returns.
771
sub turn {
772
392610
392610
1
579792
my $self = shift @_;
773
392610
50
1349014
unless( @_ ) {
100
100
100
774
1
4
$self->carp( q|no args| ); return undef }
1
25
775
0
100
0
elsif( 1 == @_ && !exists $self->{_}{fst}{$_[0]} ) {
776
1
6
$self->carp( qq|($_[0]): no such {fst} record| );
777
1
33
return undef }
778
0
0
elsif( 1 == @_ ) {
779
196506
266548
my $state = shift @_;
780
my $entry = $self->verify(
781
196506
367440
$self->{_}{fst}{$state}, $state, '', q|entry|, q|HASH| );
782
# WORKAROUND:201305070051:whynot: Otherwise there will be spurious Bs about anyway useless turns in those entries.
783
196501
100
100
561735
$state eq q|STOP| || $state eq q|BREAK| and return q|HASH|;
784
196130
100
100
507159
exists $entry->{tturn} || exists $entry->{fturn} and return '';
785
28282
100
48880
unless( exists $entry->{turns} ) {
786
# XXX:201305071531:whynot: Should just B instead, probably.
787
12167
34200
$self->carp( qq|{$state}: none supported turn| );
788
12166
271923
return undef }
789
16115
34330
$self->verify( $entry->{turns}, $state, q|turns|, q|turn|, q|HASH| );
790
16112
27440
return q|HASH| }
791
0
0
elsif( 2 == @_ ) {
792
196102
327950
my( $state, $turn ) = @_;
793
196102
228764
my $entry;
794
196102
385668
$self->verify( $turn, $state, $turn, q|turn|, '' );
795
196101
100
344734
if( exists $special_turns{$turn} ) {
50
796
180046
302436
$entry = $self->{_}{fst}{$state}{$turn} }
797
elsif( !index $turn, q|turn%| ) {
798
16055
38311
$entry = $self->{_}{fst}{$state}{turns}{substr $turn, 5} }
799
else {
800
0
0
croak sprintf q|[turn]: {%s}(%s): unknown turn|, $state, $turn }
801
196101
392229
$self->verify( $entry, $state, $turn, q|turn|, q|ARRAY| );
802
195982
423697
$self->verify( $entry->[0], $state, $turn, q|state|, '' );
803
# XXX:20121230140241:whynot: {START}{turn}{action} is ignored anyway.
804
# XXX:201305072006:whynot: {CONTINUE}{turn}{action} is ignored too.
805
195916
100
100
581034
$entry->[1] //= '' if $state eq q|START| || $state eq q|CONTINUE|;
100
806
195916
415641
$self->verify( $entry->[1], $state, $turn, q|action|, '' );
807
195882
320365
return $entry }
808
else {
809
0
0
$self->carp( sprintf q|too many args (%i)|, scalar @_ );
810
0
0
return undef }
811
}
812
813
=item B
814
815
$bb->action eq $action and die;
816
$action = $bb->action( $new_action );
817
818
Queries and sets I<$action> of B instance.
819
Modes:
820
821
=over
822
823
=item query I<$action>
824
825
No arguments -- returns current I<$action> of the instance.
826
Note, Perl FALSE B parameter.
827
828
=item set I<$action>
829
830
One scalar -- sets action of the instance.
831
Returns previous I<$action>.
832
833
=back
834
835
=cut
836
837
sub action {
838
392836
392836
1
505744
my $self = shift @_;
839
392835
100
647844
unless( @_ ) {
100
840
196979
330051
return $self->{_}{action} }
841
0
0
elsif( 1 == @_ ) {
842
195855
285985
my $backup = $self->action;
843
195855
412453
$self->diag( 5, q|changing action: (%s) (%s)|, $backup, $_[0] );
844
195855
279587
$self->{_}{action} = shift @_;
845
195855
257785
return $backup }
846
else {
847
1
7
$self->carp( sprintf q|too many args (%i)|, scalar @_ );
848
1
41
return undef }}
849
850
=item B
851
852
( $alpha, $bravo ) = $self->query( $what, $name, @items );
853
854
Internal method, then it becomes complicated.
855
Resolves I<$what> (some callback, there multiple of them) against
856
I<$namespace>, if necessary.
857
Then invokes resolved code appropriately passing I<@items> in, if any;
858
Product of the callback over I<@items> is returned back to the caller.
859
I<$name> is used for disgnostics only.
860
Trust me, it all makes perfect sense.
861
862
I<$what> must be either CODE or scalar, or else.
863
864
Strategy is like this
865
866
=over
867
868
=item I<$what> isa CODE
869
870
I<$what> is invoked with I<$self> and I<@items> as arguments.
871
Important, in this case I<$self> is passed as first argument,
872
OO isn't involved like at all.
873
874
=item I<$namespace> is empty string
875
876
Trade I<$namespace> for I<$self> (see below) and continue.
877
878
=item I<$namespace> is scalar
879
880
Treat I<$what> as a name of function in I<$namespace> namespace.
881
882
=item I<$namespace> is object reference
883
884
Treat I<$name> as a name of method of object referred by I<$namespace>.
885
886
=back
887
888
It really works.
889
890
=cut
891
892
sub query {
893
461728
461728
1
906928
my( $self, $topic, $manifest ) = ( shift @_, shift @_, shift @_ );
894
461728
2945241
my $caller = ( split m{::}, ( caller 1 )[3] )[-1];
895
461728
100
1381487
defined $topic or croak sprintf q|[%s]: %s !isa defined|,
896
$caller, $manifest, $self->state;
897
461692
1193758
$self->diag( 5, q|[%s]: %s isa (%s)|, $caller, $manifest, ref $topic );
898
461691
100
1272031
ref $topic eq q|CODE| and return $topic->( $self, @_ );
899
48
100
883
ref $topic eq '' or croak sprintf
900
q|[%s]: %s isa (%s): no way to resolve this|,
901
$caller, $manifest, ref $topic;
902
40
100
468
defined $self->{_}{namespace} or croak
903
qq|[$caller]: {namespace} !isa defined|;
904
36
62
my $anchor = $self->{_}{namespace};
905
36
61
my $backup = $topic;
906
36
100
100
168
if( ref $anchor eq '' && $anchor eq '' ) {
907
12
43
$self->diag( 5, q|[%s]: defaulting %s to $self|, $caller, $manifest );
908
12
26
$anchor = $self }
909
36
110
$self->diag( 5, q|[%s]: {namespace} isa (%s)|, $caller, ref $anchor );
910
36
100
100
unless( ref $anchor eq '' ) {
911
24
67
$self->diag( 5, q|[%s]: going for <%s>->[%s]|,
912
$caller, ref $anchor, $topic );
913
24
123
$topic = $anchor->can( $topic );
914
24
100
832
$topic or croak sprintf q|[%s]: object of <%s> can't [%s] method|,
915
$caller, ref $anchor, $backup;
916
16
57
return $anchor->$topic( @_ ) }
917
else {
918
12
50
$self->diag( 5, q|[%s]: going for <%s>::[%s]|,
919
$caller, $anchor, $topic );
920
12
83
$topic = UNIVERSAL::can( $anchor, $topic );
921
12
100
399
$topic or croak sprintf q|[%s]: <%s> package can't [%s] subroutine|,
922
$caller, $anchor, $backup;
923
8
24
return $topic->( $self, @_ ) }}
924
925
=item B
926
927
( $rule, $item ) = $self->query_switch( $item );
928
929
Internal multitool.
930
That's the point where decisions about turns are made.
931
B<(note)>
932
B converts I<$rule> (as returned by B) to specially
933
encoded scalar;
934
it's caller's responcibility pick correct I<[turn]> later.
935
Strategy:
936
937
=over
938
939
=item no arguments
940
941
Special-state mode:
942
invoke B with no arguments;
943
ignore whatever I<$item> has been possibly returned;
944
return I<$rule> alone.
945
946
=item I<$item> is C
947
948
EOF mode:
949
ignore B completely;
950
return C and C.
951
952
=item I<$item> is not C
953
954
King-size mode:
955
invoke B, pass I<$item> as single argument.
956
return I<$rule> and I<$item>
957
(whatever it became after going through B).
958
959
=back
960
961
I<$rule>, as it was returned by B, is encoded like this:
962
963
=over
964
965
=item I<$rule> is C
966
967
Return C.
968
B<(note)>
969
Don't verify if C I<[turn]> exists.
970
971
=item I<$rule> is Perl TRUE and C and/or C are present
972
973
Return C
974
B<(note)>
975
Don't verify if C I<[turn]> exists.
976
977
=item I<$rule> is Perl FALSE and C and/or C are present
978
979
Return C
980
B<(note)>
981
Don't verify if C I<[turn]> exists.
982
983
=item neither C or C are present
984
985
Encode I<$rule> like this C<'turn%' . $rule> and return that.
986
B((note)>
987
Don't verify if turn map exists.
988
B<(note)>
989
Don't verify if C<"turn%$rule"> exists in turn map.
990
991
=back
992
993
B is always invoked in list context even if I<$item> would be
994
ignored.
995
If I<$rule> shouldn't be paired with I<$item> it won't be
996
(it's safe to call B in scalar context then and
997
there won't be any trailing Cs).
998
999
=cut
1000
1001
sub query_switch {
1002
196536
196536
1
290315
my $self = shift @_;
1003
196536
249075
my @turn;
1004
# WORKAROUND:20121229000801:whynot: No B, B does its checks by itself.
1005
196536
100
100
616865
@turn = $self->query(
1006
$self->fst( $self->state, q|switch| ),
1007
sprintf( q|{%s}{switch}|, $self->state ),
1008
@_ ) if !@_ || defined $_[0];
1009
196481
657405
my $kind = $self->turn( $self->state );
1010
196479
100
100
675311
$turn[0] =
100
100
100
1011
@_ && !defined $_[0] ? q|eturn| :
1012
# TODO:202201071700:whynot: Make C special only when C is present, plz.
1013
!defined $turn[0] ? q|uturn| :
1014
# FIXME:201304230145:whynot: Defaulting to basics here looks as bad as Bing.
1015
# TODO:202212202039:whynot: L.
1016
$kind ? qq|turn%$turn[0]| :
1017
$turn[0] ? q|tturn| : q|fturn|;
1018
196479
100
487634
return @_ ? @turn : $turn[0] }
1019
1020
=item B
1021
1022
( $item, $dump ) = $self->query_source;
1023
1024
Seeks B callback and acquires whatever it returns.
1025
The callback is called in scalar context.
1026
As useful feature, also feeds I<$item> to L.
1027
L method|/query()> has detailed description how B
1028
callback is acquired.
1029
Returns I<$item> and result of L callback|/dumper>.
1030
1031
=cut
1032
1033
sub query_source {
1034
99651
99651
1
150436
my $self = shift @_;
1035
# WORKAROUND:20121229001530:whynot: No B, I<{source}> can return anything.
1036
99651
188974
my $item = $self->query( $self->{_}{source}, q|{source}|, @_ );
1037
99643
502392
return $item, $self->query_dumper( $item ) }
1038
1039
=item B
1040
1041
$dump = $self->query_dumper( $item );
1042
1043
Seeks I callback (L).
1044
If the callback wasn't configured uses simple hopefully informative and
1045
C proof substitution.
1046
Whatever the callback returns is checked to be B
1047
(C is changed to C<"(unclear)">)
1048
and then returned.
1049
1050
=cut
1051
1052
sub query_dumper {
1053
295031
295031
1
413808
my $self = shift @_;
1054
return $self->verify(
1055
$self->query(
1056
# TODO:202202210258:whynot: This is inefficient, defaulting should happen in B instead.
1057
290966
100
290966
1426615
$self->{_}{dumper} // sub { sprintf q|(%s)|, $_[1] // q|undef| },
1058
295031
100
1370043
q|{dumper}|, @_ ) // q|(unclear)|,
100
1059
# XXX:202202210304:whynot: 'source' looks like remnants of refactoring. Should investigate it deeper.
1060
$self->state, qw| source source |, '' ) }
1061
1062
=item B
1063
1064
$bb->diag( 3, 'going to die at %i.', __LINE__ );
1065
1066
Internal.
1067
Provides unified and single-point-of-failure way to output diagnostics.
1068
Intensity is under control of
1069
L configuration parameter|/diag_level>.
1070
Each object has it's own,
1071
however it's inherited when objects are copied.
1072
1073
Defined levels are:
1074
1075
=over
1076
1077
=item C<0>
1078
1079
Nothing at all.
1080
Even error reporting is suppressed.
1081
1082
=item C<1>
1083
1084
Default.
1085
Errors of here-be-dragons type.
1086
1087
=item C<2>
1088
1089
Basic diagnostics for callbacks.
1090
1091
=item C<3>
1092
1093
Basic trace.
1094
Construction, starting and leaving runs.
1095
1096
=item C<4>
1097
1098
Extended diagnostics for callbacks.
1099
1100
=item C<5>
1101
1102
Deep trace.
1103
By the way diagnostics of I entry resolving.
1104
1105
=back
1106
1107
=cut
1108
1109
sub diag {
1110
1542113
1542113
1
2037044
my $self = shift @_;
1111
1542113
100
3052851
$self->{_}{diag_level} >= shift @_ or return $self;
1112
# TODO:202212222141:whynot: Since something this B might emit warnings. And maybe it's appropriate.
1113
444
50
6711
printf STDERR sprintf( qq|[%s]: %s\n|,
1114
( split m{::}, ( caller 1 )[3])[-1], shift @_ ),
1115
map $_ // q|(undef)|, @_;
1116
444
1651
return $self }
1117
1118
=item B
1119
1120
$bb->carp( 'something wrong...' );
1121
1122
Internal.
1123
Bs consistently if I<{_}{diag_level}> is B C<0>.
1124
1125
=back
1126
1127
=cut
1128
1129
sub carp {
1130
13029
13029
1
23128
my $self = shift @_;
1131
13029
100
25643
$self->{_}{diag_level} >= 1 or return;
1132
13022
91238
unshift @_, sprintf q|[%s]: |, ( split m{::}, ( caller 1 )[3])[-1];
1133
13022
1151371
&Carp::carp }
1134
1135
=head1 BUGS AND CAVEATS
1136
1137
=over
1138
1139
=item Default For Turn Map
1140
1141
B<(missing feature)>
1142
It's not hard to imagine application of rather limited turn map that should
1143
default on anything else deemed irrelevant.
1144
Right now to achieve logic like this such defaulting ought to be merged into
1145
B.
1146
That's insane.
1147
1148
=item Diagnostics
1149
1150
B<(misdesign)>
1151
Mechanics behind diagnostics isa failure.
1152
It's messy, fragile, misguided, and (honestly) premature.
1153
At the moment it's useless.
1154
1155
=item Hash vs HASH vs Acme::FSM Ref Constructors
1156
1157
B<(messy, probably bug)>
1158
L method description|/connect()> _declares_ that list is copied.
1159
Of course, it's not true deep copy
1160
({fst} might contain CODE, it's not clear how to copy it).
1161
It's possible, one day list trailer variant of initialization may be put to
1162
sleep.
1163
See also L.
1164
1165
=item Linter
1166
1167
B<(missing feature)>
1168
It might be hard to imagine,
1169
but FST might get out of hands
1170
(ie check F).
1171
Indeed, some kind of (limited!) linter would be much desired.
1172
It's missing.
1173
1174
=item Perl FALSE, C, and C
1175
1176
B<(caveat)>
1177
Back then B treated C as any other Perl FALSE.
1178
C I<$rule> mech has made C special
1179
(if B returns C and C I<{turn}> isn't present then
1180
it's a B).
1181
Thus, at the moment, C isn't FALSE (for B).
1182
This is counter-intuitive, actually.
1183
1184
=item Returning C for misuse
1185
1186
B<(bug)>
1187
Why B screws with caller,
1188
in case of API violations (by returning C),
1189
is beyond my understanding now.
1190
1191
=item B and I<$state>
1192
1193
B<(bug)> (also see L and I<$item>|/switch() and $item>)
1194
By design and legacy,
1195
there is single point of input -- B.
1196
IRL, multiple sources are normal.
1197
Implementation wise that leads to B that on the fly figures out
1198
current I<$state> and then somehow branches (sometimes branches out).
1199
Such Bs look horrible because they are.
1200
1201
=item Special handling of C and C
1202
1203
B<(bug)>
1204
In contrary with C and C special states,
1205
special treatement of C and C is a side effect of
1206
L method|/process()> entry sequence.
1207
That's why routinely enter C or C by state flow is of
1208
there-be-dragons type.
1209
Totally should be rewritten.
1210
1211
=item B and I<$item>
1212
1213
B<(bug)>
1214
It might be surprising, but, from experience,
1215
the state flow mostly doesn't care about I<$item>.
1216
Mostly, I<{state}> entered with I<$action> C processes input
1217
and just wonders ahead.
1218
Still those pesky I<$item>s *must* be passed around (by design).
1219
Still every B *must* return I<$item> too
1220
(even if it's just a placeholder).
1221
Something must be done about it.
1222
1223
=item C, C, and B
1224
1225
B<(misdesign)>
1226
First, by design and legacy B must report what turn the control flow
1227
must make
1228
(that's why it *must* return I<$rule>).
1229
OTOH, there's some value in keeping Bes as simple as possible what
1230
results in I<{state}> with alone C I<{turn}> and B that
1231
always returns TRUE.
1232
Changes are coming.
1233
1234
=item B and B
1235
1236
B<(misdesign)>
1237
Encoding (so to speak) in use by B (in prediction mode) is plain
1238
stupid.
1239
C signals two distinct conditions
1240
(granted, both are manifest of broken I<{fst}>).
1241
Empty string doesn't distinguish safe (both C and C are present)
1242
and risky (C or C is missing) I<{state}>.
1243
C doesn't say if there's anything in turn map.
1244
All that needs loads of workout.
1245
1246
=back
1247
1248
=cut
1249
1250
=head1 DIAGNOSTICS
1251
1252
=over
1253
1254
=item C<[action]: changing action: (%s) (%s)>
1255
1256
B<(deep trace)>, L method|/action()>.
1257
Exposes change of I<$action> from previous (former I<%s>)
1258
to current (latter I<%s>).
1259
1260
=item C<[action]: too many args (%i)>
1261
1262
B<(warning)>, L method|/action()>.
1263
Obvious.
1264
None or one argument is supposed.
1265
1266
=item C<[connect]: (%s): unknow option, ignored>
1267
1268
B<(warning)>, L method|/connect()>.
1269
Each option that's not known to B is ignored and Bed.
1270
There're no means for child class to force B (as a parent class) to
1271
accept something.
1272
Child classes should process their options by itself.
1273
1274
=item C<[connect]: clean init with (%i) items in FST>
1275
1276
B<(basic trace)>, L method|/connect()>.
1277
During class construction FST has been found.
1278
That FST consists of I<%i> items.
1279
Those items are number of I<$state>s and not I<$state>s plus I<{state}>s.
1280
1281
=item C<[connect]: FST has no {START} state>
1282
1283
B<(warning)>, L method|/connect()>.
1284
I I<{state}> is required.
1285
It's missing.
1286
This situation will definetely result in devastating failure later.
1287
1288
=item C<[connect]: FST has no {STOP} state>
1289
1290
B<(warning)>, L method|/connect()>.
1291
I I<{state}> is required.
1292
It's missing.
1293
If FST is such that I I<$state> can't be reached
1294
(for whatever reasons)
1295
this may be cosmetic.
1296
1297
=item C<[connect]: ignoring (%i) FST items in trailer>
1298
1299
B<(warning)>, L method|/connect()>.
1300
A trailer has been found and was ignored.
1301
Beware, I<%i> could be misleading.
1302
For HASH in trailer that HASH is considered and key number is reported.
1303
For list in trailer an B- number in list is reported, even if it's twice
1304
as many in FST (what isa hash).
1305
Just because.
1306
1307
=item C<[connect]: (source): unset>
1308
1309
B<(warning)>, L method|/connect()>.
1310
I<$source> option isa unset, it should be.
1311
There's no other way to feed items in.
1312
This situation will definetely result in devastating failure later.
1313
1314
=item C<[connect]: stealing (%i) items in FST>
1315
1316
B<(basic trace)>, L method|/connect()>.
1317
During object construction FST has been found.
1318
That FST consists of I<%i> items.
1319
Those items are I<{state}>s, not I<$state>s plus I<{state}>s.
1320
1321
=item C<[fst]: creating {%s} record>
1322
1323
B<(basic trace)>, L method|/fst()>.
1324
New state record has been created, named I<%s>.
1325
1326
=item C<[fst]: creating {%s}{%s} entry>
1327
1328
B<(basic trace)>, L method|/fst()>.
1329
New entry named I<%s> (the latter) has been created in record named I<%s> (the
1330
former).
1331
1332
=item C<[fst]: no args>
1333
1334
B<(warning)>, L method|/fst()>.
1335
No arguments, it's an error.
1336
However, instead of promptly dieing, C is returned
1337
in blind hope this will be devastating enough.
1338
1339
=item C<[fst]: (%s): no such {fst} record>
1340
1341
B<(warning)>, L method|/fst()>.
1342
Requested entry I<%s> is missing.
1343
State record ought to exist beforehand for any usecase except
1344
L.
1345
1346
=item C<[fst]: too many args (%i)>
1347
1348
B<(warning)>, L method|/fst()>.
1349
Too many args have been passed in.
1350
And again, instead of prompt dieing C is returned.
1351
1352
=item C<[fst]: updating {%s} record>
1353
1354
B<(basic trace)>, L method|/fst()>.
1355
Named record (I<%s>) has been updated.
1356
1357
=item C<[fst]: updating {%s}{%s} entry>
1358
1359
B<(basic trace)>, L method|/fst()>.
1360
Named entry (I<%s>, the latter) in record I<%s> (the former) has been updated.
1361
1362
=item C<[process]: {%s}(%s): changing state: (CONTINUE)>
1363
1364
B<(basic trace)>, L method|/process()>.
1365
Implicit change of state from C to C is about to happen.
1366
1367
=item C<[process]: {%s}(%s): entering>
1368
1369
B<(basic trace)>, L method|/process()>.
1370
Entering state flow from I<$state> I<%s> (the former) with I<$action> I<%s>
1371
(the latter).
1372
1373
=item C<[process]: {%s}(%s): going for>
1374
1375
B<(deep trace)>, L method|/process()>.
1376
Trying to enter I<$state> I<%s> (the former) with I<$action> I<%s> (the
1377
latter).
1378
1379
=item C<[process]: {%s}(%s): %s: going with>
1380
1381
B<(basic trace)>, L method|/process()>.
1382
While in I<$state> I<%s> (1st) doing I<$action> I<%s> (2nd) the I<$item>
1383
happens to be I<%s> (3rd).
1384
C will look like this: C<((undef))>.
1385
1386
=item C<[process]: {%s}(%s): %s: turning with>
1387
1388
B<(basic trace)>, L method|/process()>.
1389
B (of I<$state> I<%s>, the 1st) has returned I<$rule> I<%s> (2nd)
1390
and I<$item> I<%s>.
1391
Now dealing with this.
1392
1393
=item C<[process]: {%s}(%s): leaving>
1394
1395
B<(basic trace)>, L method|/process()>.
1396
Leaving state flow with I<$state> I<%s> (the former) and I<$action> I<%s> (the
1397
latter).
1398
1399
=item C<[process]: {%s}(%s): switch returned: (%s)>
1400
1401
B<(basic trace)>, L method|/process()>.
1402
B (of I<$state> I<%s>, the 1st, with I<$action> I<%s>, the 2nd) has
1403
been invoked and returned something that was interpreted as I<$rule> I<%s>,
1404
the 3rd.
1405
1406
=item C<[process]: {%s}(%s): unknown action>
1407
1408
B<(croak)>, L method|/process()>.
1409
Attempt to enter I<$state> I<%s> (the former) has been made.
1410
However, it has failed because I<$action> I<%s> (the latter) isn't known.
1411
1412
=item C<< [query]: [$caller]: <%s> package can't [%s] subroutine >>
1413
1414
B<(croak)>, L method|/query()>.
1415
I<$namespace> and I<$what> has been resolved as package I<%s> (the former)
1416
and subroutine I<%s> (the latter).
1417
However, the package can't do such subroutine.
1418
1419
=item C<[query]: [query_dumper]: %s !isa defined>
1420
1421
=item C<[query]: [query_source]: %s !isa defined>
1422
1423
=item C<[query]: [query_switch]: %s !isa defined>
1424
1425
B<(croak)>, L method|/query()>.
1426
I<$what> (I<%s> is I<$name>) should have some meaningful value.
1427
It doesn't.
1428
1429
=item C<[query]: [query_dumper]: %s isa (%s): no way to resolve this>
1430
1431
=item C<[query]: [query_source]: %s isa (%s): no way to resolve this>
1432
1433
=item C<[query]: [query_switch]: %s isa (%s): no way to resolve this>
1434
1435
B<(croak)>, L method|/query()>.
1436
C[ (the former I<%s>) is I<%s> (the latter). ]
1437
I<$what> is neither CODE nor scalar.
1438
1439
=item C<[query]: [query_dumper]: %s isa (%s)>
1440
1441
=item C<[query]: [query_source]: %s isa (%s)>
1442
1443
=item C<[query]: [query_switch]: %s isa (%s)>
1444
1445
B<(deep trace)>, L method|/query()>.
1446
C[ (the former I<%s>) is I<%s> (the latter). ]
1447
1448
=item C<[query]: [query_dumper]: {namespace} !isa defined>
1449
1450
=item C<[query]: [query_source]: {namespace} !isa defined>
1451
1452
=item C<[query]: [query_switch]: {namespace} !isa defined>
1453
1454
B<(croak)>, L method|/query()>.
1455
I<$what> isn't CODE, now to resolve it I<$namespace> is required.
1456
Apparently, it was missing all along.
1457
1458
=item C<[query]: [query_dumper]: {namespace} isa (%s)>
1459
1460
=item C<[query]: [query_source]: {namespace} isa (%s)>
1461
1462
=item C<[query]: [query_switch]: {namespace} isa (%s)>
1463
1464
B<(deep trace)>, L method|/query()>.
1465
C[ is I<%s>. ]
1466
1467
=item C<[query]: [query_dumper]: defaulting %s to $self>
1468
1469
=item C<[query]: [query_source]: defaulting %s to $self>
1470
1471
=item C<[query]: [query_switch]: defaulting %s to $self>
1472
1473
B<(deep trace)>, L method|/query()>.
1474
I<$namespace> is an empty string.
1475
The blackboard object will be used to resolve the callback.
1476
1477
=item C<< [query]: [query_dumper]: going for <%s>->[%s] >>
1478
1479
=item C<< [query]: [query_source]: going for <%s>->[%s] >>
1480
1481
=item C<< [query]: [query_switch]: going for <%s>->[%s] >>
1482
1483
B<(deep trace)>, L method|/query()>.
1484
Attempting to call I<%s> (the latter) method on object of I<%s> (the former)
1485
class.
1486
1487
=item C<< [query]: [query_dumper]: going for <%s>::[%s] >>
1488
1489
=item C<< [query]: [query_source]: going for <%s>::[%s] >>
1490
1491
=item C<< [query]: [query_switch]: going for <%s>::[%s] >>
1492
1493
B<(deep trace)>, L method|/query()>.
1494
Attempting to call I<%s> (the latter) subrouting of package I<%s> (the
1495
former).
1496
1497
=item C<< [query]: [query_dumper]: object of <%s> can't [%s] method >>
1498
1499
=item C<< [query]: [query_source]: object of <%s> can't [%s] method >>
1500
1501
=item C<< [query]: [query_switch]: object of <%s> can't [%s] method >>
1502
1503
B<(croak)>, L method|/query()>.
1504
The object of I<%s> (the former) class can't do I<%s> (the latter) method.
1505
1506
=item C<[state]: changing state: (%s) (%s)>
1507
1508
B<(deep trace)>, L method|/state()>.
1509
Exposes change of state from previous (former I<%s>)
1510
to current (latter I<%s>).
1511
1512
=item C<[state]: too many args (%i)>
1513
1514
B<(warning)>, L method|/state()>.
1515
Obvious.
1516
None or one argument is supposed.
1517
B has returned C in this case,
1518
most probably will bring havoc in a moment.
1519
1520
=item C<[turn]: (%s): no such {fst} record>
1521
1522
B<(warning)>, L method|/turn()>.
1523
Peeking for I<[turn]>s of I<%s> I<$state> yeilds nothing, there's no such
1524
state.
1525
1526
=item C<[turn]: {%s}: none supported turn>
1527
1528
B<(warning)>, L method|/turn()>.
1529
Whatever content of I<%s> entry is FSM doesn't know how to handle it.
1530
1531
=item C<[turn]: {%s}(%s): unknown turn>
1532
1533
B<(croak)>, L method|/turn()>.
1534
There was request for I<[turn]> I<%s> (the latter) of I<$state> I<%s> (the
1535
former).
1536
While I<{state}> record has been found and is OK,
1537
there is no such I<$rule>.
1538
1539
=item C<[turn]: no args>
1540
1541
B<(warning)>, L method|/turn()>.
1542
No argumets, it's an error.
1543
1544
=item C<[turn]: too many args (%i)>
1545
1546
B<(warning)>, L method|/turn()>.
1547
There's no way to handle that many (namely: I<%i>) arguments.
1548
1549
=item C<[verify]: {%s}{%s}: %s !isa defined>
1550
1551
B<(croak)>, L method|/verify()>.
1552
I<$rc> queried
1553
from something in I<{fst}> related to I<%s> (3rd)
1554
(value of which is I<%s> (2nd))
1555
while in I<$state> I<%s> (1st)
1556
isn't defined.
1557
1558
=item C<[verify]: {%s}{%s}: %s isa (%s), should be (%s)>
1559
1560
B<(croak)>, L method|/verify()>.
1561
B[ of I<$rc> queried ]
1562
from something in I<{fst}> related to I<%s> (3rd)
1563
(value of which is I<%s> (2nd))
1564
while in I<$state> I<%s> (1st) is I<%s> (4th).
1565
While it should be I<%s> (5th)
1566
(the last one is literally I<$test>).
1567
1568
=back
1569
1570
=cut
1571
1572
=head1 EXAMPLES
1573
1574
Here are example records.
1575
Whole I<{fst}>, honestly, might become enormous,
1576
thus are skipped for brewity.
1577
1578
alpha =>
1579
{
1580
switch => sub {
1581
shift % 42, ''
1582
},
1583
tturn => [ qw/ alpha NEXT / ],
1584
fturn => [ qw/ STOP horay! / ]
1585
}
1586
1587
B supposedly produces some numbers.
1588
Then,
1589
if I<$item> doesn't devide C then go for another number.
1590
If I<$item> devides then break out.
1591
Also, please note, C (and C) is special --
1592
it needs B I<$action> but it can be literally anything.
1593
1594
bravo =>
1595
{
1596
switch => sub {
1597
my $item = shift;
1598
$item % 15 ? 'charlie' :
1599
$item % 5 ? 'delta' :
1600
$item % 3 ? 'echo' :
1601
undef, $item
1602
},
1603
uturn => [ qw/ bravo NEXT / ],
1604
turns =>
1605
{
1606
charlie => [ qw/ charlie SAME / ],
1607
delta => [ qw/ delta SAME / ],
1608
echo => [ qw/ echo SAME / ]
1609
}
1610
}
1611
1612
Again, B supposedly produces some numbers.
1613
Then some kind of FizBuzz happens.
1614
Also, returning C as default raises questions.
1615
However, it's acceptable for example.
1616
1617
Now, quick demonstration, that's how this FizzBuzz would look
1618
using B capabilities (and B of I syntax).
1619
1620
bravo_foo =>
1621
{
1622
switch => sub {
1623
my $item = shift;
1624
$item % 15 ? !0 : !1, $item
1625
},
1626
tturn => [ qw/ charlie SAME / ],
1627
fturn => [ qw/ bravo_bar SAME / ]
1628
},
1629
bravo_bar =>
1630
{
1631
switch => sub {
1632
my $item = shift;
1633
$item % 5 ? !0 : !1, $item
1634
},
1635
tturn => [ qw/ delta SAME / ],
1636
fturn => [ qw/ bravo_baz SAME / ]
1637
},
1638
bravo_baz =>
1639
{
1640
switch => sub {
1641
my $item = shift;
1642
$item % 3 ? !0 : !1, $item
1643
},
1644
tturn => [ qw/ echo SAME / ],
1645
fturn => [ qw/ bravo NEXT / ]
1646
}
1647
1648
World of a difference.
1649
Furthermore, if you dare, take a look at
1650
F and F.
1651
But better don't, those are horrible.
1652
1653
Now, illustration what I<$namespace> configuration parameter opens.
1654
1655
Classics:
1656
1657
my $bb = Acme::FSM->connect(
1658
{ },
1659
alpha => {
1660
switch => sub {
1661
shift % 42, ''
1662
}
1663
}
1664
);
1665
1666
Illustrations below are heavily stripped for brevity
1667
(Perlwise and Bwise).
1668
1669
Separate namespace:
1670
1671
package Secret::Namespace;
1672
1673
sub bravo_sn {
1674
shift;
1675
shift % 42, ''
1676
}
1677
1678
package Regular::Namespace;
1679
use Acme::FSM;
1680
1681
my $bb = Acme::FSM->connect(
1682
{ namespace => 'Secret::Namespace' },
1683
alpha => {
1684
switch => 'bravo_sn'
1685
}
1686
);
1687
1688
Separate class:
1689
1690
package Secret::Class;
1691
1692
sub new { bless { }, shift }
1693
sub bravo_sc {
1694
shift;
1695
shift % 42, ''
1696
}
1697
1698
package Regular::Class;
1699
use Secret::Class;
1700
use Acme::FSM;
1701
1702
my $not_bb = Secret::Class->new;
1703
my $bb = Acme::FSM->connect(
1704
{ namespace => $not_bb },
1705
alpha => {
1706
switch => 'bravo_sc'
1707
}
1708
);
1709
1710
And, finally, B implodes upon itself:
1711
1712
package Secret::FSM;
1713
use parent qw/ Acme::FSM /;
1714
1715
sub connect {
1716
my $class = shift;
1717
my $bb = $class->SUPER::connect( @_ );
1718
} # or just skip constructor, if not needed
1719
sub bravo_sf {
1720
shift;
1721
shift % 42, ''
1722
}
1723
1724
package main;
1725
1726
my $bb = Secret::FSM->connect(
1727
{ namespace => '' },
1728
alpha => {
1729
switch => 'bravo_sf'
1730
}
1731
);
1732
1733
=cut
1734
1735
=head1 COPYRIGHT AND LICENSE
1736
1737
Original Copyright: 2008 Dale M Amon. All rights reserved.
1738
1739
Original License:
1740
1741
LGPL-2.1
1742
Artistic
1743
BSD
1744
1745
Original Code Acquired from:
1746
1747
http://www.cpan.org/authors/id/D/DA/DALEAMON/DMAMisc-1.01-3.tar.gz
1748
1749
Copyright 2012, 2013, 2022 Eric Pozharski
1750
1751
GNU LGPLv3
1752
1753
AS-IS, NO-WARRANTY, HOPE-TO-BE-USEFUL
1754
1755
=cut
1756
1757
1