File Coverage

blib/lib/Acme/FSM.pm
Criterion Covered Total %
statement 181 194 93.3
branch 113 116 97.4
condition 36 37 97.3
subroutine 18 18 100.0
pod 11 13 84.6
total 359 378 94.9


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