#!/usr/bin/perl
#
# eg7: RPN evaluator, version 2, more ops
#

use strict;
use warnings;
use Function::Parameters qw(:strict);

# arithmetic operators and how to do them..
my %op = (
	'+'      => fun ($x,$y) { return $x + $y },
	'-'      => fun ($x,$y) { return $x - $y },
	'*'      => fun ($x,$y) { return $x * $y },
	'/'      => fun ($x,$y) { return $x / $y },
	'%'      => fun ($x,$y) { return $x % $y },
	'^'      => fun ($x,$y) { return $x ** $y },
	'>'      => fun ($x,$y) { return $x > $y ? 1 : 0 }, # in case we print value
	'swap'   => fun ($x,$y) { return ($y, $x) },
);

#
# my $n = eval_rpn( @atom ):
#	Given an array @atom of atomic expression
#	elements (numbers or operands) in reverse
#	polish notation (rpn), evaluate the expression
#	and return the answer $n.
#
fun eval_rpn(@atom)                             # each atom: operator or number
{
	my @stack;                              # evaluation stack
	foreach my $atom (@atom)
	{
		if( $atom =~ /^\d+$/ )          # number?
		{
			push @stack, $atom;
		} else				# operator?
		{
			die "eval_rpn: bad atom $atom\n"
				unless exists $op{$atom};
			my $depth = @stack;
			die "eval_rpn: stack depth $depth when 2 needed\n"
				if $depth < 2;
			my $y = pop @stack;
			my $x = pop @stack;
			push @stack, $op{$atom}->( $x, $y );
		}
	}
	my $depth = @stack;
	die "eval_rpn: @ end, stack depth $depth, should be 1\n"
				if $depth != 1;
	return pop @stack;
}

my @exprs = (
	"7 5 * 9 % 2 ^",
	"7 5 * 9 % 2 swap ^"
);
foreach my $str (@exprs)
{
	my $n = eval_rpn( split( /\s+/, $str ) );
	print "rpn: $str\n     result: $n\n";
}
