PipeScript.types.ps1xml
<?xml version="1.0" encoding="utf-16"?> <!-- Generated with EZOut 1.9.7: Install-Module EZOut or https://github.com/StartAutomating/EZOut --> <Types> <Type> <Name>System.Management.Automation.Language.Ast</Name> <Members> <ScriptMethod> <Name>ConvertFromAST</Name> <Script> param() return $this </Script> </ScriptMethod> <ScriptMethod> <Name>GetLineage</Name> <Script> $thisParent = $this.Parent while ($thisParent) { $thisParent $thisParent = $thisParent.Parent } </Script> </ScriptMethod> <ScriptMethod> <Name>Transpile</Name> <Script> [ScriptBlock]::Create( "$this" ) | .>PipeScript </Script> </ScriptMethod> <ScriptProperty> <Name>Tokens</Name> <GetScriptBlock> $text = $this.Extent.ToString() $previousToken = $null $tokenCount = 0 @(foreach ($token in [Management.Automation.PSParser]::Tokenize($text, [ref]$null)) { Add-Member NoteProperty Text $text -Force -InputObject $token Add-Member NoteProperty PreviousToken $previousToken -Force -InputObject $token if ($token.Type -in 'Variable', 'String') { $realContent = $text.Substring($token.Start, $token.Length) Add-Member NoteProperty Content $realContent -Force -InputObject $token } $previousToken = $token $tokenCount++ $token }) </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>Transpilers</Name> <GetScriptBlock> $scriptText = $this.Extent.ToString() # If the ScriptBlock had attributes, we'll add them to a special list of things that will be transpiled first. # Find all AST elements within the script block. $astList = @($this.FindAll({$true}, $false)) # At various points within transpilation, we will be skipping processing until a known end pointer. For now, set this to null. $skipUntil = 0 # Keep track of the offset from a starting position as well, for the same reason. $myOffset = 0 # Walk over each item in the abstract syntax tree. :NextAstItem foreach ($item in $astList) { # If skipUntil was set, if ($skipUntil) { # find the space between now and the last known offset. try { $newOffset = $scriptText.IndexOf($item.Extent.Text, $myOffset) if ($newOffset -eq -1) { continue } $myOffset = $newOffset } catch { $ex =$_ $null = $null } if ($myOffset -lt $skipUntil) { # If this is before our skipUntil point continue # ignore this AST element. } $skipUntil = $null # If we have reached our skipUntil point, let's stop skipping. } # otherwise, find if any pipescripts match this AST $foundTranspilers = Get-Transpiler -CouldPipe $item -ValidateInput $item if ($foundTranspilers) { foreach ($transpiler in $foundTranspilers) { [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Transpiler.Location' Transpiler = if ($Transpiler.ExtensionInputObject.ResolvedCommand) { @($Transpiler.ExtensionInputObject.ResolvedCommand) -ne $null } else { $Transpiler.ExtensionCommand } AST = $item } } $start = $scriptText.IndexOf($item.Extent.Text, $myOffset) # determine the end of this AST element $end = $start + $item.Extent.Text.Length $skipUntil = $end # set SkipUntil } } </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>System.Management.Automation.Language.AttributeAst</Name> <Members> <AliasProperty> <Name>Args</Name> <ReferencedMemberName>ArgumentList</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>Arguments</Name> <ReferencedMemberName>ArgumentList</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>Parameters</Name> <ReferencedMemberName>Parameter</ReferencedMemberName> </AliasProperty> <ScriptProperty> <Name>ArgumentList</Name> <GetScriptBlock> $Parameter = [Ordered]@{} $ArgumentList = @() # Collect all of the arguments of the attribute, in the order they were specified. $argsInOrder = @( @($this.PositionalArguments) + @($this.NamedArguments) | Sort-Object { $_.Extent.StartOffset} ) # Now we need to map each of those arguments into either named or positional arguments. foreach ($attributeArg in $argsInOrder) { # Named arguments are fairly straightforward: if ($attributeArg -is [Management.Automation.Language.NamedAttributeArgumentAst]) { $argName = $attributeArg.ArgumentName $argAst = $attributeArg.Argument $parameter[$argName] = if ($argName -eq $argAst) { # If the argument is the name, $true # treat it as a [switch] parameter. } # If the argument value was an ScriptBlockExpression else { $argAst } } else { # If we are a positional parameter, for the moment: if ($parameter.Count) { # add it to the last named parameter. $parameter[@($parameter.Keys)[-1]] = @() + $parameter[@($parameter.Keys)[-1]] + $argAst } else { # Or add it to the list of string arguments. $ArgumentList += $attributeArg.ConvertFromAst() } } } return $ArgumentList </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>Parameter</Name> <GetScriptBlock> $Parameter = [Ordered]@{} # Collect all of the arguments of the attribute, in the order they were specified. $argsInOrder = @( @($this.PositionalArguments) + @($this.NamedArguments) | Sort-Object { $_.Extent.StartOffset} ) # Now we need to map each of those arguments into either named or positional arguments. foreach ($attributeArg in $argsInOrder) { # Named arguments are fairly straightforward: if ($attributeArg -is [Management.Automation.Language.NamedAttributeArgumentAst]) { $argName = $attributeArg.ArgumentName $argAst = $attributeArg.Argument $parameter[$argName] = if ($argName -eq $argAst) { # If the argument is the name, $true # treat it as a [switch] parameter. } # If the argument value was an ScriptBlockExpression else { $argAst.ConvertFromAst() } } else { # If we are a positional parameter, for the moment: if ($parameter.Count) { # add it to the last named parameter. $parameter[@($parameter.Keys)[-1]] = @() + $parameter[@($parameter.Keys)[-1]] + $attributeArg.ConvertFromAst() } } } return $Parameter </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>ResolvedCommand</Name> <GetScriptBlock> <# .SYNOPSIS Resolves an Attribute to a CommandInfo .DESCRIPTION Resolves an Attribute to one or more CommandInfo. .EXAMPLE { [InvokePipeScript()]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [Microsoft.PowerShell.Core.GetCommand()]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [Get_Command()]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [GetCommand()]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [cmd()]$null }.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand #> # Get the name of the transpiler. $transpilerStepName = if ($this.TypeName.IsGeneric) { $this.TypeName.TypeName.Name } else { $this.TypeName.Name } $decamelCase = [Regex]::new('(?<=[a-z])(?=[A-Z])') @( # If a Transpiler exists by that name, it will be returned first. Get-Transpiler -TranspilerName $transpilerStepName # Then, any periods in the attribute name will be converted to slashes, $fullCommandName = $transpilerStepName -replace '\.','\' -replace '_','-' # and any underscores to dashes. # Then, the first CamelCased code will have a - injected in between the CamelCase. $fullCommandName = $decamelCase.Replace($fullCommandName, '-', 1) # Now we will try to find the command. $ExecutionContext.SessionState.InvokeCommand.GetCommand($fullCommandName, 'All') ) </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>System.Management.Automation.Language.CommandAst</Name> <Members> <AliasProperty> <Name>Args</Name> <ReferencedMemberName>ArgumentList</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>Arguments</Name> <ReferencedMemberName>ArgumentList</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>Parameters</Name> <ReferencedMemberName>Parameter</ReferencedMemberName> </AliasProperty> <ScriptMethod> <Name>AsSentence</Name> <Script> <# .SYNOPSIS Maps Natural Language Syntax to PowerShell Parameters .DESCRIPTION Maps a statement in natural language syntax to a set of PowerShell parameters. All parameters will be collected. For the purposes of natural language processing ValueFromPipeline will be ignored. The order the parameters is declared takes precedence over Position attributes. #> param() # Because we want to have flexible open-ended arguments here, we do not hard-code any arguments. # we parse them. $IsRightToLeft = $false $SpecificCommands = @() $specificCommandNames = @() for ($argIndex =0 ; $argIndex -lt $args.Count; $argIndex++) { $arg = $args[$argIndex] if ($arg -is [Management.Automation.CommandInfo]) { $SpecificCommands += $arg $specificCommandNames += $arg.Name continue } if ($arg -match '^[-/]{0,2}(?:Is)?RightToLeft$') { # If -RightToLeft was passed $IsRightToLeft = $true continue } $argCommands = @( $foundTranspiler = Get-Transpiler -TranspilerName $arg if ($foundTranspiler) { foreach ($transpiler in $foundTranspiler) { if ($transpiler.Validate($arg)) { $transpiler } } } else { $ExecutionContext.SessionState.InvokeCommand.GetCommands($arg, 'All', $true) } ) if ($argCommands) { $SpecificCommands += $argCommands continue } } $mappedParameters = [Ordered]@{} $sentence = [Ordered]@{ PSTypeName='PipeScript.Sentence' Command = $null } $commandAst = $this $commandElements = @($commandAst.CommandElements) # If we are going right to left, reverse the command elements if ($IsRightToLeft) { [Array]::Reverse($commandElements) } if ($SpecificCommands) { $potentialCommandNames = $specificCommandNames $potentialCommands = $SpecificCommands } else { # The first command element should be the name of the command. $firstCommandElement = $commandElements[0] $commandName = '' $potentialCommandNames = @() $potentialCommands = @( if ($firstCommandElement.Value -and $firstCommandElement.StringConstantType -eq 'BareWord') { $commandName = $firstCommandElement.Value $foundTranspiler = Get-Transpiler -TranspilerName $commandName if ($foundTranspiler) { foreach ($transpiler in $foundTranspiler) { if ($transpiler.Validate($commandAst)) { $potentialCommandNames += $commandName $transpiler } } } else { foreach ($foundCmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands($commandName, 'All', $true)) { $foundCmd $potentialCommandNames += $commandName } } }) if (-not $potentialCommands) { [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Sentence' Keyword = '' Command = $null Arguments = $commandElements[0..$commandElements.Length] } } } $mappedParameters = [Ordered]@{} if (-not $Script:SentenceWordCache) { $Script:SentenceWordCache = @{} } $potentialCommandIndex = -1 foreach ($potentialCommand in $potentialCommands) { $potentialCommandIndex++ $commandName = $potentialCommandName = $potentialCommandNames[$potentialCommandIndex] <# Each potential command can be thought of as a simple sentence with (mostly) natural syntax command <parametername> ...<parameterargument> (etc) either more natural or PowerShell syntax should be allowed, for example: all functions can Quack { "quack" } would map to the command all and the parameters -Function and -Can (with the arguments Quack and {"quack"}) Assuming -Functions was a `[switch]` or an alias to a `[switch]`, it will match that `[switch]` and only that switch. If -Functions was not a `[switch]`, it will match values from that point. If the parameter type is not a list or PSObject, only the next parameter will be matched. If the parameter type *is* a list or an PSObject, or ValueFromRemainingArguments is present and no named parameters were found, then all remaining arguments will be matched until the next named parameter is found. _Aliasing is important_ when working with a given parameter. The alias, _not_ the parameter name, will be what is mapped. #> # Cache the potential parameters $potentialParameters = $potentialCommand.Parameters # Assume the current parameter is empty, $currentParameter = '' # the current parameter metadata is null, $currentParameterMetadata = $null # there is no current clause, $currentClause = @() # and there are no unbound parameters. $unboundParameters = @() $clauses = @() # Walk over each command element in a for loop (we may adjust the index when we match) for ($commandElementIndex = 1 ;$commandElementIndex -lt $commandElements.Count; $commandElementIndex++) { $commandElement = $CommandElements[$commandElementIndex] # by default, we assume we haven't found a parameter. $parameterFound = $false $barewordSequenece = @(for ($cei = $commandElementIndex; $cei -lt $commandElements.Count; $cei++) { if ( $commandElements[$cei] -isnot [Management.Automation.Language.StringConstantExpressionAst] -or $commandElements[$cei].StringConstantType -ne 'Bareword' ) { break } $commandElements[$cei].Value }) # That assumption is quickly challenged if the AST type was CommandParameter if ($commandElement -is [Management.Automation.Language.CommandParameterAst]) { # If there were already clauses, finalize them before we start this clause if ($currentClause) { $clauses += [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Sentence.Clause' Name = if ($currentParameter) { $currentParameter} else { '' } ParameterName = if ($currentParameterMetadata) { $currentParameterMetadata.Name } else { '' } Words = $currentClause } } $commandParameter = $commandElement # In that case, we know the name they want to use for the parameter $currentParameter = $commandParameter.ParameterName $currentClause = @($currentParameter) # We need to get the parameter metadata as well. $currentParameterMetadata = # If it was the real name of a parameter, this is easy if ($potentialCommand.Parameters[$currentParameter]) { $potentialCommand.Parameters[$currentParameter] $parameterFound = $true } else { # Otherwise, we need to search each parameter for aliases. foreach ($cmdParam in $potentialCommand.Parameters.Values) { if ($cmdParam.Aliases -contains $currentParameter) { $parameterFound = $true $cmdParam break } } } # If the parameter had an argument if ($commandParameter.Argument) { # Use that argument if ($mappedParameters[$currentParameter]) { $mappedParameters[$currentParameter] = @($mappedParameters[$currentParameter]) + @( $commandParameter.Argument ) } else { $mappedParameters[$currentParameter] = $commandParameter.Argument } # and move onto the next element. $clauses += [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Sentence.Clause' Name = if ($currentParameter) { $currentParameter} else { '' } ParameterName = if ($currentParameterMetadata) { $currentParameterMetadata.Name } else { '' } Words = $currentClause } $currentParameter = '' $currentParameterMetadata = $null $currentClause = @() continue } # Since we have found a parameter, we advance the index. $commandElementIndex++ } # If the command element was a bareword, it could also be the name of a parameter elseif ($barewordSequenece) { # We need to know the name of the parameter as it was written. # However, we also want to allow --parameters and /parameters, $potentialParameterName = $barewordSequenece[0] # therefore, we will compare against the potential name without leading dashes or slashes. $potentialBarewordList =@( for ( $barewordSequenceIndex = $barewordSequenece.Length; $barewordSequenceIndex -ge 0; $barewordSequenceIndex-- ) { $barewordSequenece[0..$barewordSequenceIndex] -join ' ' -replace '^[-/]{0,}' } ) $dashAndSlashlessName = $potentialParameterName -replace '^[-/]{0,}' # If no parameter was found but a parameter has ValueFromRemainingArguments, we will map to that. $valueFromRemainingArgumentsParameter = $null # Walk over each potential parameter in the command foreach ($potentialParameter in $potentialParameters.Values) { $parameterFound = $( # otherwise, we have to check each alias. :nextAlias foreach ($potentialAlias in $potentialParameter.Aliases) { if ($potentialBarewordList -contains $potentialAlias) { $potentialParameterName = $potentialAlias $true break } } # If the parameter name matches, if ($potentialBarewordList -contains $potentialParameter.Name) { $true # we've found it, } else { } ) # If we found the parameter if ($parameterFound) { if ($currentClause) { $clauses += [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Sentence.Clause' Name = if ($currentParameter) { $currentParameter} else { '' } ParameterName = if ($currentParameterMetadata) { $currentParameterMetadata.Name } else { '' } Words = $currentClause } } # keep track of of it and advance the index. $currentParameter = $potentialParameterName $currentParameterMetadata = $potentialParameter if ($currentParameter -match '\s') { $barewordCount = @($currentParameter -split '\s').Length $currentClause = @($commandElements[$commandElementIndex..($commandElementIndex + $barewordCount - 1)]) $commandElementIndex += $barewordCount } else { $commandElementIndex++ $currentClause = @($commandElement) } break } else { # If we did not, check the parameter for .ValueFromRemainingArguments foreach ($attr in $potentialParameter.Attributes) { if ($attr.ValueFromRemainingArguments) { $valueFromRemainingArgumentsParameter = $potentialParameter break } } } } } # If we have our current parameter, but it is a switch, if ($currentParameter -and $currentParameterMetadata.ParameterType -eq [switch]) { $mappedParameters[$currentParameter] = $true # set it if ($currentClause) { $clauses += [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Sentence.Clause' Name = if ($currentParameter) { $currentParameter} else { '' } ParameterName = if ($currentParameterMetadata) { $currentParameterMetadata.Name } else { '' } Words = $currentClause } } $currentParameter = '' # and clear the current parameter. $currentClause = @() $commandElementIndex-- continue } # Refersh our $commandElement, as the index may have changed. $commandElement = $CommandElements[$commandElementIndex] # If we have a ValueFromRemainingArguments but no current parameter mapped if ($valueFromRemainingArgumentsParameter -and -not $currentParameter) { # assume the ValueFromRemainingArguments parameter is the current parameter. $currentParameter = $valueFromRemainingArgumentsParameter.Name $currentParameterMetadata = $valueFromRemainingArgumentsParameter $currentClause = @() } # If we have a current parameter if ($currentParameter) { # Map the current element to this parameter. $mappedParameters[$currentParameter] = if ($mappedParameters[$currentParameter]) { @($mappedParameters[$currentParameter]) + @($commandElement) } else { if ($commandElement.Value) { $commandElement.Value } elseif ($commandElement -is [ScriptBlockExpressionAst]) { [ScriptBlock]::Create($commandElement.Extent.ToString() -replace '^\{' -replace '\}$') } else { $commandElement } } $currentClause += $commandElement } else { # otherwise add the command element to our unbound parameters. $unboundParameters += if ($commandElement.Value) { $commandElement.Value } elseif ($commandElement -is [ScriptBlockExpressionAst]) { [ScriptBlock]::Create($commandElement.Extent.ToString() -replace '^\{' -replace '\}$') } else { $commandElement } $currentClause += $commandElement } } if ($currentClause) { $clauses += [PSCustomObject][Ordered]@{ PSTypeName = 'PipeScript.Sentence.Clause' Name = if ($currentParameter) { $currentParameter} else { '' } ParameterName = if ($currentParameterMetadata) { $currentParameterMetadata.Name } else { '' } Words = $currentClause } } if ($potentialCommand -isnot [Management.Automation.ApplicationInfo] -and @($mappedParameters.Keys) -match '^[-/]') { $keyIndex = -1 :nextParameter foreach ($mappedParamName in @($mappedParameters.Keys)) { $keyIndex++ $dashAndSlashlessName = $mappedParamName -replace '^[-/]{0,}' if ($potentialCommand.Parameters[$mappedParamName]) { continue } else { foreach ($potentialParameter in $potentialCommand.Parameters) { if ($potentialParameter.Aliases -contains $mappedParamName) { continue nextParameter } } $mappedParameters.Insert($keyIndex, $dashAndSlashlessName, $mappedParameters[$mappedParamName]) $mappedParameters.Remove($mappedParamName) } } } $sentence = [PSCustomObject]@{ PSTypeName = 'PipeScript.Sentence' Keyword = $potentialCommandName Command = $potentialCommand Clauses = $clauses Parameters = $mappedParameters Arguments = $unboundParameters } $sentence } </Script> </ScriptMethod> <ScriptProperty> <Name>ArgumentList</Name> <GetScriptBlock> $parameterAstType = [Management.Automation.Language.CommandParameterAst] @( for ( $commandElementIndex = 1 $commandElementIndex -lt $this.CommandElements.Count $commandElementIndex++ ) { $commandElement = $this.CommandElements[$commandElementIndex] $nextElement = $this.CommandElements[$commandElementIndex + 1] if ($commandElement -is $parameterAstType) { if (-not $commandElement.Argument -and $nextElement -and $nextElement -isnot $parameterAstType) { $commandElementIndex++ } } else { $commandElement.ConvertFromAst() } } ) </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>IsAssigned</Name> <GetScriptBlock> $this.Parent.IsAssigned -as [bool] </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>IsPiped</Name> <GetScriptBlock> ($this.Parent -is [Management.Automation.Language.PipelineAst]) -and ($this.Parent.PipelineElements.Count -gt 1) </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>IsPipedFrom</Name> <GetScriptBlock> if ($this.Parent -isnot [Management.Automation.Language.PipelineAst]) { return $false } $this.Parent.PipelineElements.IndexOf($this) -lt ($this.Parent.PipelineElements.Count - 1) </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>IsPipedTo</Name> <GetScriptBlock> if ($this.Parent -isnot [Management.Automation.Language.PipelineAst]) { return $false } $this.Parent.PipelineElements.IndexOf($this) -gt 0 </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>Parameter</Name> <GetScriptBlock> $commandAst = $this $NamedParameters = [Ordered]@{} $parameterAstType = [Management.Automation.Language.CommandParameterAst] for ( $commandElementIndex = 1 $commandElementIndex -lt $commandAst.CommandElements.Count $commandElementIndex++ ) { $commandElement = $commandAst.CommandElements[$commandElementIndex] $nextElement = $commandAst.CommandElements[$commandElementIndex + 1] if ($commandElement -is $parameterAstType) { if ($commandElement.Argument) { $NamedParameters[$commandElement.ParameterName] = $commandElement.Argument.ConvertFromAst() } elseif ($nextElement -and $nextElement -isnot $parameterAstType) { $NamedParameters[$commandElement.ParameterName] = $nextElement.ConvertFromAst() $commandElementIndex++ } else { $NamedParameters[$commandElement.ParameterName] = $true } } } $NamedParameters </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>PipelineLength</Name> <GetScriptBlock> if ($this.Parent -isnot [Management.Automation.Language.PipelineAst]) { return $null } $this.Parent.PipelineElements.Count </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>PipelinePosition</Name> <GetScriptBlock> if ($this.Parent -isnot [Management.Automation.Language.PipelineAst]) { return $null } $this.Parent.PipelineElements.IndexOf($this) </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>ResolvedCommand</Name> <GetScriptBlock> $commandName = $this.CommandElements[0].ToString() $foundTranspiler = Get-Transpiler -TranspilerName $commandName if ($foundTranspiler) { foreach ($transpiler in $foundTranspiler) { if ($transpiler.Validate($this)) { $transpiler } } } else { $ExecutionContext.SessionState.InvokeCommand.GetCommands($commandName, 'All', $true) } </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>System.Management.Automation.Language.ConstantExpressionAst</Name> <Members> <ScriptMethod> <Name>ConvertFromAST</Name> <Script> $this.Value </Script> </ScriptMethod> </Members> </Type> <Type> <Name>System.Management.Automation.Language.PipelineAST</Name> <Members> <ScriptProperty> <Name>IsAssigned</Name> <GetScriptBlock> $this.Parent -and $this.Parent.GetType().Name -in 'AssignmentStatementAST', 'HashtableAST' </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>PipeScript</Name> <Members> <ScriptProperty> <Name>PipeScriptType</Name> <GetScriptBlock> if ($this.Source -match '\.psx\.ps1{0,1}$') { "Transpiler" } elseif ($this.Source -match "\.ps1{0,1}\.(?<ext>[^.]+$)") { "SourceGenerator" } elseif (($this.Source -match '\.[^\.\\/]+\.ps1$') -or ($this.Source -match 'build\.ps1$')) { "BuildScript" } elseif ($this.Source) { "PipeScriptFile" } else { "Function" } </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>PipeScript.Sentence</Name> <Members> <AliasProperty> <Name>Argument</Name> <ReferencedMemberName>Arguments</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>ArgumentList</Name> <ReferencedMemberName>Arguments</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>Parameter</Name> <ReferencedMemberName>Parameters</ReferencedMemberName> </AliasProperty> <ScriptMethod> <Name>Run</Name> <Script> if (-not $this.Keyword) { throw "Sentence lacks a keyword" } if (-not $this.Command) { throw "Sentence has no command" } $parameters = $this.Parameters $arguments = $this.Arguments if (-not $parameters -and -not $arguments) { & $this.Command } elseif (-not $arguments) { & $this.Command @parameters } elseif (-not $parameters) { & $this.Command @arguments } else { & $this.Command @arguments @parameters } </Script> </ScriptMethod> </Members> </Type> <Type> <Name>System.Management.Automation.ScriptBlock</Name> <Members> <ScriptMethod> <Name>Transpile</Name> <Script> $TranspilerErrors = @() $TranspilerWarnings = @() $ErrorsAndWarnings = @{ErrorVariable='TranspilerErrors';WarningVariable='TranspilerWarnings'} $this | .>PipeScript @ErrorsAndWarnings if ($TranspilerErrors) { $failedMessage = (@( "$($TranspilerErrors.Count) error(s)" if ($transpilerWarnings) { "$($TranspilerWarnings.Count) warning(s)" } ) -join ',') + (@( foreach ($transpilerError in $TranspilerErrors) { "$($transpilerError | Out-String)" } ) -join [Environment]::Newline) throw $failedMessage } elseif ($TranspilerWarnings) { foreach ($TranspilerWarning in $TranspilerWarnings) { Write-Warning "$TranspilerWarning " } } </Script> </ScriptMethod> <ScriptProperty> <Name>Transpilers</Name> <GetScriptBlock> $this.Ast.Transpilers </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>System.Management.Automation.Language.ScriptBlockExpressionAst</Name> <Members> <ScriptMethod> <Name>ConvertFromAST</Name> <Script> $this.GetScriptBlock() </Script> </ScriptMethod> <ScriptMethod> <Name>GetScriptBlock</Name> <Script> [ScriptBlock]::create($this -replace '^\{' -replace '\}$') </Script> </ScriptMethod> </Members> </Type> <Type> <Name>System.Management.Automation.Language.ScriptBlockAst</Name> <Members> <ScriptMethod> <Name>ConvertFromAST</Name> <Script> $this.GetScriptBlock() </Script> </ScriptMethod> <ScriptMethod> <Name>GetScriptBlock</Name> <Script> [ScriptBlock]::create($this -replace '^\{' -replace '\}$') </Script> </ScriptMethod> </Members> </Type> <Type> <Name>System.Management.Automation.Language.ScriptRequirements</Name> <Members> <ScriptMethod> <Name>ToString</Name> <Script> $this.Script.ToString() </Script> </ScriptMethod> <ScriptProperty> <Name>Script</Name> <GetScriptBlock> $requirement = $this [ScriptBlock]::create( @(if ($requirement.RequirementPSVersion) { "#requires -Version $($requirement.RequirementPSVersion)" } if ($requirement.IsElevationRequired) { "#requires -RunAsAdministrator" } if ($requirement.RequiredModules) { "#requires -Module $(@(foreach ($reqModule in $requirement.RequiredModules) { if ($reqModule.Version -or $req.RequiredVersion -or $req.MaximumVersion) { '@{' + $(@(foreach ($prop in $reqModule.PSObject.Properties) { if (-not $prop.Value) { continue } if ($prop.Name -in 'Name', 'Version') { "Module$($prop.Name)='$($prop.Value.ToString().Replace("'","''"))'" } elseif ($prop.Name -eq 'RequiredVersion') { "MinimumVersion='$($prop.Value)'" } else { "$($prop.Name)='$($prop.Value)'" } }) -join ';') + '}' } else { $reqModule.Name } }) -join ',')" } if ($requirement.RequiredAssemblies) { "#requires -Assembly $($requirement.RequiredAssemblies -join ',')" }) -join [Environment]::NewLine ) </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>System.Management.Automation.Language.TypeConstraintAst</Name> <Members> <ScriptProperty> <Name>ResolvedCommand</Name> <GetScriptBlock> <# .SYNOPSIS Resolves an TypeConstraintAST to a CommandInfo .DESCRIPTION Resolves an TypeConstraintAST to one or more CommandInfo Objects. .EXAMPLE { [InvokePipeScript[a]]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [Microsoft.PowerShell.Core.GetCommand]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [Get_Command]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [GetCommand]$null }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand .EXAMPLE { [cmd]$null }.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute.ResolvedCommand #> # Get the name of the transpiler. $transpilerStepName = if ($this.TypeName.IsGeneric) { $this.TypeName.TypeName.Name } else { $this.TypeName.Name } $decamelCase = [Regex]::new('(?<=[a-z])(?=[A-Z])') @( # If a Transpiler exists by that name, it will be returned first. Get-Transpiler -TranspilerName $transpilerStepName # Then, any periods in the attribute name will be converted to slashes, $fullCommandName = $transpilerStepName -replace '\.','\' -replace '_','-' # and any underscores to dashes. # Then, the first CamelCased code will have a - injected in between the CamelCase. $fullCommandName = $decamelCase.Replace($fullCommandName, '-', 1) # Now we will try to find the command. $ExecutionContext.SessionState.InvokeCommand.GetCommand($fullCommandName, 'All') ) </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>System.Management.Automation.Language.VariableExpressionAst</Name> <Members> <ScriptMethod> <Name>ConvertFromAST</Name> <Script> # Most variables we will not know the value of until we have run. # the exceptions to the rule are: $true, $false, and $null if ($this.variablePath.userPath -in 'true', 'false', 'null') { $ExecutionContext.SessionState.PSVariable.Get($this.variablePath).Value } else { $this } </Script> </ScriptMethod> <ScriptMethod> <Name>GetAssignments</Name> <Script> <# .SYNOPSIS Gets assignments of a variable .DESCRIPTION Searches the abstract syntax tree for assignments of the variable. .EXAMPLE { $x = 1 $y = 2 $x * $y }.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetAssignments() .EXAMPLE { [int]$x, [int]$y = 1, 2 $x * $y }.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetAssignments() .EXAMPLE { param($x, $y) $x * $y }.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetAssignments() #> param() $astVariableName = "$this" $variableFoundAt = @{} foreach ($parent in $this.GetLineage()) { $parent.FindAll({ param($ast) $IsAssignment = ( $ast -is [Management.Automation.Language.AssignmentStatementAst] -and $ast.Left.Find({ param($leftAst) $leftAst -is [Management.Automation.Language.VariableExpressionAST] -and $leftAst.Extent.ToString() -eq $astVariableName }, $false) ) -or ( $ast -is [Management.Automation.Language.ParameterAst] -and $ast.Name.ToString() -eq $astVariableName ) if ($IsAssignment -and -not $variableFoundAt[$ast.Extent.StartOffset]) { $variableFoundAt[$ast.Extent.StartOffset] = $ast $ast } }, $false) } </Script> </ScriptMethod> <ScriptMethod> <Name>GetVariableType</Name> <Script> <# .SYNOPSIS Gets a Variable's Likely Type .DESCRIPTION Determines the type of a variable. This looks for the closest assignment statement and uses this to determine what type the variable is likely to be. .NOTES Subject to revision and improvement. While this covers many potential scenarios, it does not always .EXAMPLE { [int]$x = 1 $y = 2 $x + $y }.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetVariableType() .EXAMPLE { $x = Get-Process $x + $y }.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetVariableType() #> if ($this.VariablePath.userPath -eq 'psBoundParmeters') { return [Management.Automation.PSBoundParametersDictionary] } $assignments = $this.GetAssignments() $closestAssignment = $assignments[0] # Our easiest scenario is that the variable is assigned in a parameter if ($closestAssignment -is [Management.Automation.Language.ParameterAst]) { # If so, the .StaticType will give us our variable type. return $closestAssignment.StaticType } # Our next simple scenario is that the closest assignment is declaring a hashtable if ($closestAssignment.Right.Expression -is [Management.Automation.Language.HashtableAst]) { return [hashtable] } # The left can be a convert expression. if ($closestAssignment.Left -is [Management.Automation.Language.ConvertExpressionAst]) { # If the left was [ordered] if ($closestAssignment.Left.Type.Tostring() -eq '[ordered]') { return [Collections.specialized.OrderedDictionary] # return an OrderedDictionary } else { # If the left side's type can be reflected $reflectedType = $closestAssignment.Left.Type.TypeName.GetReflectionType() if ($reflectedType) { return $reflectedType # return it. } else { # otherwise, return the left's static type. return $closestAssignment.Left.StaticType } } } # Determine if the left side is multiple assignment $isMultiAssignment =$closestAssignment.Left -is [Management.Automation.Language.ArrayLiteralAst] # If the left side is not multiple assignment, but the right side is an array if (-not $isMultiAssignment -and $closestAssignment.Right.Expression -is [Management.Automation.ArrayExpressionAst]) { # then the object is an array. return [Object[]] } # Next, if the right as a convert expression if ($closestAssignment.Right.Expression -is [Management.Automation.Language.ConvertExpressionAst]) { # If it was '[ordered]' if ($closestAssignment.Right.Expression.Type.Tostring() -eq '[ordered]') { # return an ordered dictionary return [Collections.specialized.OrderedDictionary] } else { # Otherwise, see if we have a reflected type. $reflectedType = $closestAssignment.Right.Expression.Type.TypeName.GetReflectionType() if ($reflectedType) { return $reflectedType # If we do, return it. } else { # If we don't, return the static type of the expression return $closestAssignment.Right.Expression.StaticType } } } # The right side could be a pipeline if ($closestAssignment.Right -is [Management.Automation.Language.PipelineAst]) { # If so, walk backwards thru the pipeline for ($pipelineElementIndex = $closestAssignment.Right.PipelineElements.Count - 1; $pipelineElementIndex -ge 0; $pipelineElementIndex--) { $commandInfo = $closestAssignment.Right.PipelineElements[$pipelineElementIndex].ResolvedCommand # If the command had an output type, return it. if ($commandInfo.OutputType) { return $commandInfo.OutputType.Type } } } # If we don't know, return nothing return </Script> </ScriptMethod> </Members> </Type> </Types> |