#!/usr/local/bin/perl -w ### # # Arbitary maths expression evaluator # (c) copyright Simon Wistow , 2003 # Released under the same terms as Perl itself # # based on the grammar by Abigail available from # http://ucan.foad.org/~abigail/Perl/Talks/Parsing/calculator.html # # and some code from # http://hea-www.harvard.edu/~alexey/calc-src.txt # ### # # Some examples # # 2 + 4 # 2 * 4 + 3 # 2 * (4+3) # 8 * -10 # 2.6 ^ 5.9 # 3.9**12.4 # 5%2 # sin(2) # abs(-2) # ceil(2.3) # foo = sqrt(100); bar = rand(30); foo - bar # r = 2; d = pi * r^2; d # bin(2) use strict; use Parse::RecDescent; use Math::Big; use Math::BigFloat; #use Math::BigInt; die "Usage $0 \n" unless @ARGV; my $expr = join " ", @ARGV; my $gram = do {local $/; }; #print $gram; $::RD_HINT = 1; $::RD_WARN = 1; $::RD_ERRORS = 1; my $parser = Parse::RecDescent -> new ($gram) or die "Compilation error!\n"; my $res; foreach my $part ( split /;/, $expr) { $res = $parser->start($part) || die "Not a proper math expression\n"; } print "$res\n"; __DATA__ { my %variables } # exp(1) { $variables {'e'} = $variables {'E'} = Math::BigFloat->new(2.718281828459045) } # pi :) mmm, pi { $variables {'pi'} = $variables {'PI'} = Math::BigFloat->new(3.14159265358979) } # speed of light { $variables {'c'} = $variables {'C'} = Math::BigFloat->new(2.9979245620e+10) } # gravitational constant { $variables {'g'} = $variables {'G'} = Math::BigFloat->new(6.67259e-08) } # charge of an electron { $variables {'ee'} = $variables {'EE'} = Math::BigFloat->new(4.8032068e-10) } # erg*s Planck's constant { $variables {'h'} = $variables {'H'} = Math::BigFloat->new(6.6260755e-27) } # mass of electron { $variables {'me'} = $variables {'ME'} = Math::BigFloat->new(9.1093897e-28) } # mass of proton { $variables {'mp'} = $variables {'MP'} = Math::BigFloat->new(1.6726231e-24) } # erg { $variables {'eV'} = $variables {'keV'} = Math::BigFloat->new(1.602192e-12) } # fine structure constant { $variables {'c'} = $variables {'C'} = Math::BigFloat->new(2.9979245620e+10) # Thomson cross section = 8pi/3*re^2 { $variables {'sigmaT'} = Math::BigFloat->new(6.6524616e-25) } # (erg/K) Blotzman constant { $variables {'k'} = $variables {'K'} = Math::BigFloat->new(1.380658e-16) } # mol^-1 Avogadro constant { $variables {'na'} = $variables {'NA'} = Math::BigFloat->new(6.0221367e+23) } # Stefan-Boltzmann constant { $variables {'sigma'} = Math::BigFloat->new(5.67051e-5) } start: statement /^\Z/ { $variables {'.'} = $item [1] } # a statement can either be a variable assignment # or a conversion to a non numeric item statement: variable '=' statement { $variables {$item [1]} = $item [3] } | 'bin' '(' term ')' { $item[3]->as_bin } | 'hex' '(' term ')' { $item[3]->as_hex } | 'oct' '(' term ')' { oct("$item[3]") } | expression # sort out precedence in very neat manner expression: term '+' expression { $item [1] + $item [3] } | term '-' expression { $item [1] - $item [3] } | term # handle these things term: factor '*' term { $item [1] * $item [3] } | factor 'x' term { $item [1] * $item [3] } | factor '/' term { $item [1] / $item [3] } | factor '%' term { $item [1] % $item [3] } | factor '**' term { $item [1] ** $item[3] } | factor '^' term { $item [1] ** $item[3] } | 'sin' '(' term ')' { Math::Big::sin($item[3], $item[3]->precision) } | 'cos' '(' term ')' { Math::Big::cos($item[3], $item[3]->precision) } | 'tan' '(' term ')' { Math::Big::tan($item[3], $item[3]->precision) } | 'exp' '(' term ')' { Math::BigFloat->new(exp("$item[3]")) } | 'rand' '(' term ')' { Math::BigFloat->new(rand($item[3])) } | 'int' '(' term ')' { $item[3]->is_positive? $item[3]->bfloor : $item[3]->bceil } | 'abs' '(' term ')' { $item[3]->babs() } | 'neg' '(' term ')' { $item[3]->bneg() } | 'log' '(' term ')' { $item[3]->blog } | 'floor' '(' term ')' { $item[3]->bfloor() } | 'ceil' '(' term ')' { $item[3]->bceil() } | 'sqrt' '(' term ')' { $item[3]->bsqrt } | factor factor: number | variable { $variables {$item [1]} ||= Math::BigFloat -> new (0) } | '+' factor { $item [2] } | '-' factor { $item [2] * -1 } | '(' statement ')' { $item [2] } # parse a floating point number. Thank you Advanced Perl programming number: /-?(?:\d+(?:\.\d*)?|\.\d+)/ { Math::BigFloat -> new ($item [1]) } # Variables can only be letters variable: /[a-z]+/i | '.'