#!/usr/bin/perl
#
# eg6: RPN evaluator, version 1
#

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 },
);

#
# my $n = eval_rpn( @atom ):
#	Given an array @atom of numbers or operands in Reverse
#	Polish Notation (rpn), evaluate 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 = (
	"5 6 * 7 -",
	"1 2 3 * + 4 - 5 *",
	"1 2 3 4 5 6 7 8 9 10 + + + + + + + + +",
);
foreach my $str (@exprs)
{
	my $n = eval_rpn( split( /\s+/, $str ) );
	print "rpn: $str\n     result: $n\n";
}
