use 5.010; use warnings; use Test::More 'no_plan'; use List::Util qw< reduce >; sub translator { my ($errormsg, $rulename, $context) = @_; if (substr($rulename,0,1) eq '-') { $rulename = substr($rulename,1); return "You forgot to define $rulename. :-("; } if ($errormsg eq q{}) { if ($rulename) { return "<$rulename> failed to match '$context'"; } else { return "Main pattern failed to match '$context'"; } } if (lc(substr($errormsg,0,6)) eq 'wanted') { return "$errormsg, but was given '$context'. What's up with that?"; } return $errormsg; } my $calculator = do{ use Regexp::Grammars; qr{ \A (?: \Z | ) <[_Operand=Mult]> ** <[_Op=(\+|\-)]> (?{ $MATCH = shift @{$MATCH{_Operand}}; for my $term (@{$MATCH{_Operand}}) { my $op = shift @{$MATCH{_Op}}; if ($op eq '+') { $MATCH += $term; } else { $MATCH -= $term; } } }) | (?: <[_Operand=Pow]> ** <[_Op=(\*|/|%)]> (?{ $MATCH = reduce { eval($a . shift(@{$MATCH{_Op}}) . $b) } @{$MATCH{_Operand}}; }) ) (?: <[_Operand=Term]> ** <_Op=(\^)> (?{ $MATCH = reduce { $b ** $a } reverse @{$MATCH{_Operand}}; }) ) (?: | \( \) ) <...> | }xms }; local $/ = ""; { my $temp = Regexp::Grammars::set_error_translator(\&translator); while (my $input = ) { chomp $input; my ($text, $expected) = split /\s+/, $input, 2; if ($text =~ $calculator) { is $/{Answer}, $expected => "Input $.: $text"; } else { is_deeply \@!, eval($expected), => "Input $.: $text"; } } } { use Regexp::Grammars; if ('foo' =~ m{ <...> }xms) { fail 'Restore default translator'; } else { is_deeply \@!, ["Can't match subrule (not implemented)"] => 'Restore default translator'; } } __DATA__ 2 2 2*3+4 10 2zoo [ "Extra junk after expression at index 1: 'zoo'", "Wanted end of input, but was given 'zoo'. What's up with that?", "Main pattern failed to match 'zoo'", "You forgot to define Trailing_stuff. :-(", ] zoo [ " failed to match 'zoo'", "You forgot to define Trailing_stuff. :-(", ]