Acme-FSM

 view release on metacpan or  search on metacpan

lib/FSM.pm  view on Meta::CPAN

=cut

my @options = qw| diag_level namespace source dumper |;
sub connect     {
    my( $self, $opts ) = ( shift @_, shift @_ );
    ref $opts eq q|HASH|                                      or croak sprintf
      q|[connect]: {options} HASH is required, got (%s) instead|, ref $opts;
    my( $trailer, $leader );
    if( ref $self eq '' )           {
        $trailer = ref $_[0] eq q|HASH|;
        $self = bless { _ => { fst => $trailer ? shift @_ : { @_ }}}, $self;
        $leader = q|clean init with| }
    else                            {
        $trailer = @_;
        $self = bless { _ => { %{$self->{_}} }}, ref $self;
        $leader = q|stealing|        }
    $self->{_}{$_} = delete $opts->{$_} // $self->{_}{$_}    foreach @options;
    $self->{_}{diag_level} //= 1;
    $self->diag( 3, q|%s (%i) items in FST|,
      $leader, scalar keys %{$self->{_}{fst}} );

    $self->carp( qq|($_): unknown option, ignored| )      foreach keys %$opts;
    $self->carp( q|(source): unset| )       unless defined $self->{_}{source};
    $trailer = ref $_[0] eq q|HASH| ? keys %{$_[0]} : @_          if $trailer;
    $self->carp( qq|ignoring ($trailer) FST items in trailer| )   if $trailer;
    $self->carp( q|FST has no {START} state| )                          unless
      exists $self->{_}{fst}{START};
    $self->carp( q|FST has no {STOP} state| )                           unless
      exists $self->{_}{fst}{STOP};
    @{$self->{_}}{qw| state action |} = qw| START VOID |;
    return $self }

=head1 B<process()>

    $bb = Acme::FSM->connect( { }, \%fst );
    $rc = $bb->process;

This is heart, brains, liver, and so on of B<Acme::FSM>.
B<process()> is what does actual state flow.
That state flow is steered by records in I<%fst>.
Each record consists of:

=over

=item B<switch()> callback

This is some user supplied code that
always consumes whatever B<source()> callback returns (at some point in past),
(optionally) regurgitates said input,
and decides what I<[turn]> to go next.
Except when in special I<{state}> (see below) it's supplied with one argument:
I<$item>.
In special states I<$item> is missing.
B<switch()> returns two controls:  I<$rule> and processed I<$item>.
What to do with returned I<$item> is determined by I<$action> (see below).

=item various I<[turn]>s

Each I<[turn]> spec is an ARRAY with two elements (trailing elements are
ignored).
First element is I<$state> to change to.
Second is I<$action> that sets what to do with I<$item> upon changing to named
I<$state>.
Here are known I<[turn]>s in order of logical treating decreasing.

=over

=item C<eturn>

That I<[turn]> will be choosen by FSM itself when I<$item> at hands is
C<undef>
(as returned by B<source()>).
I<switch()> isn't invoked -- I<$item> is void, there's nothing to call
I<switch()> with.
However, I<$action> may change I<$item>.

=item C<uturn>

That I<[turn]> will be choosen by FSM if I<$rule> is C<undef>
(as returned by B<switch()>).
Idea behind this is to give an FST an option to bailout.
In original B<DMA::FSM> that's not possible (except B<croak>ing, you know).
Also, see L<B<BUGS AND CAVEATS>|/Perl FALSE, undef, and uturn>.

=item C<tturn> and/or C<fturn>

If any (hence 'or') is present then I<$rule> returned by B<switch()> is
treated as Perl boolean, except C<undef> it's handled by C<uturn>.
That's how it is in original B<DMA::FSM>.
If B<switch()> always returns TRUE (or FALSE) then C<fturn> (or C<tturn>) can
be missing.
Also, see L<B<BUGS AND CAVEATS>|/tturn, fturn, and switch()>.

=item C<turns>

If neither C<tturn> nor C<fturn> is present then whatever I<$rule> would be
returned by B<switch()> is treated as string.
That string is supposed to be a key in I<%$turns>.
I<$rule> returned is forced to be string (so if you dare to return objects,
such objects should have C<""> overloaded).
Also, see L<B<BUGS AND CAEATS>|/Default For Turn Map>.

=back

I<$state> is treated as a key in FST hash, except when that's a special state.
Special states (in alphabetical order):

=over

=item C<BREAK>

Basically, it's just like C<STOP> I<$state>.
All comments there (see below) apply.
It's added to enable break out from loop, do something, then get back in loop.
I<$state> is changed to C<CONTINUE> just before returning to caller
(I<switch()> is called in C<BREAK> state).
The choice where to implicitly change state to C<CONTINUE> has been completely
arbitrary;
probably wrong.

=item C<CONTINUE>

Just like C<START> state (see below, all comments apply).
While C<BREAK> is turned to C<CONTINUE> implicitly no other handling is made.

=item C<START>

It's I<$state> set by B<connect()>.
I<$action> (it's also set by B<connect()>) is ignored
(if I<$action> is C<undef> then silently replaces with empty string),
B<switch()> is invoked with no arguments
(there's not anything to process yet).
Whatever I<$item> could be returned is ignored.
I<$rule> is followed.
Thus C<eturn> can't be followed.
See also L<B<BUGS AND CAVEATS>|/Special handling of START and CONTINUE>.

=item C<STOP>

It's last state in the state flow.
I<$action> is retained
(that's what B<process()> will return)
(however, because it's processed before C<STOP> state is acknowledged it must
not be C<undef>)
but otherwise ignored.
B<switch()> is invoked with no arguments
(B<switch()> of previous I<{state}> should have took care).
Whatever I<$item> could be returned is ignored.
Whatever I<$rule> could be returned is reported (at I<(basic trace)> level)
but otherwise ignored.
Then I<$action> is returned and state flow terminates.

=back

Supported I<$action>s (in alphabetical order):

=over

=item C<NEXT>

Drop whatever I<$item> at hands.
Request another.

If FST has such record:

    somestate => { eturn => [ somestate => 'NEXT' ] }

then FSM will stay in C<somestate> as long as I<source()> callback returns
C<undef>.
Thus consuming all resources available.
No options provided to limit that consumption.

=item C<SAME>

Retains I<$item> uncoditionally.
That is, even if I<$item> isn't B<defined> it's kept anyway.

lib/FSM.pm  view on Meta::CPAN

(also B<carp>s).
Also see below.

=item *

Empty string is returned if there're I<tturn> and/or I<fturn> turns.
I<turns> hash is ignored in that case.

=item *

C<HASH> is returned if there's turn map
(and neither I<tturn> nor I<fturn> is present).
B<(note)> In that case, B<turn()> checks for I<turns> is indeed a HASH,
nothing more
(however B<croaks> if that's not the case);
It may as well be empty;
Design legacy.

=item *

Returns C<HASH> for C<STOP> and C<BREAK> I<$state>s without any further
processing
(For those I<$state>s any I<$rule> is ignored and C<HASH> enables I<switch()>
callbacks to give more informative logs
(while that information is mangled anyway);
Probably bad idea).

=item *

C<undef> is returned if there's nothing to say --
neither I<tturn>, nor I<fturn>, nor turn map --
this record is kind of void.
The record should be studied to find out why.
B<carp>s in that case.

=back

=item query specific I<[turn]>

Two scalars are I<$state> and specially encoded I<$rule>
(refer to L<B<query_switch()> method|/query_switch()> about encoding).
If I<$rule> can't be decoded then B<croak>s.
Returns (after verification) requested I<$rule> as ARRAY.
While straightforward I<[turn]>s (such as C<tturn>, C<fturn>, and such) could
be in fact queried through L<B<fst()> method|/fst()> turn map needs bit more
sophisticated handling;
and that's what B<turn()> does;
in fact asking for C<turns> will result in B<croak>.
I<$action> of C<START> and C<CONTINUE> special states suffer implicit
defaulting to empty string.

=item anything else

No arguments or more then two is an non-fatal error.
Returns C<undef> (with B<carp>).

=back

=cut

# TODO:202202172011:whynot: As soon as supported perl is young enough change it to smartmatch, plz.
my %special_turns = map { $_ => 1 } qw| eturn uturn tturn fturn |;
# TODO:202202162030:whynot: Consider more elaborate (informative) returns.
sub turn {
    my $self = shift @_;
    unless( @_                                       ) {
        $self->carp( q|no args| );         return undef }
    elsif( 1 == @_ && !exists $self->{_}{fst}{$_[0]} ) {
        $self->carp( qq|($_[0]): no such {fst} record| );
                                           return undef }
    elsif( 1 == @_                                   ) {
        my $state = shift @_;
        my $entry = $self->verify(
          $self->{_}{fst}{$state}, $state, '', q|entry|, q|HASH| );
# WORKAROUND:201305070051:whynot: Otherwise there will be spurious B<carp>s about anyway useless turns in those entries.
        $state eq q|STOP| || $state eq q|BREAK|            and return q|HASH|;
        exists $entry->{tturn} || exists $entry->{fturn}        and return '';
        unless( exists $entry->{turns} ) {
# XXX:201305071531:whynot: Should just B<croak> instead, probably.
            $self->carp( qq|{$state}: none supported turn| );
                             return undef }
        $self->verify( $entry->{turns}, $state, q|turns|, q|turn|, q|HASH| ); 
                                         return q|HASH| }
    elsif( 2 == @_                                   ) {
        my( $state, $turn ) = @_;
        my $entry;
        $self->verify( $turn, $state, $turn, q|turn|, '' );
        if( exists $special_turns{$turn} )                                {
                                   $entry = $self->{_}{fst}{$state}{$turn} }
        elsif( !index $turn, q|turn%|    )                                {
                  $entry = $self->{_}{fst}{$state}{turns}{substr $turn, 5} }
        else                                                              {
            croak sprintf q|[turn]: {%s}(%s): unknown turn|, $state, $turn }
        $self->verify( $entry, $state, $turn, q|turn|, q|ARRAY| );
        $self->verify( $entry->[0], $state, $turn, q|state|, '' );
# XXX:20121230140241:whynot: {START}{turn}{action} is ignored anyway.
# XXX:201305072006:whynot: {CONTINUE}{turn}{action} is ignored too.
        $entry->[1] //= ''     if $state eq q|START| || $state eq q|CONTINUE|;
        $self->verify( $entry->[1], $state, $turn, q|action|, '' );
                                          return $entry }
    else                                               {
        $self->carp( sprintf q|too many args (%i)|, scalar @_ );
                                           return undef }
}

=item B<action()>

    $bb->action eq $action and die;
    $action = $bb->action( $new_action );

Queries and sets I<$action> of B<A::F> instance.
Modes:

=over

=item query I<$action>

No arguments -- returns current I<$action> of the instance.
Note, Perl FALSE B<isa> parameter.

=item set I<$action>

lib/FSM.pm  view on Meta::CPAN

Don't verify if turn map exists.
B<(note)>
Don't verify if C<"turn%$rule"> exists in turn map.

=back

B<switch()> is always invoked in list context even if I<$item> would be
ignored.
If I<$rule> shouldn't be paired with I<$item> it won't be
(it's safe to call B<query_switch()> in scalar context then and
there won't be any trailing C<undef>s).

=cut

sub query_switch                {
    my $self = shift @_;
    my @turn;
# WORKAROUND:20121229000801:whynot: No B<verify()>, B<query()> does its checks by itself.
    @turn = $self->query(
      $self->fst( $self->state, q|switch| ),
      sprintf( q|{%s}{switch}|, $self->state ),
      @_ )                                            if !@_ || defined $_[0];
    my $kind = $self->turn( $self->state );
    $turn[0] =
      @_ && !defined $_[0] ? q|eturn|          :
# TODO:202201071700:whynot: Make C<undef> special only when C<uturn> is present, plz.
      !defined $turn[0]    ? q|uturn|          :
# FIXME:201304230145:whynot: Defaulting to basics here looks as bad as B<croak>ing.
# TODO:202212202039:whynot: L<Default For Turn Map>.
      $kind                ? qq|turn%$turn[0]| :
      $turn[0]             ? q|tturn|          : q|fturn|;
    return @_ ? @turn : $turn[0] }

=item B<query_source()>

    ( $item, $dump ) = $self->query_source;

Seeks B<source()> callback and acquires whatever it returns.
The callback is called in scalar context.
As useful feature, also feeds I<$item> to L<dumper callback|/query_dumper()>.
L<B<query()> method|/query()> has detailed description how B<source()>
callback is acquired.
Returns I<$item> and result of L<I<dumper> callback|/dumper>.

=cut

sub query_source                              {
    my $self = shift @_;
# WORKAROUND:20121229001530:whynot: No B<verify()>, I<{source}> can return anything.
    my $item = $self->query( $self->{_}{source}, q|{source}|, @_ );
    return $item, $self->query_dumper( $item ) }

=item B<query_dumper()>

    $dump = $self->query_dumper( $item );

Seeks I<dumper> callback (L<configured at construction time|/dumper>).
If the callback wasn't configured uses simple hopefully informative and
C<undef> proof substitution.
Whatever the callback returns is checked to be B<defined>
(C<undef> is changed to C<"(unclear)">)
and then returned.

=cut

sub query_dumper                             {
    my $self = shift @_;
    return $self->verify(
      $self->query(
# TODO:202202210258:whynot: This is inefficient, defaulting should happen in B<connect()> instead.
        $self->{_}{dumper} // sub { sprintf q|(%s)|, $_[1] // q|undef| },
        q|{dumper}|,     @_ ) // q|(unclear)|,
# XXX:202202210304:whynot: 'source' looks like remnants of refactoring.  Should investigate it deeper.
      $self->state, qw| source source |, '' ) }

=item B<diag()>

    $bb->diag( 3, 'going to die at %i.', __LINE__ );

Internal.
Provides unified and single-point-of-failure way to output diagnostics.
Intensity is under control of
L<I<diag_level> configuration parameter|/diag_level>.
Each object has it's own,
however it's inherited when objects are copied.

Defined levels are:

=over

=item C<0>

Nothing at all.
Even error reporting is suppressed.

=item C<1>

Default.
Errors of here-be-dragons type.

=item C<2>

Basic diagnostics for callbacks.

=item C<3>

Basic trace.
Construction, starting and leaving runs.

=item C<4>

Extended diagnostics for callbacks.

=item C<5>

Deep trace.
By the way diagnostics of I<switch> entry resolving.

=back

=cut

lib/FSM.pm  view on Meta::CPAN

Implementation wise that leads to B<source()> that on the fly figures out
current I<$state> and then somehow branches (sometimes branches out).
Such B<source()>s look horrible because they are.

=item Special handling of C<START> and C<CONTINUE>

B<(bug)>
In contrary with C<STOP> and C<BREAK> special states,
special treatement of C<START> and C<CONTINUE> is a side effect of
L<B<process()> method|/process()> entry sequence.
That's why routinely enter C<START> or C<CONTINUE> by state flow is of
there-be-dragons type.
Totally should be rewritten.

=item B<switch()> and I<$item>

B<(bug)>
It might be surprising, but, from experience,
the state flow mostly doesn't care about I<$item>.
Mostly, I<{state}> entered with I<$action> C<NEXT> processes input
and just wonders ahead.
Still those pesky I<$item>s *must* be passed around (by design).
Still every B<switch()> *must* return I<$item> too
(even if it's just a placeholder).
Something must be done about it.

=item C<tturn>, C<fturn>, and B<switch()>

B<(misdesign)>
First, by design and legacy B<switch()> must report what turn the control flow
must make
(that's why it *must* return I<$rule>).
OTOH, there's some value in keeping B<switch()>es as simple as possible what
results in I<{state}> with alone C<tturn> I<{turn}> and B<switch()> that
always returns TRUE.
Changes are coming.

=item B<turn()> and B<fst()>

B<(misdesign)>
Encoding (so to speak) in use by B<turn()> (in prediction mode) is plain
stupid.
C<undef> signals two distinct conditions
(granted, both are manifest of broken I<{fst}>).
Empty string doesn't distinguish safe (both C<tturn> and C<fturn> are present)
and risky (C<tturn> or C<fturn> is missing) I<{state}>.
C<HASH> doesn't say if there's anything in turn map.
All that needs loads of workout.

=back

=cut

=head1 DIAGNOSTICS

=over

=item C<[action]: changing action: (%s) (%s)>

B<(deep trace)>, L<B<action()> method|/action()>.
Exposes change of I<$action> from previous (former I<%s>)
to current (latter I<%s>).

=item C<[action]: too many args (%i)>

B<(warning)>, L<B<action()> method|/action()>.
Obvious.
None or one argument is supposed.

=item C<[connect]: (%s): unknow option, ignored>

B<(warning)>, L<B<connect()> method|/connect()>.
Each option that's not known to B<A::F> is ignored and B<carp>ed.
There're no means for child class to force B<A::F> (as a parent class) to
accept something.
Child classes should process their options by itself.

=item C<[connect]: clean init with (%i) items in FST>

B<(basic trace)>, L<B<connect()> method|/connect()>.
During class construction FST has been found.
That FST consists of I<%i> items.
Those items are number of I<$state>s and not I<$state>s plus I<{state}>s.

=item C<[connect]: FST has no {START} state>

B<(warning)>, L<B<connect()> method|/connect()>.
I<START> I<{state}> is required.
It's missing.
This situation will definetely result in devastating failure later.

=item C<[connect]: FST has no {STOP} state>

B<(warning)>, L<B<connect()> method|/connect()>.
I<STOP> I<{state}> is required.
It's missing.
If FST is such that I<STOP> I<$state> can't be reached
(for whatever reasons)
this may be cosmetic.

=item C<[connect]: ignoring (%i) FST items in trailer>

B<(warning)>, L<B<connect()> method|/connect()>.
A trailer has been found and was ignored.
Beware, I<%i> could be misleading.
For HASH in trailer that HASH is considered and key number is reported.
For list in trailer an B<item> number in list is reported, even if it's twice
as many in FST (what isa hash).
Just because.

=item C<[connect]: (source): unset>

B<(warning)>, L<B<connect()> method|/connect()>.
I<$source> option isa unset, it should be.
There's no other way to feed items in.
This situation will definetely result in devastating failure later.

=item C<[connect]: stealing (%i) items in FST>

B<(basic trace)>, L<B<connect()> method|/connect()>.
During object construction FST has been found.
That FST consists of I<%i> items.
Those items are I<{state}>s, not I<$state>s plus I<{state}>s.

=item C<[fst]: creating {%s} record>

B<(basic trace)>, L<B<fst()> method|/fst()>.
New state record has been created, named I<%s>.

=item C<[fst]: creating {%s}{%s} entry>

B<(basic trace)>, L<B<fst()> method|/fst()>.
New entry named I<%s> (the latter) has been created in record named I<%s> (the
former).

=item C<[fst]: no args>

B<(warning)>, L<B<fst()> method|/fst()>.
No arguments, it's an error.
However, instead of promptly dieing, C<undef> is returned
in blind hope this will be devastating enough.

=item C<[fst]: (%s): no such {fst} record>

B<(warning)>, L<B<fst()> method|/fst()>.
Requested entry I<%s> is missing.
State record ought to exist beforehand for any usecase except
L<record creation|/set {state} of specific $state>.

=item C<[fst]: too many args (%i)>

B<(warning)>, L<B<fst()> method|/fst()>.
Too many args have been passed in.
And again, instead of prompt dieing C<undef> is returned.

=item C<[fst]: updating {%s} record>

B<(basic trace)>, L<B<fst()> method|/fst()>.
Named record (I<%s>) has been updated.

=item C<[fst]: updating {%s}{%s} entry>

B<(basic trace)>, L<B<fst()> method|/fst()>.
Named entry (I<%s>, the latter) in record I<%s> (the former) has been updated.

=item C<[process]: {%s}(%s): changing state: (CONTINUE)>

B<(basic trace)>, L<B<process()> method|/process()>.
Implicit change of state from C<BREAK> to C<CONTINUE> is about to happen.

=item C<[process]: {%s}(%s): entering>

B<(basic trace)>, L<B<process()> method|/process()>.
Entering state flow from I<$state> I<%s> (the former) with I<$action> I<%s>
(the latter).

=item C<[process]: {%s}(%s): going for>

B<(deep trace)>, L<B<process()> method|/process()>.
Trying to enter I<$state> I<%s> (the former) with I<$action> I<%s> (the
latter).

=item C<[process]: {%s}(%s): %s: going with>

B<(basic trace)>, L<B<process()> method|/process()>.
While in I<$state> I<%s> (1st) doing I<$action> I<%s> (2nd) the I<$item>
happens to be I<%s> (3rd).
C<undef> will look like this: C<((undef))>.

=item C<[process]: {%s}(%s): %s: turning with>

B<(basic trace)>, L<B<process()> method|/process()>.
B<switch()> (of I<$state> I<%s>, the 1st) has returned I<$rule> I<%s> (2nd)
and I<$item> I<%s>.
Now dealing with this.

=item C<[process]: {%s}(%s): leaving>

B<(basic trace)>, L<B<process()> method|/process()>.
Leaving state flow with I<$state> I<%s> (the former) and I<$action> I<%s> (the
latter).

=item C<[process]: {%s}(%s): switch returned: (%s)>

B<(basic trace)>, L<B<process()> method|/process()>.
B<switch()> (of I<$state> I<%s>, the 1st, with I<$action> I<%s>, the 2nd) has
been invoked and returned something that was interpreted as I<$rule> I<%s>,
the 3rd.

=item C<[process]: {%s}(%s): unknown action>

B<(croak)>, L<B<process()> method|/process()>.
Attempt to enter I<$state> I<%s> (the former) has been made.
However, it has failed because I<$action> I<%s> (the latter) isn't known.

=item C<< [query]: [$caller]: <%s> package can't [%s] subroutine >>

B<(croak)>, L<B<query()> method|/query()>.
I<$namespace> and I<$what> has been resolved as package I<%s> (the former)
and subroutine I<%s> (the latter).
However, the package can't do such subroutine.

=item C<[query]: [query_dumper]: %s !isa defined>

=item C<[query]: [query_source]: %s !isa defined>

=item C<[query]: [query_switch]: %s !isa defined>

B<(croak)>, L<B<query()> method|/query()>.

lib/FSM.pm  view on Meta::CPAN


=item C<[query]: [query_source]: {namespace} !isa defined>

=item C<[query]: [query_switch]: {namespace} !isa defined>

B<(croak)>, L<B<query()> method|/query()>.
I<$what> isn't CODE, now to resolve it I<$namespace> is required.
Apparently, it was missing all along.

=item C<[query]: [query_dumper]: {namespace} isa (%s)>

=item C<[query]: [query_source]: {namespace} isa (%s)>

=item C<[query]: [query_switch]: {namespace} isa (%s)>

B<(deep trace)>, L<B<query()> method|/query()>.
C<ref $namespace> is I<%s>.

=item C<[query]: [query_dumper]: defaulting %s to $self>

=item C<[query]: [query_source]: defaulting %s to $self>

=item C<[query]: [query_switch]: defaulting %s to $self>

B<(deep trace)>, L<B<query()> method|/query()>.
I<$namespace> is an empty string.
The blackboard object will be used to resolve the callback.

=item C<< [query]: [query_dumper]: going for <%s>->[%s] >>

=item C<< [query]: [query_source]: going for <%s>->[%s] >>

=item C<< [query]: [query_switch]: going for <%s>->[%s] >>

B<(deep trace)>, L<B<query()> method|/query()>.
Attempting to call I<%s> (the latter) method on object of I<%s> (the former)
class.

=item C<< [query]: [query_dumper]: going for <%s>::[%s] >>

=item C<< [query]: [query_source]: going for <%s>::[%s] >>

=item C<< [query]: [query_switch]: going for <%s>::[%s] >>

B<(deep trace)>, L<B<query()> method|/query()>.
Attempting to call I<%s> (the latter) subrouting of package I<%s> (the
former).

=item C<< [query]: [query_dumper]: object of <%s> can't [%s] method >>

=item C<< [query]: [query_source]: object of <%s> can't [%s] method >>

=item C<< [query]: [query_switch]: object of <%s> can't [%s] method >>

B<(croak)>, L<B<query()> method|/query()>.
The object of I<%s> (the former) class can't do I<%s> (the latter) method.

=item C<[state]: changing state: (%s) (%s)>

B<(deep trace)>, L<B<state()> method|/state()>.
Exposes change of state from previous (former I<%s>)
to current (latter I<%s>).

=item C<[state]: too many args (%i)>

B<(warning)>, L<B<state()> method|/state()>.
Obvious.
None or one argument is supposed.
B<state()> has returned C<undef> in this case,
most probably will bring havoc in a moment.

=item C<[turn]: (%s): no such {fst} record>

B<(warning)>, L<B<turn()> method|/turn()>.
Peeking for I<[turn]>s of I<%s> I<$state> yeilds nothing, there's no such
state.

=item C<[turn]: {%s}: none supported turn>

B<(warning)>, L<B<turn()> method|/turn()>.
Whatever content of I<%s> entry is FSM doesn't know how to handle it.

=item C<[turn]: {%s}(%s): unknown turn>

B<(croak)>, L<B<turn()> method|/turn()>.
There was request for I<[turn]> I<%s> (the latter) of I<$state> I<%s> (the
former).
While I<{state}> record has been found and is OK,
there is no such I<$rule>.

=item C<[turn]: no args>

B<(warning)>, L<B<turn()> method|/turn()>.
No argumets, it's an error.

=item C<[turn]: too many args (%i)>

B<(warning)>, L<B<turn()> method|/turn()>.
There's no way to handle that many (namely: I<%i>) arguments.

=item C<[verify]: {%s}{%s}: %s !isa defined>

B<(croak)>, L<B<verify()> method|/verify()>.
I<$rc> queried
from something in I<{fst}> related to I<%s> (3rd)
(value of which is I<%s> (2nd))
while in I<$state> I<%s> (1st)
isn't defined.

=item C<[verify]: {%s}{%s}: %s isa (%s), should be (%s)>

B<(croak)>, L<B<verify()> method|/verify()>.
B<ref> of I<$rc> queried
from something in I<{fst}> related to I<%s> (3rd)
(value of which is I<%s> (2nd))
while in I<$state> I<%s> (1st) is I<%s> (4th).
While it should be I<%s> (5th)
(the last one is literally I<$test>).

=back



( run in 2.282 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )