{"id":66584,"date":"2016-08-02T15:21:46","date_gmt":"2016-08-02T15:21:46","guid":{"rendered":"https:\/\/www.simple-talk.com\/?p=66584"},"modified":"2016-09-21T17:04:15","modified_gmt":"2016-09-21T17:04:15","slug":"creating-powershell-stacks-expression-evaluaters","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/creating-powershell-stacks-expression-evaluaters\/","title":{"rendered":"Creating PowerShell Stacks and Expression Evaluators"},"content":{"rendered":"<p>PowerShell has a surprising number of different ways of creating objects that have data and methods. Although it is only recently that PowerShell has supported objects directly by means of classes, dynamic modules are a great way of getting the same result. It enables the programmer to create type-constrained data structures that have methods. The problem has been that they don\u2019t seem that easy to understand, and in order to explain them in a way that makes sense, it really needs something convincing and practical by way of an example; what is needed is a demonstration of when a dynamic module would be useful.<\/p>\n<p>One use that seems obvious to me is the stack, also known as the \u2018LIFO\u2019 buffer, which is an abstract data structure that is like a stack of plates in a canteen. It has some classic methods, &#8216;pop()&#8217; where you grab a plate, &#8216;push()&#8217; where you put a plate on the stack, &#8216;peek()&#8217; where you see what plate is on the top of the stack and &#8216;count()&#8217; where you find out how many plates there are. You may need a &#8216;clear()&#8217; method too. When you\u2019re debugging the workings of a stack, it is nice to be able to see the data.<\/p>\n<p>I use stacks for writing expression analysers. Generally I like at least two stacks, probably up to four. They tend to be different sizes and may have other differences. If written as objects, the code becomes much cleaner and easier to read. Why do I write expression analysers? You might imagine that you would never need such a thing, but once you have one, reasons for using it just keep appearing. They are handy for parsing document-based hierarchical data structures, for parsing grammars and for creating Domain-Specific Languages (DSLs). A DSL is handy when you&#8217;re writing a complex application because it cuts down dependencies, and allows you to develop scripts for complex workflows without recompiling .<\/p>\n<p>What I describe here is a cut-down version of what I use, just to illustrate the way of creating and using stacks. I extend this basic algorithm, originally from Dijkstra&#8217;s shunting algorithm, into a complete language interpreter. All it needs is a third stack to implement block iterations and &#8216;while&#8217; loops. Why not use PowerShell itself as your DSL? I&#8217;ve tried that, but my experience is that it is pretty-well impossible to eliminate script-injection attacks, and I also find that there isn\u2019t quite enough control.<\/p>\n<p>You\u2019ll notice that the function has dynamic modules that are created as custom objects, one for each stack we need. (the return stack isn\u2019t necessary at this stage, and you may also need an iteration stack too)<\/p>\n<pre class=\"theme:powershell-ise left-set:true right-set:true lang:ps decode:true \" title=\"Creating a dynamic module\">$ValueStack = New-Module &lt;code&gt;-ScriptBlock&lt;\/code&gt; @TheStackCode -argumentlist 40 -name 'ValueStack' -AsCustomObject\r\n$FunctionStack = New-Module -ScriptBlock @TheStackCode -argumentlist 20 -name 'FunctionStack' -AsCustomObject\r\n<\/pre>\n<p>The initialisation code that we passed as a scriptblock named @TheStackCode is called with whatever arguments you need. In our case we just need to specify the height of the stack that we require: (-argumentlist 20).<\/p>\n<p>The value stack is saved in a variable for use later. You\u2019ll see that each function has become a method (a ScriptMethod) so that this will work \u2026<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">   $ValueStack.Push('one')\r\n    $ValueStack.Push('two')\r\n    $ValueStack.Push('three')\r\n    $ValueStack.Push('four')\r\n    $valueStack.TheStack\r\n    $ValueStack.Pop()\r\n    $ValueStack.Pop()\r\n    $ValueStack.Pop()\r\n    $ValueStack.Pop()\r\n    $valueStack.TheStack\r\n<\/pre>\n<p>The Push() method pushes the string object supplied as a parameter onto the stack and the Pop() method takes each off in turn. The array that is used to store the stack values can be accessed via the object\u2019s TheStack member (a PowerShell NoteProperty) if required for debugging.<\/p>\n<p>The only other part is the initialisation code.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$theStackcode = {\r\n    #create the scriptblock containing the data and logic for the stack object.\r\n    param ([int]$length)\r\n    \r\n    $stacklength = $length; $Stackpointer = 0;\r\n    $TheStack = @($null) * $stacklength #initialise the stack array\r\n    \r\n    Function Count { $script:stackpointer } #returns the entries on the stack\r\n    Function Pop # pop the entry off the stack\r\n    {\r\n        if ($Script:Stackpointer -gt 0)\r\n        {\r\n            $Script:TheStack[--$Script:Stackpointer];\r\n            $Script:TheStack[$Script:Stackpointer] = $null\r\n        }\r\n        else { $null } #return a null if there are no entries to pop off the stack\r\n    };\r\n    Function Push ($value) #push the value on to the stack\r\n    {\r\n        if ($Script:Stackpointer -lt ($Script:stacklength - 1)) # check for overflow\r\n        { $Script:TheStack[$Script:Stackpointer++] = $value } #add the value\r\n    }\r\n    Function Peek #see what is on the top of the stack without taking it off\r\n    {\r\n        if ($Stackpointer -gt 0) { $TheStack[$Stackpointer] }\r\n        else { $null } #null if there is nothing on the stack\r\n    }\r\n    Function Count #see how long the stack is\r\n    {\r\n        $TheStack.Length\r\n    }\r\n    Export-ModuleMember -Function Pop, Push, Peek, Count\r\n    Export-ModuleMember -Variable TheStack\r\n}  \r\n<\/pre>\n<p>Yes, a lot of magic goes on under the covers to make it so simple, but you have an object that provides information to intellisense and plays well with the Get-Method cmdlet.<\/p>\n<p>So here is the complete code for the expression analyser.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">&lt;#\r\n    .SYNOPSIS\r\n        Simple expression analyser\r\n    \r\n    .DESCRIPTION\r\n        This function was developed to test out a simple PowerShell expression analyser \r\n        that is intended for processing syntax expressions. However, it was easier to test\r\n        doing numeric expressions\r\n    \r\n    .PARAMETER Expression\r\n        The string expression that should be evaluated. This is a mathematical expression\r\n        that currently supports just the basic mathematical operation, just to get the\r\n\r\n    \r\n    .EXAMPLE\r\n        \t\tPS C:\\&gt; Evaluate-Expression -Expression 'pi*(23-4)\/2^2'\r\n    \r\n    .NOTES\r\n    ===========================================================================\r\n\t Created on:   \t07-Jul-16 2:37 PM\r\n\t Created by:   \tPhil Factor\r\n\t Organization: \tSimple-Talk\r\n\t Filename:     \t\r\n\t===========================================================================\r\n#&gt;\r\nfunction Evaluate-Expression\r\n{\r\n    [CmdletBinding()]\r\n    [OutputType([psobject])]\r\n    param\r\n    (\r\n        [string]$Expression #the string you want to evaluate\r\n    )\r\n    $ErrorActionPreference = \"Stop\";\r\n    $theStackcode = { #this is the constructor\r\n        #create the scriptblock containing the data and logic for the stack object.\r\n        param ([int] $length) #you can specify the length of the stack\r\n        $stacklength = $length; $Stackpointer = 0;\r\n        $TheStack = @(0) * $stacklength #initialise the stack array\r\n\r\n        Function Count { $script:stackpointer } #returns the entries on the stack\r\n\r\n        Function Pop # pop the entry off the stack\r\n        {\r\n            if ($Script:Stackpointer -gt 0) { $Script:TheStack[--$Script:Stackpointer] }\r\n            else { $null } #return a null if there are no entries to pop off the stack\r\n        };\r\n\r\n        Function Push ($value) #push the value on to the stack\r\n        {\r\n            if ($Script:Stackpointer -lt ($Script:stacklength - 1)) # check for overflow\r\n\r\n            { $Script:TheStack[$Script:Stackpointer++] = $value } #add the value\r\n        }\r\n\r\n        Function Peek #see what is on the top of the stack without taking it off\r\n        {\r\n            if ($Stackpointer -gt 0) { $TheStack[$Stackpointer-1] }\r\n            else { $null } #null if there is nothing on the stack\r\n        }\r\n        Export-ModuleMember -Function Pop,Push,Peek,Count\r\n        Export-ModuleMember -Variable TheStack\r\n     }\r\n    #now we create the dynamic module as a custom object. We can add noteproperties, \r\n    #scriptproperties\r\n    $ValueStack = New-Module -ScriptBlock @TheStackCode -argumentlist 40 -name 'ValueStack' -AsCustomObject\r\n    $FunctionStack = New-Module -ScriptBlock @TheStackCode -argumentlist 20 -name 'FunctionStack' -AsCustomObject\r\n    \r\n    $Token = @{\r\n        '{' = @{ precedence = 0; type = 'structural'; meaning = 'start' };\r\n        '*' = @{ precedence = 8; type = 'binary'; meaning = 'mul' };\r\n        '%' = @{ precedence = 8; type = 'binary'; meaning = 'mod' };\r\n        '\/' = @{ precedence = 8; type = 'binary'; meaning = 'div' };\r\n        '-' = @{ precedence = 7; type = 'binary'; meaning = 'minus' };\r\n        '+' = @{ precedence = 7; type = 'binary'; meaning = 'plus' };\r\n        '(' = @{ precedence = 5; type = 'structural'; meaning = 'OpenBracket' };;\r\n        ')' = @{ precedence = 1; type = 'structural'; meaning = 'CloseBracket' };\r\n        '^' = @{ precedence = 9; type = 'binary'; meaning = 'Power' };\r\n        '&lt;&lt;' = @{ precedence = 9; type = 'binary'; meaning = 'shl' };\r\n        '&gt;&gt;' = @{ precedence = 9; type = 'binary'; meaning = 'shr' };\r\n        'abs' = @{ precedence = 9; type = 'unary'; meaning = 'abs' };\r\n       '}' = @{ precedence = 1; type = 'structural'; meaning = 'end' };\r\n        \r\n    }\r\n    #\r\n    $FunctionStack.Push('{') #indicate start of expression\r\n    $expression = $expression + '}'\r\n    $i = 0; $state = 'begin'; $unexecutable = $false;\r\n    \r\n    \r\n    #$expression.Substring($i)\r\n    #skip leading spaces\r\n    #if ($expression -eq $null -or $expression.Length -eq 0) {Throw 'no expression'}\r\n    While ($i -lt $expression.Length)\r\n    {\r\n        #take out any leading space characters\r\n        if ($expression.Substring($i) -cmatch '(?m)\\A\\s+') { $i = $i + $matches[0].Length }\r\n        #first pick up your literal (number in our case because we haven't done strings yet!)\r\n        #check for numeric value.If the token is a number, then add it to the value stack\r\n        if (($state -ne 'val') -and ($expression.Substring($i) -cmatch '(?im)^[-+]?(\\d+(?:\\.?\\d*)|\\d*\\.\\d+)'))\r\n        {# OK. we can add it to the value stack and change state (this copes with unary operators)\r\n            $ValueStack.Push($matches[0]); $i = $i + $matches[0].Length; $state = 'val'\r\n        }#we have moved the index $i past the end of the number we've identified.\r\n        elseif ($expression.Substring($i) -cmatch '^(e)|^(pi)') #do any constants\r\n            {\r\n            switch ($matches[0]) #we only do pi and E but you'd also deal with any ones you define\r\n                { \r\n                    'e' {$i=$i+1;$ValueStack.Push([math]::E)} \r\n                    'pi' {$i=$i+2;$ValueStack.Push([math]::PI) } \r\n                    default {$ValueStack.Push($null) }\r\n                }\r\n            }\r\n        #If the token is a function token, then push it onto the stack.\r\n        elseif ($expression.Substring($i) -cmatch '^(\\+)|^(\\-)|^(\\*)|^(\\%)|^(\\\\)|^(\\\/)|^(\\()|^(\\))|^(\\})|^(\\{)|^(\\^)|^(abs)|^(&lt;&lt;)|^(&gt;&gt;)')\r\n        {\r\n            $currentToken = $matches[0]; $i = $i + $matches[0].Length; $state = $Token[$currentToken].type;\r\n            # we have a special treatment for brackets.\r\n            if ($currentToken -ne '(')\r\n            {\r\n                if ($FunctionStack.Peek() -eq $null){throw \"missing '(' bracket in expression\"}\r\n                while (($FunctionStack.Peek() -ne $null) -and $Token[$currentToken].Precedence -lt $Token[$FunctionStack.Peek()].Precedence)\r\n                { #while the precedence of this token is less than the token at the top of the function stack\r\n                    $operator = $FunctionStack.Pop(); #pop the function off the function stack\r\n                    write-verbose \"popped $operator after comparing with $currenttoken\"\r\n                    $rvalue = $ValueStack.Pop(); #get the top value which becomes the right-side value \r\n                    if ($Token[$operator].type -eq 'binary') \r\n                    { #and get the next from the stack if it is a binary op\r\n                        $lvalue = $ValueStack.Pop();\r\n                        if ($lvalue -eq $null) { throw \"syntax error: too many operators, or missing close-bracket\" }\r\n                    }\r\n                    #OK we've bottomed-out a bracketed expression so we can push the value it resolved to \r\n                    if ($operator -eq '(' -and $currentToken -eq ')') { $ValueStack.Push($rvalue); $state = 'val'; break; }\r\n                    if ($Token[$operator].type -eq 'structural') \r\n                      { throw \"syntax error: missing bracket while processing '$operator' with '$currentToken'\" }\r\n                    if ($rvalue -eq $null) { throw \"syntax error: missing value(s)\" }\r\n                    # now we can provide the code for operators without direct powerShell-equivalence\r\n                    if ($token[$operator].meaning -eq 'Power')\r\n                    { $repl = \"[math]::pow($lvalue , $rvalue)\"; $unexecutable = $true }\r\n                    elseif ($token[$operator].meaning -eq 'abs')\r\n                    { $repl = \"[math]::abs($rvalue)\"; $unexecutable = $true }\r\n                    elseif ($token[$operator].meaning -eq 'shr')\r\n                    { $repl = \"$lvalue -shr  $rvalue\"  }\r\n                    elseif ($token[$operator].meaning -eq 'shl')\r\n                    { $repl = \"$lvalue -shl  $rvalue\"  }\r\n                     else #we can do it with a simple repl (not in production!)\r\n                    { $repl = \"$lvalue $operator  $rvalue\" };\r\n                    write-verbose \"executing '$repl' at point of '$currentToken' with '$operator' ($state)\"\r\n                    $result = Invoke-Expression $repl;\r\n                    $ValueStack.Push($result); #and we just push the value onto the stack\r\n                    if ($currentToken -eq ')') { $state = 'val' };\r\n                }\r\n            }\r\n            if ($currentToken -ne ')') { write-verbose \"pushing $currentToken at $i\"; $FunctionStack.Push($currentToken) }\r\n        }\r\n        else\r\n        { throw \"unrecognised token at $($expression.Substring($i))\" }\r\n        #\r\n    }\r\n    $valueStack.Pop()\r\nwrite-verbose \"ValueStack=$($valueStack.count()) Value=$($valueStack.peek()) and FunctionStack=$($functionStack.count()) Function=$($functionStack.peek())\"   \r\nwhile ($functionStack.count() -gt 0)# just to help debugging.\r\n  {write-verbose \"$($functionStack.pop())\"}\r\n\r\n} \r\n<\/pre>\n<p>And I have a simple unit-test on the same page to check out that the basic operations are working (it doesn\u2019t catch all the edge cases!). Why are the simple expressions so much easier to do? This is because wherever I can, I am checking against the same expression in PowerShell. Sometimes the operators are different or don\u2019t exist in PowerShell, which uses the static maths class from .net when it can.<\/p>\n<pre class=\"theme:powershell-ise top-margin:20 bottom-margin:20 left-set:true left-margin:10 lang:ps decode:true\">#and a collection of simple unit tests to catch anything major.\r\nif ([math]::Round((evaluate-expression  'pi*4*4'),2) -ne 50.27) { evaluate-expression  'pi*4*4' -verbose }\r\nif ((evaluate-expression  '1024 &gt;&gt; 2') -ne 256) {evaluate-expression  '1024 &gt;&gt; 2' -verbose}\r\nif ([math]::Round((evaluate-expression  'e*2'),2) -ne 5.44) { evaluate-expression  'e*2' -verbose }\r\nif ((evaluate-expression '2+abs(-345)+8') -ne 355) {evaluate-expression '2+abs(-345)+8' -verbose}\r\nif ([math]::Round((evaluate-expression 'pi*(23-4)\/2^2'),2) -ne 14.92) { evaluate-expression  'e*2' -verbose }\r\nif ((evaluate-expression '3 +  4 * 2 \/  1 - 5)  ^ 2 ^ 3') -ne 1679616) {evaluate-expression '3 +  4 * 2 \/  1 - 5)  ^ 2 ^ 3' -verbose}\r\n @('(1+2)+3','(23+4)*7','456+(2*3)+4','(456+2*3)+4','456+(2*3+4)','(456+2*3+4)','456+(2)*3+4','-456+-4',\r\n   '(9*0)+5*6','45*(8+89)','9%5')|\r\n    foreach-object{if ((evaluate-expression $_) -ne (Invoke-Expression $_)) {\r\n      \"oops! $($_) was calculated as $(evaluate-expression $_ -verbose) but should be $(Invoke-Expression $_)\" \r\n      } \r\n   }\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>To demonstrate that dynamic modules in PowerShell can be used to easily create objects with methods and properties, Phil Factor implements an expression analyser written in PowerShell, using a variation of Dijkstra&#8217;s Shunting Algorithm.&hellip;<\/p>\n","protected":false},"author":154613,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2,35],"tags":[],"coauthors":[6813],"class_list":["post-66584","post","type-post","status-publish","format-standard","hentry","category-blogs","category-powershell"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/66584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/154613"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=66584"}],"version-history":[{"count":12,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/66584\/revisions"}],"predecessor-version":[{"id":68077,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/66584\/revisions\/68077"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=66584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=66584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=66584"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=66584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}