Task

Create a stack-based evaluator for an expression in   reverse Polish notation (RPN)   that also shows the changes in the stack as each individual token is processed as a table.



 Assume an input of a correct, space separated, string of tokens of an RPN expression
 Test with the RPN expression generated from the   Parsing/Shunting-yard algorithm   task: 

         3 4 2 * 1 5 - 2 3 ^ ^ / + 

 Print or display the output here


Notes

    ^    means exponentiation in the expression above.
    /    means division.


See also

   Parsing/Shunting-yard algorithm for a method of generating an RPN from an infix expression.
   Several solutions to 24 game/Solve make use of RPN evaluators (although tracing how they work is not a part of that task).
   Parsing/RPN to infix conversion.
   Arithmetic evaluation.


 
function Invoke-Rpn
{
  <#
    .SYNOPSIS
        A stack-based evaluator for an expression in reverse Polish notation.
    .DESCRIPTION
        A stack-based evaluator for an expression in reverse Polish notation.
 
        All methods in the Math and Decimal classes are available.
    .PARAMETER Expression
        A space separated, string of tokens.
    .PARAMETER DisplayState
        This switch shows the changes in the stack as each individual token is processed as a table.
    .EXAMPLE
        Invoke-Rpn -Expression "3 4 Max"
    .EXAMPLE
        Invoke-Rpn -Expression "3 4 Log2"
    .EXAMPLE
        Invoke-Rpn -Expression "3 4 2 * 1 5 - 2 3 ^ ^ / +"
    .EXAMPLE
        Invoke-Rpn -Expression "3 4 2 * 1 5 - 2 3 ^ ^ / +" -DisplayState
#>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true)]
        [AllowEmptyString()]
        [string]
        $Expression,
 
        [Parameter(Mandatory=$false)]
        [switch]
        $DisplayState
    )
    Begin
    {
        function Out-State ([System.Collections.Stack]$Stack)
        {
            $array = $Stack.ToArray()
            [Array]::Reverse($array)
            $array | ForEach-Object -Process { Write-Host ("{0,-8:F3}" -f $_) -NoNewline } -End { Write-Host }
        }
 
        function New-RpnEvaluation
        {
            $stack = New-Object -Type System.Collections.Stack
 
            $shortcuts = @{
                "+" = "Add"; "-" = "Subtract"; "/" = "Divide"; "*" = "Multiply"; "%" = "Remainder"; "^" = "Pow"
            }
 
            :ARGUMENT_LOOP foreach ($argument in $args)
            {
                if ($DisplayState -and $stack.Count)
                {
                    Out-State $stack
                }
 
                if ($shortcuts[$argument])
                {
                    $argument = $shortcuts[$argument]
                }
 
                try
                {
                    $stack.Push([decimal]$argument)
                    continue
                }
                catch
                {
                }
 
                $argCountList = $argument -replace "(\D+)(\d*)",‘$2’
                $operation = $argument.Substring(0, $argument.Length – $argCountList.Length)
 
                foreach($type in [Decimal],[Math])
                {
                    if ($definition = $type::$operation)
                    {
                        if (-not $argCountList)
                        {
                            $argCountList = $definition.OverloadDefinitions |
                                Foreach-Object { ($_ -split ", ").Count } |
                                Sort-Object -Unique
                        }
 
                        foreach ($argCount in $argCountList)
                        {
                            try
                            {
                                $methodArguments = $stack.ToArray()[($argCount–1)..0]
                                $result = $type::$operation.Invoke($methodArguments)
 
                                $null = 1..$argCount | Foreach-Object { $stack.Pop() }
 
                                $stack.Push($result)
 
                                continue ARGUMENT_LOOP
                            }
                            catch
                            {
                                ## If error, try with the next number of arguments
                            }
                        }
                    }
                }
            }
 
            if ($DisplayState -and $stack.Count)
            {
                Out-State $stack
                if ($stack.Count)
                {
                    Write-Host "`nResult = $($stack.Peek())"
                }
            }
            else
            {
                $stack
            }
        }
    }
    Process
    {
        Invoke-Expression -Command "New-RpnEvaluation $Expression"
    }
    End
    {
    }
}
 
Invoke-Rpn -Expression "3 4 2 * 1 5 - 2 3 ^ ^ / +" -DisplayState
 