#!/usr/bin/perl use strict; use warnings; use Parse::RecDescent; my %dispatch = ( '+' => sub { $_[0] + $_[1] }, '-' => sub { $_[0] - $_[1] }, '*' => sub { $_[0] * $_[1] }, '/' => sub { $_[0] / $_[1] }, '^' => sub { $_[0] ** $_[1] }, 'abs' => sub { abs $_[0] }, 'sqrt' => sub { sqrt $_[0] }, ); sub calculate { my $rule = shift @_; if ($rule eq 'FUNCTION') { my ($func, $x) = @_; my $val = eval { $dispatch{$func}->($x); }; die $@ if $@; return $val; } my @atom = @{ shift @_ }; my $val = shift @atom; while (@atom) { my ($op, $num) = splice(@atom, 0, 2); eval { $val = $dispatch{$op}->($val, $num); }; die $@ if $@; } return $val; } my $grammar = <<'__GRAMMAR__'; evaluate : EXPR /\Z/ { $item[1] } EXPR : ADD_SUB { $item[1] } ADD_SUB : { main::calculate( @item ) } ADD_SUB_OP : '+' | '-' MUL_DIV_MOD : { main::calculate( @item ) } MUL_DIV_MOD_OP : '*' | '/' | '%' POW : { main::calculate( @item ) } POW_OP : '^' FUNCTION : FUNC_NAME GROUP { main::calculate( @item ) } | GROUP FUNC_NAME : 'abs' | 'sqrt' GROUP : '(' EXPR ')' { $item[2] } | NUMBER NUMBER : FLOAT | INTEGER | NAN INTEGER : /[+-]?\d+/ FLOAT : /([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?/ NAN : /(Inf(inity)?|NaN)/i __GRAMMAR__ my $parser = Parse::RecDescent->new($grammar) or die("Bad grammar\n"); my $answer = $parser->evaluate('11 - (4 + 4)^3 * sqrt(5 * (6 - 1)) + abs(-3)'); print defined $answer ? $answer : 'Invalid expression';