package FSM; use strict; use Carp; use vars qw($AUTOLOAD); # Create new object sub new { my $self = {}; my ($proto, $initial) = @_; my $class = ref($proto) || $proto; # Init ourselves $self->{INITIAL} = $initial; $self->{CURRENT} = $initial; $self->{STATES} = {}; bless ($self, $class); return $self; } sub setInitialState { my ($self, $initial) = @_; $self->{INITIAL} = $initial; return $self; } sub setCurrentState { my ($self, $current) = @_; $self->{CURRENT} = $current; return $self; } sub getCurrentState { my ($self, $current) = @_; return $self->{CURRENT}; } sub reset { my $self = shift; $self->{CURRENT} = $self->{INITIAL}; return $self; } sub addState { my $self = shift; my %args = @_; $self->{STATES}->{$args{STATE}}->{$args{SYMBOL}} = {NEXT => $args{NEXT}, ACTION => $args{ACTION}}; return $self; } sub removeState { my $self = shift; my %args = @_; if (exists $args{SYMBOL}) { delete $self->{STATES}->{$args{STATE}}->{$args{SYMBOL}}; } else { delete $self->{STATES}->{$args{STATE}}; } return $self; } # Be sure to override in child sub normalize { my ($self, $symbol) = @_; my $ret = {}; $ret->{SYMBOL} = $symbol; return $ret; } sub process { my ($self, $rawSymbol) = @_; my $state = $self->{STATES}->{$self->{CURRENT}}; $rawSymbol = $self->normalize($rawSymbol); my $symbol = $rawSymbol->{SYMBOL}; print STDERR "Current state " . $self->{CURRENT} . ", got symbol " . $symbol . "\n"; if (!exists $state->{$symbol} && exists $state->{'*'}) { print STDERR "Unrecognized symbol " . $symbol . ", using *\n"; $symbol = "*"; } # Do some action! $state->{$symbol}->{ACTION}($self, $rawSymbol) if ref $state->{$symbol}->{ACTION}; # Switch state if (exists $state->{$symbol}->{NEXT}) { $self->{CURRENT} = $state->{$symbol}->{NEXT}; } else { die "Don't know how to handle symbol " . $rawSymbol->{SYMBOL}; } return $self; } 1;
Symbol | condition | Next state | Act |
---|---|---|---|
LOGIN | INIT | SESSION | Opening session |
* | INIT | INIT | - |
* | SESSION | SESSION | - |
SAY | SESSION | SESSION | Print line number N |
EXIT | SESSION | INIT | - |
MEMORIZE | SESSION | STORE | - |
* | STORE | STORE | Save string to buffer |
EXIT | STORE | SESSION | - |
package ChatBot; use FSM; @ISA = ("FSM"); use strict; use Carp; use vars qw($AUTOLOAD); sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = $class->SUPER::new("INIT"); $self->addState(STATE => "INIT", SYMBOL => "*", NEXT => "INIT", ACTION => \&doIntroduce); $self->addState(STATE => "INIT", SYMBOL => "LOGIN", NEXT => "SESSION", ACTION => \&doLogin); $self->addState(STATE => "INIT", SYMBOL => "EXIT", NEXT => "INIT", ACTION => \&doQuit); $self->addState(STATE => "SESSION", SYMBOL => "*", NEXT => "SESSION"); $self->addState(STATE => "SESSION", SYMBOL => "EXIT", NEXT => "INIT"); $self->addState(STATE => "SESSION", SYMBOL => "SAY", NEXT => "SESSION", ACTION => \&doSay); $self->addState(STATE => "SESSION", SYMBOL => "MEMORIZE",NEXT => "STORE"); $self->addState(STATE => "STORE", SYMBOL => "*", NEXT => "STORE", ACTION => \&doRemember); $self->addState(STATE => "STORE", SYMBOL => "EXIT", NEXT => "SESSION"); $self->{SESSION} = {}; $self->{LOGIN} = ""; return $self; } sub normalize { my ($self, $symbol) = @_; my $ret = {}; if ($symbol =~ /^(\S+)(.*)$/) { $ret->{SYMBOL} = uc $1; $ret->{DATA} = $2; $ret->{RAW} = $symbol; } else { $ret->{SYMBOL} = "*"; $ret->{DATA} = $symbol; $ret->{RAW} = $symbol; } return $ret; } sub doIntroduce { my $self = shift; print "Please introduce yourself first!\n"; return $self; } sub doLogin { my ($self, $symbol) = @_; print "Welcome," . $symbol->{DATA} . "\n"; $self->{LOGIN} = $symbol->{DATA}; $self->{SESSION}->{$self->{LOGIN}} = () unless exists $self->{SESSION}->{$self->{LOGIN}}; return $self; } sub doSay { my ($self, $symbol) = @_; if (defined $self->{SESSION}->{$self->{LOGIN}}->[$symbol->{DATA}]) { print $self->{SESSION}->{$self->{LOGIN}}->[$symbol->{DATA}]; } else { print "No record\n"; } return $self; } sub doRemember { my ($self, $symbol) = @_; push @{ $self->{SESSION}->{$self->{LOGIN}} }, $symbol->{RAW}; return $self; } sub doQuit { my ($self, $symbol) = @_; print "Bye bye!\n"; exit; return $self; } 1;
{ 'INIT' => { '*' => { 'ACTION' => \&doIntroduce, 'NEXT' => 'INIT' }, 'LOGIN' => { 'ACTION' => \&doLogin, 'NEXT' => 'SESSION' }, 'EXIT' => { 'ACTION' => \&doQuit, 'NEXT' => 'INIT' } }, 'STORE' => { '*' => { 'ACTION' => \&doRemember, 'NEXT' => 'STORE' }, 'EXIT' => { 'NEXT' => 'SESSION' } }, 'SESSION' => { 'SAY' => { 'ACTION' => \&doSay, 'NEXT' => 'SESSION' }, '*' => { 'NEXT' => 'SESSION' }, 'MEMORIZE' => { 'NEXT' => 'STORE' }, 'EXIT' => { 'NEXT' => 'INIT' } } }
use ChatBot; $bot = ChatBot->new(); while(<>) { $bot->process($_); }
hello world! login %username% hello world! say 3 memorize hey, do you really remember everything i would say? let's check exit say 0 exit hello login %username% say 1 exit
Please introduce yourself first! Welcome, %username% No record hey, do you really remember everything i would say? Please introduce yourself first! Welcome, %username% let's check
Source: https://habr.com/ru/post/141503/
All Articles