Acme-FSM

 view release on metacpan or  search on metacpan

lib/FSM.pm  view on Meta::CPAN

The FST is just borrowed from source object during object construction.
Thus, in the synopsis, I<$bb3> and I<$bb2> use references to HASH
I<%$fst2>.
An idea behind this is to minimize memory footprint.
OTOH, maninpulations with one HASH are effectevely manipulations with FST of
any other copy-object that borrowed that FST.

IOW, anything behind FST HASH of class construction or options HASH of object
construction isa trailer and B<carp>ed.
Obviously, there's no trailer in class construction with list FST.

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



( run in 1.169 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )