Ast.psm1
[CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Test-ModuleManifest -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [Core] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - Processing folder" #region [functions] - [public] - [Core] - [Get-AstCommand] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-AstCommand] - Importing" function Get-AstCommand { <# .SYNOPSIS Retrieves command Ast (Abstract Syntax Tree) elements from a PowerShell script or Ast object. .DESCRIPTION This function extracts and returns command Ast elements from a specified PowerShell script file, script content, or an existing Ast object. The function supports multiple input methods, including direct script text, file paths, or existing Ast objects. It also provides an option to search nested functions and script block expressions. .EXAMPLE Get-AstCommand -Path "C:\Scripts\MyScript.ps1" Output: ```powershell Ast : {@{Name=Get-Process; Extent=...}, @{Name=Write-Host; Extent=...}} Tokens : {...} Errors : {} ``` Parses the specified script file and extracts command Ast elements. .EXAMPLE Get-AstCommand -Script "Get-Process; Write-Host 'Hello'" Output: ```powershell Ast : {@{Name=Get-Process; Extent=...}, @{Name=Write-Host; Extent=...}} Tokens : {...} Errors : {} ``` Parses the provided script content and extracts command Ast elements. .EXAMPLE $ast = [System.Management.Automation.Language.Parser]::ParseInput("Get-Process", [ref]$null, [ref]$null) Get-AstCommand -Ast $ast Output: ```powershell Ast : {@{Name=Get-Process; Extent=...}} Tokens : {...} Errors : {} ``` Extracts command Ast elements from a manually parsed Ast object. .OUTPUTS PSCustomObject .NOTES Returns an object containing extracted Ast elements, tokens, and errors. .LINK https://psmodule.io/Ast/Functions/Core/Get-AstCommand/ #> [CmdletBinding(DefaultParameterSetName = 'Ast')] param ( # The name of the command to search for. Defaults to all commands ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # An existing Ast object to search. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Ast' )] [System.Management.Automation.Language.Ast] $Ast, # Search nested functions and script block expressions. [Parameter()] [switch] $Recurse ) begin {} process { $scriptAst = @() switch ($PSCmdlet.ParameterSetName) { 'Path' { $scriptAst += (Get-AstScript -Path $Path).Ast } 'Script' { $scriptAst += (Get-AstScript -Script $Script).Ast } 'Ast' { $scriptAst += $Ast } } # Extract function definitions $ast = foreach ($astItem in $scriptAst) { $astItem.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $Recurse) | Where-Object { $_.Name -like $Name } } } end { [pscustomobject]@{ Ast = @($ast) Tokens = $scriptAst.tokens Errors = $scriptAst.errors } } } Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-AstCommand] - Done" #endregion [functions] - [public] - [Core] - [Get-AstCommand] #region [functions] - [public] - [Core] - [Get-AstFunction] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-AstFunction] - Importing" function Get-AstFunction { <# .SYNOPSIS Retrieves function definitions from a PowerShell script or Ast. .DESCRIPTION This function extracts function definitions from a given PowerShell script file, script content, or an existing Ast (Abstract Syntax Tree) object. It supports searching by function name and can optionally search within nested functions and script block expressions. .EXAMPLE Get-AstFunction -Path "C:\Scripts\MyScript.ps1" Output: ```powershell Ast : {FunctionDefinitionAst, FunctionDefinitionAst} Tokens : {...} Errors : {} ``` Retrieves function definitions from the specified script file. .EXAMPLE Get-AstFunction -Script "$scriptContent" Output: ```powershell Ast : {FunctionDefinitionAst} Tokens : {...} Errors : {} ``` Parses and retrieves function definitions from the provided script content. .EXAMPLE $ast = Get-AstScript -Path "C:\Scripts\MyScript.ps1" | Select-Object -ExpandProperty Ast Get-AstFunction -Ast $ast Output: ```powershell Ast : {FunctionDefinitionAst} Tokens : {...} Errors : {} ``` Extracts function definitions from an existing Ast object. .OUTPUTS PSCustomObject .NOTES Contains Ast objects, tokenized script content, and parsing errors if any. .LINK https://psmodule.io/Ast/Functions/Core/Get-AstFunction #> [CmdletBinding(DefaultParameterSetName = 'Ast')] param ( # The name of the function to search for. Defaults to all functions ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # An existing Ast object to search. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Ast' )] [System.Management.Automation.Language.Ast] $Ast, # Search nested functions and script block expressions. [Parameter()] [switch] $Recurse ) begin {} process { $scriptAst = @() switch ($PSCmdlet.ParameterSetName) { 'Path' { $scriptAst += (Get-AstScript -Path $Path).Ast } 'Script' { $scriptAst += (Get-AstScript -Script $Script).Ast } 'Ast' { $scriptAst += $Ast } } # Extract function definitions $ast = foreach ($astItem in $scriptAst) { $astItem.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $Recurse) | Where-Object { $_.Name -like $Name } } } end { [pscustomobject]@{ Ast = @($ast) Tokens = $scriptAst.tokens Errors = $scriptAst.errors } } } Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-AstFunction] - Done" #endregion [functions] - [public] - [Core] - [Get-AstFunction] #region [functions] - [public] - [Core] - [Get-AstScript] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-AstScript] - Importing" function Get-AstScript { <# .SYNOPSIS Parses a PowerShell script or script file and returns its abstract syntax tree (Ast). .DESCRIPTION The Get-AstScript function parses a PowerShell script or script file and returns its abstract syntax tree (Ast), along with tokens and errors encountered during parsing. This function can be used to analyze the structure of a script by specifying either the script content directly or the path to a script file. .EXAMPLE Get-AstScript -Path "C:\\Scripts\\example.ps1" Output: ```powershell Ast : [System.Management.Automation.Language.ScriptBlockAst] Tokens : {Token1, Token2, ...} Errors : {Error1, Error2, ...} ``` Parses the PowerShell script located at "C:\\Scripts\\example.ps1" and returns its Ast, tokens, and any parsing errors. .EXAMPLE Get-AstScript -Script "Write-Host 'Hello World'" Output: ```powershell Ast : [System.Management.Automation.Language.ScriptBlockAst] Tokens : {Token1, Token2, ...} Errors : {} ``` Parses the provided PowerShell script string and returns its Ast, tokens, and any parsing errors. .OUTPUTS PSCustomObject .NOTES The returned custom object contains the following properties: - `Ast` - [System.Management.Automation.Language.ScriptBlockAst]. The abstract syntax tree (Ast) of the parsed script. - `Tokens` - [System.Management.Automation.Language.Token[]]. The tokens generated during parsing. - `Errors` - [System.Management.Automation.Language.ParseError[]]. Any parsing errors encountered. .LINK https://psmodule.io/Ast/Functions/Core/Get-AstScript/ #> [outputType([System.Management.Automation.Language.ScriptBlockAst])] [CmdletBinding()] param ( # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [ValidateScript({ Test-Path -Path $_ })] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script ) begin {} process { $tokens = $null $errors = $null switch ($PSCmdlet.ParameterSetName) { 'Path' { $ast = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$tokens, [ref]$errors) } 'Script' { $ast = [System.Management.Automation.Language.Parser]::ParseInput($Script, [ref]$tokens, [ref]$errors) } } [pscustomobject]@{ Ast = $ast Tokens = $tokens Errors = $errors } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-AstScript] - Done" #endregion [functions] - [public] - [Core] - [Get-AstScript] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - Done" #endregion [functions] - [public] - [Core] #region [functions] - [public] - [Functions] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - Processing folder" #region [functions] - [public] - [Functions] - [Get-AstFunctionAlias] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-AstFunctionAlias] - Importing" function Get-AstFunctionAlias { <# .SYNOPSIS Retrieves function aliases from a PowerShell script or file. .DESCRIPTION This function parses a PowerShell script or file to extract function definitions and identify any aliases assigned to them via the `[Alias()]` attribute. It supports searching by function name and allows recursive searching within nested functions and script blocks. .EXAMPLE Get-AstFunctionAlias -Path "C:\Scripts\MyScript.ps1" -Name "Get-User" Output: ```powershell Name Alias ---- ----- Get-User {RetrieveUser, FetchUser} ``` Retrieves aliases assigned to the function `Get-User` within the specified script file. .EXAMPLE Get-AstFunctionAlias -Script $scriptContent -Recurse Output: ```powershell Name Alias ---- ----- Get-Data {FetchData, RetrieveData} ``` Searches for function aliases within the provided script content, including nested functions. .OUTPUTS PSCustomObject .NOTES An object containing the function name and its associated aliases. .LINK https://psmodule.io/Ast/Functions/Functions/Get-AstFunctionAlias #> [CmdletBinding()] param ( # The name of the command to search for. Defaults to all commands ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [ValidateScript({ Test-Path -Path $_ })] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # Search nested functions and script block expressions. [Parameter()] [switch] $Recurse ) begin {} process { switch ($PSCmdlet.ParameterSetName) { 'Path' { $functionAst = Get-AstFunction -Name $Name -Path $Path -Recurse:$Recurse } 'Script' { $functionAst = Get-AstFunction -Name $Name -Script $Script -Recurse:$Recurse } } # Process each function and extract aliases $functionAst.Ast | ForEach-Object { $funcName = $_.Name $funcAttributes = $_.Body.FindAll({ $args[0] -is [System.Management.Automation.Language.AttributeAst] }, $true) | Where-Object { $_.Parent -is [System.Management.Automation.Language.ParamBlockAst] } $aliasAttr = $funcAttributes | Where-Object { $_.TypeName.Name -eq 'Alias' } if ($aliasAttr) { $aliases = $aliasAttr.PositionalArguments | ForEach-Object { $_.ToString().Trim('"', "'") } [PSCustomObject]@{ Name = $funcName Alias = $aliases } } } | Where-Object { $_.Name -like $Name } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-AstFunctionAlias] - Done" #endregion [functions] - [public] - [Functions] - [Get-AstFunctionAlias] #region [functions] - [public] - [Functions] - [Get-AstFunctionName] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-AstFunctionName] - Importing" function Get-AstFunctionName { <# .SYNOPSIS Retrieves the names of functions from an abstract syntax tree (Ast) in a PowerShell script. .DESCRIPTION Parses a PowerShell script file or script content to extract function names using an abstract syntax tree (Ast). The function supports searching by name, parsing from a file path, or directly from a script string. It can also search within nested functions and script block expressions when the -Recurse switch is used. .EXAMPLE Get-AstFunctionName -Path "C:\Scripts\example.ps1" Output: ```powershell Get-Data Set-Configuration ``` Extracts function names from the specified PowerShell script file. .EXAMPLE Get-AstFunctionName -Script "function Test-Function { param($x) Write-Host $x }" Output: ```powershell Test-Function ``` Extracts function names from the given script string. .EXAMPLE Get-AstFunctionName -Path "C:\Scripts\example.ps1" -Recurse Output: ```powershell Get-Data Set-Configuration Helper-Function ``` Extracts function names from the specified script file, including nested functions. .OUTPUTS System.String .NOTES The name of each function found in the PowerShell script. .LINK https://psmodule.io/Ast/Functions/Functions/Get-AstFunctionName/ #> [CmdletBinding()] param ( # The name of the command to search for. Defaults to all commands ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [ValidateScript({ Test-Path -Path $_ })] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # Search nested functions and script block expressions. [Parameter()] [switch] $Recurse ) begin {} process { switch ($PSCmdlet.ParameterSetName) { 'Path' { $functionAst = Get-AstFunction -Name $Name -Path $Path -Recurse:$Recurse } 'Script' { $functionAst = Get-AstFunction -Name $Name -Script $Script -Recurse:$Recurse } } # Process each function and extract the name $functionAst.Ast | ForEach-Object { $_.Name } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-AstFunctionName] - Done" #endregion [functions] - [public] - [Functions] - [Get-AstFunctionName] #region [functions] - [public] - [Functions] - [Get-AstFunctionType] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-AstFunctionType] - Importing" function Get-AstFunctionType { <# .SYNOPSIS Retrieves the type of an abstract syntax tree (Ast) function. .DESCRIPTION Parses a PowerShell script file or script content to determine the type of function present. The function classifies functions as `Function`, `Filter`, `Workflow`, or `Configuration`. It supports searching for specific function names and can process nested functions if required. .EXAMPLE Get-AstFunctionType -Path "C:\Scripts\MyScript.ps1" Output: ```powershell Name Type ---- ---- Test1 Function Test2 Filter ``` Parses the specified script file and identifies function types. .EXAMPLE Get-AstFunctionType -Script "function Test { param() Write-Output 'Hello' }" Output: ```powershell Name Type ---- ---- Test Function ``` Parses the provided script content and determines the function type. .OUTPUTS PSCustomObject .NOTES Represents the function name and its determined type. .LINK https://psmodule.io/Ast/Functions/Functions/Get-AstFunctionType/ #> [OutputType([pscustomobject])] [CmdletBinding()] param( # The name of the command to search for. Defaults to all commands ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [ValidateScript({ Test-Path -Path $_ })] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # Search nested functions and script block expressions. [Parameter()] [switch] $Recurse ) begin {} process { switch ($PSCmdlet.ParameterSetName) { 'Path' { $functionAst = Get-AstFunction -Name $Name -Path $Path -Recurse:$Recurse } 'Script' { $functionAst = Get-AstFunction -Name $Name -Script $Script -Recurse:$Recurse } } $functionAst.Ast | ForEach-Object { $type = if ($_.IsWorkflow) { 'Workflow' } elseif ($_.IsConfiguration) { 'Configuration' } elseif ($_.IsFilter) { 'Filter' } else { 'Function' } [pscustomobject]@{ Name = $_.Name Type = $type } } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-AstFunctionType] - Done" #endregion [functions] - [public] - [Functions] - [Get-AstFunctionType] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - Done" #endregion [functions] - [public] - [Functions] #region [functions] - [public] - [Lines] Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - Processing folder" #region [functions] - [public] - [Lines] - [Get-AstLineComment] Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - [Get-AstLineComment] - Importing" filter Get-AstLineComment { <# .SYNOPSIS Extracts comment tokens from a given line of PowerShell code. .DESCRIPTION This function parses a given line of PowerShell code and extracts comment tokens. It utilizes the PowerShell parser to analyze the input and return tokens that match the specified kind, defaulting to 'Comment'. .EXAMPLE "# This is a comment" | Get-AstLineComment Output: ```powershell Kind : Comment Text : # This is a comment ``` Extracts the comment token from the input PowerShell line. .OUTPUTS System.Management.Automation.Language.Token[] .NOTES An array of tokens representing comments extracted from the input line. .LINK https://psmodule.io/Ast/Functions/Lines/Get-AstLineComment/ #> [OutputType([System.Management.Automation.Language.Token[]])] [CmdletBinding()] param ( # Input line of PowerShell code from which to extract the comment. [Parameter( Mandatory, ValueFromPipeline )] [string] $Line , # The type of comment to extract. [Parameter()] [string] $Kind = 'Comment' ) # Parse the line using the PowerShell parser to obtain its tokens. $tokens = $null $null = [System.Management.Automation.Language.Parser]::ParseInput($Line, [ref]$tokens, [ref]$null) # Find comment token(s) in the line. ($tokens | Where-Object { $_.Kind -eq $Kind }) } Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - [Get-AstLineComment] - Done" #endregion [functions] - [public] - [Lines] - [Get-AstLineComment] Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - Done" #endregion [functions] - [public] - [Lines] #region [functions] - [public] - [Scripts] Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - Processing folder" #region [functions] - [public] - [Scripts] - [Get-AstScriptCommand] Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - [Get-AstScriptCommand] - Importing" function Get-AstScriptCommand { <# .SYNOPSIS Retrieves the commands used within a specified PowerShell script. .DESCRIPTION Analyzes a given PowerShell script and extracts all command invocations. Optionally includes call operators (& and .) in the results. Returns details such as command name, position, and file reference. .EXAMPLE Get-AstScriptCommand -Path "C:\Scripts\example.ps1" Extracts and lists all commands found in the specified PowerShell script. .EXAMPLE Get-AstScriptCommand -Path "C:\Scripts\example.ps1" -IncludeCallOperators Extracts all commands, including those executed with call operators (& and .). .LINK https://psmodule.io/Ast/Functions/Scripts/Get-AstScriptCommand/ #> [CmdletBinding(DefaultParameterSetName = 'Ast')] param ( # The name of the function to search for. Defaults to all functions ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. # Validate using Test-Path [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # An existing Ast object to search. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Ast' )] [System.Management.Automation.Language.Ast] $Ast, # Search nested functions and script block expressions. [Parameter()] [switch] $Recurse, # Include call operators in the results, i.e. & and . [Parameter()] [switch] $IncludeCallOperators ) begin {} process { $scriptAst = @() switch ($PSCmdlet.ParameterSetName) { 'Path' { $scriptAst += (Get-AstScript -Path $Path).Ast } 'Script' { $scriptAst += (Get-AstScript -Script $Script).Ast } 'Ast' { $scriptAst += $Ast } } # Gather CommandAsts $commandAst = $scriptAst.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $Recurse) if (-not $IncludeCallOperators) { $commandAst = $commandAst | Where-Object { $_.InvocationOperator -notin 'Ampersand', 'Dot' } } $commandAst | ForEach-Object { $invocationOperator = switch ($_.InvocationOperator) { 'Ampersand' { '&' } 'Dot' { '.' } } $_.CommandElements[0].Extent | Where-Object { $_.Text -like $Name } | ForEach-Object { [pscustomobject]@{ Name = [string]::IsNullOrEmpty($invocationOperator) ? $_.Text : $invocationOperator StartLineNumber = $_.StartLineNumber StartColumnNumber = $_.StartColumnNumber EndLineNumber = $_.EndLineNumber EndColumnNumber = $_.EndColumnNumber File = $_.File } } } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - [Get-AstScriptCommand] - Done" #endregion [functions] - [public] - [Scripts] - [Get-AstScriptCommand] Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - Done" #endregion [functions] - [public] - [Scripts] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Get-AstCommand' 'Get-AstFunction' 'Get-AstScript' 'Get-AstFunctionAlias' 'Get-AstFunctionName' 'Get-AstFunctionType' 'Get-AstLineComment' 'Get-AstScriptCommand' ) Variable = '' } Export-ModuleMember @exports #endregion Member exporter |