#!/usr/bin/perl -w use strict; # {{{ 0. Basic modules and declarations. use Carp; use Data::Dumper; use POE; use POE::Wheel::ReadLine; use vars qw($parser $player @objects @libmsgs $input); use constant false => 0; use constant true => 1; open LOG, ">log"; sub w { print LOG (@_, "\n") } # }}} # {{{ 2. Library messages. use constant UNK_VERB => 0; use constant UNK_WORD => 1; use constant NO_FORM => 2; sub libmsg { return meval($libmsgs[$_[0]], @_[1..$#_]) } @libmsgs[UNK_VERB, NO_FORM] = ("I don't know how to do that.\n", "Those words don't fit together; I can't understand you.\n"); { local $" = ''; $libmsgs[UNK_WORD] = sub { "I only understood you as wanting to @_" }; } # {{{ 1. The parser. use constant PARSE_ERR => -1; use constant PARSE_OK => 0; # {{{ 1.1. init_parser sub init_parser { $parser && return 1; my $p = POE::Session->create ( # {{{ 1.1.1. Actual parsing routines. inline_states => { # {{{ 1.1.1.1. _start _start => sub { w("Parser starting"); $parser = $_[SESSION]; }, # }}} # {{{ 1.1.1.2. parse_line parse_line => sub { my ($heap, $text) = @_[HEAP, ARG0]; my (@text, $verb, $counter); w("Parsing line: $text"); # {{{ 1.1.1.2.1. Splitting the line into pieces for ($text) { pos = 0; while ( /(?: (?<=[a-zA-Z0-9_$%\#\-]) ([,!&().\/:; \t]+) (?=[a-zA-Z0-9_$%\#\-]))| (?: (?<=[,!&().\/:; \t]+) ([a-zA-Z0-9_$%\#\-]+) (?=[,!&9).\/:; \t])/gx) { w("Parsed piece: $+"); push @text, $+; } } # }}} # {{{ 1.1.1.2.2. Finding the verb ($verb = $heap->{verbs}->{$text[0]}) or (print libmsg(UNK_VERB), return PARSE_ERR); # }}} # {{{ 1.1.1.2.3. Parsing the verb $counter = 0; w("Got verb: \n",Dumper $verb); FORM: foreach (@$verb) { my @usedobjs; TOKEN: foreach (@$verb[0..($#$verb-1)]) { w("Token: $_"); next TOKEN unless $_; if (!ref) { if (/[a-zA-Z0-9_$%\#-]/) { ($text[$counter] =~ /^\s*$/) and $counter++; ($_ eq $text[$counter]) or next FORM; } else { my $x = $_; $x =~ tr/ \t//d; ($text[$counter] eq $x) or next FORM; } } elsif (ref eq 'CODE') { my $flag = false; OBJ: foreach my $obj (@objects) { if ($_->($obj)) { push @usedobjs, $obj; $flag = true; last OBJ; } } (!$flag) && next FORM; } } $verb->[$#$verb-1]->(@usedobjs); return PARSE_OK; } # }}} print libmsg(NO_FORM); return PARSE_ERR; }, # }}} # {{{ 1.1.1.3. mod_verb mod_verb => sub { my ($heap, $verb, $grammar) = @_[HEAP, ARG0, ARG1]; w("Modifying verb $verb to: ",Dumper $grammar); $heap->{verbs}->{$verb} = [ @$grammar ]; }, } # }}} ); } # }}} # }}} # }}} # }}} # {{{ 3. iscode, meval. sub iscode { return (ref shift eq 'CODE') } sub meval { return $_[0]->(@_[1..$#_]) if &iscode; return $_[0] } # }}} # {{{ 4. Input. # {{{ 4.1. init_input sub init_input { $input && return 1; my $i; $i = POE::Session->create ( # {{{ 4.1.1. Actual input subroutines. inline_states => { # {{{ 4.1.1.1. _start _start => sub { w("Input handler starting"); my $heap = $_[HEAP]; $input = $_[SESSION]; $heap->{wheel} = POE::Wheel::ReadLine->new ( InputEvent => 'got_line' ); w("Wheel created: ",$heap->{wheel}); $heap->{wheel}->get('> '); }, # }}} # {{{ 4.1.1.2. got_line got_line => sub { my ($heap, $line) = @_[HEAP, ARG0]; if (defined $line) { w("Line input: $line"); $heap->{wheel}->addhistory($line); $_[KERNEL]->call($parser, 'parse_line', $line); w("Event posted"); $heap->{wheel}->get('> '); } elsif ($_[ARG1] eq 'interrupt') { exit 0; } }, # }}} } # }}} ); } # }}} # }}} # {{{ 5. Tester. w("Calling init_input"); init_input(); w("Calling init_parser"); init_parser(); w("Posting mod_verb"); $poe_kernel->post($parser, 'mod_verb', "quit", [ '', sub { print "\n"; exit 0 } ]); w("Running kernel"); $poe_kernel->run;