The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
#Copyright 2007-8 Arthur S Goldstein
use Parse::Stallion;

my %calculator_rules = (
 start_expression => A(
   'expression', 'end_of_string',
   E(sub {return $_[0]->{expression}})
  )
,
 expression =>
   A('term', 
    M(A('plus_or_minus', 'term')),
    E(sub {my $to_combine = $_[0]->{term};
    my $plus_or_minus = $_[0]->{plus_or_minus};
    my $value = shift @$to_combine;
    for my $i (0..$#{$to_combine}) {
      if ($plus_or_minus->[$i] eq '+') {
        $value += $to_combine->[$i];
      }
      else {
        $value -= $to_combine->[$i];
      }
    }
    return $value;
   })
  ),
,
 term =>
   A('factor', 
    M (A('times_or_divide_or_modulo', 'factor')),
    E(sub {my $to_combine = $_[0]->{factor};
    my $times_or_divide_or_modulo = $_[0]->{times_or_divide_or_modulo};
    my $value = shift @$to_combine;
    for my $i (0..$#{$to_combine}) {
      if ($times_or_divide_or_modulo->[$i] eq '*') {
        $value *= $to_combine->[$i];
      }
      elsif ($times_or_divide_or_modulo->[$i] eq '%') {
        $value = $value % $to_combine->[$i];
      }
      else {
#could check for zero
        $value /= $to_combine->[$i];
      }
    }
    return $value;
   })
  ),
,
 factor =>
   A('fin_exp', 
    M (A( 'power_of', 'fin_exp')),
    E(sub {my $to_combine = $_[0]->{fin_exp};
    my $value = pop @$to_combine;
    while ($#{$to_combine} > -1) {
      $value = (pop @$to_combine) ** $value;
    }
    return $value;
   })
  ),
,
fin_exp =>
  O(A('left_parenthesis', 'expression', 'right_parenthesis',
     E(sub {return $_[0]->{expression} })
    ),
    A('number',
     E(sub {return $_[0]->{number} })
    ),
   )
,
end_of_string => qr/\z/,
number => qr/\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*/,
,
left_parenthesis => qr/\s*(\()\s*/,
right_parenthesis => qr/\s*(\))\s*/,
power_of => qr/\s*(\*\*)\s*/,
plus_or_minus =>
  O('plus', 'minus'),
plus => qr/\s*(\+)\s*/,
minus => qr/\s*(\-)\s*/,
times_or_divide_or_modulo =>
  O('times', 'divided_by', 'modulo')
,
modulo => qr/\s*(\%)\s*/,
times => qr/\s*(\*)\s*/,
divided_by => qr/\s*(\/)\s*/
);

my $calculator_parser = new Parse::Stallion(\%calculator_rules);
#$calculator_parser->generate_evaluate_subroutines;

my $result = $calculator_parser->parse_and_evaluate("7+4");
print "should be 11, result is $result\n";

$result = $calculator_parser->parse_and_evaluate("7*4");
print "should be 28, Result is $result\n";

$result = $calculator_parser->parse_and_evaluate("3+7*4");
print "should be 31, result is $result\n";

$result = $calculator_parser->parse_and_evaluate("3+-+7*4",{parse_info=>$parse_info = {}});

print "should be 0, Parse succeeded: ".$parse_info->{parse_succeeded}."\n";

print "\nAll done\n";