This is a calculator using polish prefix notation with parentheses. It will properly parse and calculate when given input such as:
(+ 1 2 3 (* 4 3 (- 2 1) (% 5 2)) 3 4)
#NOTE: % is the modulo operator, / is for dividing.
It can do this, because I implemented it using recursive ascent parsing. If you want to try it, I would recommend running it in verbose mode to see what it is doing. You can do this by putting a space followed by 1,v,V,verbose,VERBOSE, or similar after the program name when you run it. Enjoy.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dump qw(dump);
sub arith;
sub solvepart($);
sub testiterations;
sub reformat;
my $VERBOSE=shift//0;
my $MAXITERATIONS=500;
my $iterations;
my @stack;
my $num=qr/(-?\d++\.?\d*?)/;
while(<>){
$iterations=0;
my $given = $_;
if ($given ~~ /exit|end|quit/i){
die "PROGRAM TERMINATED\n";
}
if ($given !~ /^(\((?:[^()]++|(?-1))*+\))/){
die "IMPROPER INPUT\n";
}
until ($given !~ /[()]/){
reformat $given;
$given =~ s/(.*)(\(\S++ ++$num ++($num)?\))/$1.solvepart($2)/e
+;
testiterations;
}
print "\nANSWER: ".$given."\n\n";
}
sub testiterations{
$iterations++;
if ($iterations==$MAXITERATIONS){
die "MAX ITERATIONS ACHIEVED: ABORTING\n";
}
}
sub reformat($){
1 while $_[0] =~ s/(?<leading> \((?<op>\S++)\s++$num [^()]*?\
+s++)
(?<nums>$num\s++$num)
\)
/$+{leading}\($+{op}\ $+{nums}\)\)/x;
}
sub solvepart($){
my $given = shift;
unshift(@stack, [$2,$3,$4]) while $given =~ s/(.*)\((\S++) ++(-?\d
+++\.?\d*?) ++(-?\d++\.?\d*?)?\)/$1/;
if ($VERBOSE or $VERBOSE ~~ /v(?:erbose)?/i){
print "\nDATA:\n";
dump @stack;
}
return arith;
}
sub arith{
my $value=0;
while(@stack){
#my @level = map{handleneg($_)}@{pop @stack};
my @level = @{pop @stack};
if ($level[0]eq'+'){
$level[2]
?($value=$level[1]+$level[2])
:($value+=$level[1]);
}
elsif ($level[0]eq'-'){
$level[2]
?($value=$level[1]-$level[2])
:($value=$level[1]-$value);
}
elsif ($level[0]eq'/'){
$level[2]
?($value=$level[1]/$level[2])
:($value=$level[1]/$value);
}
elsif ($level[0]eq'*'){
$level[2]
?($value=$level[1]*$level[2])
:($value*=$level[1]);
}
elsif ($level[0]eq'**'or$level[0]eq'^'){
$level[2]
?($value=$level[1]**$level[2])
:($value=$level[1]**$value);
}
elsif ($level[0]eq'%'){
$level[2]
?($value=$level[1] % $level[2])
:($value=$level[1] % $value);
}
else {
die "IMPROPER INPUT\n";
}
}
return $value;
}
You can exit by inputing eXiT, end, QUIT, or similar.