133 lines
3.9 KiB
Plaintext
133 lines
3.9 KiB
Plaintext
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
|