1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
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
<Answer>
(?:
\Z
|
<warning: (?{ "Extra junk after expression at index $INDEX: '$CONTEXT'" })>
<warning: Wanted end of input>
<error:>
)
<rule: Answer>
<[_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; }
}
})
|
<Trailing_stuff>
<rule: Mult>
(?:
<[_Operand=Pow]> ** <[_Op=(\*|/|%)]>
(?{ $MATCH = reduce { eval($a . shift(@{$MATCH{_Op}}) . $b) }
@{$MATCH{_Operand}};
})
)
<rule: Pow>
(?:
<[_Operand=Term]> ** <_Op=(\^)>
(?{ $MATCH = reduce { $b ** $a } reverse @{$MATCH{_Operand}}; })
)
<rule: Term>
(?:
<MATCH=Literal>
| \( <MATCH=Answer> \)
)
<rule: Trailing_stuff>
<...>
<token: Literal>
<error:>
|
<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
}xms
};
local $/ = "";
{
my $temp = Regexp::Grammars::set_error_translator(\&translator);
while (my $input = <DATA>) {
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{ <Answer> <rule: Answer> <...> }xms) {
fail 'Restore default translator';
}
else {
is_deeply \@!, ["Can't match subrule <Answer> (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 [
"<Literal> failed to match 'zoo'",
"You forgot to define Trailing_stuff. :-(",
]
|