use 5.010;
use warnings;

package Calculator;
use List::Util qw< reduce >;

sub Answer {
    my ($class, $result_hash) = @_;

    my $sum = shift @{$result_hash->{Operand}};

    for my $term (@{$result_hash->{Operand}}) {
        my $op = shift @{$result_hash->{Op}};
        if ($op eq '+') { $sum += $term; }
        else            { $sum -= $term; }
    }

    return $sum;
}

sub Mult {
    my ($class, $result_hash) = @_;

    return reduce { eval($a . shift(@{$result_hash->{Op}}) . $b) }
                  @{$result_hash->{Operand}};
}

sub Pow {
    my ($class, $result_hash) = @_;

    return reduce { $b ** $a } reverse @{$result_hash->{Operand}};
}


use Test::More 'no_plan'; 

my $calculator = do{
    use Regexp::Grammars;
    qr{
        <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 = <DATA>) {
    chomp $input;
    my ($expr, $result) = split /\s*=\s*/, $input;
    ok +($expr =~ $calculator->with_actions(bless {}, 'Calculator')) => "Matched expression: $expr";
    cmp_ok $/{Answer}, '==', $result => "Got right answer ($result)";
}

ok +('1+1' =~ $calculator) => "Matched final expression";
use Data::Dumper 'Dumper';
cmp_ok $/{Answer}{""}, 'eq', '1+1' => "Got right answer (hash)";

__DATA__
1+1+1     = 3
2^3*4+5   = 37
2+3*4^5   = 3074
2+3*4+5   = 19
2*3+4*5   = 26
2*(3+4)*5 = 70
2+3+4-5   = 4
100/10/2  = 5
100/10*2  = 20
