Learn about the PowerShell Abstract Syntax Tree (AST) – Part 2

In my previous blog article a few weeks ago on Learning about the PowerShell Abstract Syntax Tree (AST), I mentioned there was an easier way to retrieve the AST so that you didn't have to cast everything to a script block. There are two .NET static methods, ParseFile and ParseInput, that are part of the Parser Class in the System.Management.Automation.Language namespace which can be used to retrieve the AST.

First, I’ll store the content of one of my functions in a variable.

1$Code = Get-Content -Path U:\GitHub\Hyper-V\MrHyperV\public\Get-MrVmHost.ps1 -Raw

part2-ast1b.jpg

The ParseInput static method can be used with the content of the function from the previous example to retrieve things like the function itself without the Requires statement (no Regular Expression needed).

1$Tokens = $null
2$Errors = $null
3[System.Management.Automation.Language.Parser]::ParseInput($Code, [ref]$Tokens, [ref]$Errors)

part2-ast2b.jpg

Due to the way that .NET works, you'll first need to define the variable used for Tokens and Errors otherwise an error will be generated.

1[System.Management.Automation.Language.Parser]::ParseInput($Code, [ref]$Tokens, [ref]$Errors)

part2-ast3b.jpg

1[ref] cannot be applied to a variable that does not exist.
2At line:1 char:1
3+ [System.Management.Automation.Language.Parser]::ParseInput($Code, [re 
4+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5+ CategoryInfo : InvalidOperation: (Tokens:VariablePath) [], RuntimeException
6+ FullyQualifiedErrorId : NonExistingVariableReference

If you don't need the information contained in them, instead of setting a variable to $null and then using it, you can reduce the number of steps necessary by simply using $null for them when calling the .NET method.

1[System.Management.Automation.Language.Parser]::ParseInput($Code, [ref]$null, [ref]$null)

part2-ast4b.jpg

One last tip before moving onto the ParseFile static method. If you're going to use the Get-Content cmdlet to retrieve the code for working with ParseInput, be sure to add its Raw parameter otherwise the formatting of the code in the results will be skewed as shown in the following example.

1$Code = Get-Content -Path U:\GitHub\Hyper-V\MrHyperV\public\Get-MrVmHost.ps1
2[System.Management.Automation.Language.Parser]::ParseInput($Code, [ref]$null, [ref]$null)

part2-ast5a.jpg

If you're going to retrieve all of the content from a file similarly to what I've shown so far in this blog article, save yourself a step or two by simply using the ParseFile static method instead of ParseInput.

1$FilePath = 'U:\GitHub\Hyper-V\MrHyperV\public\Get-MrVmHost.ps1'
2[System.Management.Automation.Language.Parser]::ParseFile($FilePath, [ref]$null, [ref]$null)

part2-ast6a.jpg

Consider storing the results in a variable while prototyping with this or any command. Remember that any command that produces object based output can be piped to Get-Member to determine its methods and properties.

1$Results = [System.Management.Automation.Language.Parser]::ParseFile($FilePath, [ref]$null, [ref]$null)
2$Results | Get-Member

part2-ast7a.jpg

Note that the System.Management.Automation.Language namespace only exists in Windows PowerShell version 3.0 or higher, although that shouldn't be a problem since Windows PowerShell version 2.0 is deprecated. All examples shown in this blog article were tested on Windows 10 Enterprise Edition version 1809 with both Windows PowerShell 5.1 and PowerShell Core 6.1 (PowerShell Core installs side by side on Windows based systems).

The examples shown in this blog article only return the top level AST. In Part 3 of this blog article series, I'll dive into searching for AST instances recursively.

µ