Skip to content

Instantly share code, notes, and snippets.

@marcoarthur
Created July 25, 2021 01:48
Show Gist options
  • Save marcoarthur/3e1d4f657fde29dcbb42758032bb5432 to your computer and use it in GitHub Desktop.
Save marcoarthur/3e1d4f657fde29dcbb42758032bb5432 to your computer and use it in GitHub Desktop.
Regexp::Grammar plus Mojo::Promises
use strictures 2;
use 5.030;
## no critic Perl::Critic::Policy::Modules::RequireFilenameMatchesPackage
package Calculator::Actions {
use Mojo::Base "Mojo::EventEmitter", -async_await, -signatures;
use List::Util qw< reduce >;
async sub Answer( $self, $MATCH_ref ) {
my $value = await shift @{$MATCH_ref->{Operand}};
for my $term (@{$MATCH_ref->{Operand}}) {
my $op = shift @{$MATCH_ref->{Op}//=[]};
if ($op eq '+') { $value += await $term; }
else { $value -= await $term; }
}
$self->emit( sub_exp => { exp => $MATCH_ref->{""}, return => $value } );
return Mojo::Promise->timer( int(rand(3)) => $value );
}
async sub Mult($self, $MATCH_ref) {
my $value = shift @{$MATCH_ref->{Operand}};
$value = await $value if ref($value);
for my $term (@{$MATCH_ref->{Operand}}) {
my $op = shift @{$MATCH_ref->{Op}//=[]};
if ($op eq '*') {
$value *= ref($term) ? await $term : $term;
}
elsif( $op eq '/' ) {
$value /= ref($term) ? await $term : $term;
}
elsif( $op eq '%' ) {
$value %= ref($term) ? await $term : $term;
}
else {
warn "Unknown op $op";
}
}
# BUG:
# we have a bug here, since we can't await inside
# reduce function, and we need in order to resolve
# the promise before operate on its value.
# my $val = reduce {
# # $b can be a promise.
# my $eval = $a . shift(@{$MATCH_ref->{Op}}) . $b;
# eval($eval)
# }
# @{$MATCH_ref->{Operand}};
$self->emit( sub_exp => { exp => $MATCH_ref->{""}, return => $value } );
return Mojo::Promise->timer( int(rand(2)) => $value );
}
sub Pow {
my ($self, $MATCH_ref) = @_;
reduce { $b ** $a } reverse @{$MATCH_ref->{Operand}};
}
}
# extracted from Damian
my $calculator = do{
use Regexp::Grammars;
qr{
\A
<Answer>
<rule: Answer>
<[Operand=Mult]>+ % <[Op=(\+|\-)]>
<rule: Mult>
<[Operand=Pow]>+ % <[Op=(\*|/|%)]>
<rule: Pow>
<[Operand=Term]>+ % <Op=(\^)>
<rule: Term>
<MATCH=Literal>
|
\( <MATCH=Answer> \)
<token: Literal>
<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
}xms
};
while (my $input = <>) {
my $actions = Calculator::Actions->new;
$actions->on(
sub_exp => sub {
my (undef, $exp) = @_;
say sprintf "calculating %s --> %s", $exp->{exp}, $exp->{return};
}
);
if ($input =~ $calculator->with_actions($actions) ) {
$/{Answer}->then( sub {
say "Solved!";
say '--> ', shift
}
)->wait;
}
}
__END__
What is this about?
This is an example to show that we can have some grammar to parse an expression
in this case an arithmetical computation. Wrap the actions class that solves the
computation, but using Promises, so the whole parse tree is computing forming
promises to be resolved/rejected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment