Skip to content

Instantly share code, notes, and snippets.

@romero126
Last active May 9, 2021 22:44
Show Gist options
  • Save romero126/7015e8d9706b25f83cf09f9bdd0ac5f5 to your computer and use it in GitHub Desktop.
Save romero126/7015e8d9706b25f83cf09f9bdd0ac5f5 to your computer and use it in GitHub Desktop.
ExpandStringVisitor: Context locked String Interpolation using AstVisitor
class ConstrainedVariableInterpolation : System.Management.Automation.Language.AstVisitor {
hidden [Hashtable] $Property
[System.Collections.Generic.List[PSCustomObject]] $Result
hidden [System.Management.Automation.Language.Ast] $Ast
[System.Management.Automation.Language.AstVisitAction] CheckForPostAction([System.Management.Automation.Language.Ast] $ast, [System.Management.Automation.Language.AstVisitAction] $action)
{
throw 'CheckForPostAction not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitErrorStatement([System.Management.Automation.Language.ErrorStatementAst] $errorStatementAst)
{
throw 'VisitErrorStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitErrorExpression([System.Management.Automation.Language.ErrorExpressionAst] $errorExpressionAst)
{
throw 'VisitErrorExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitScriptBlock([System.Management.Automation.Language.ScriptBlockAst] $scriptBlockAst)
{
#throw 'VisitScriptBlock not supported'
if (
($scriptBlockAst.ParamBlock) -or
($scriptBlockAst.BeginBlock) -or
($scriptBlockAst.ProcessBlock) -or
($scriptBlockAst.DynamicParamBlock)
)
{
throw "VisitScriptBlock not supported"
}
return $this.ExpandExpressionList($scriptBlockAst.EndBlock)
}
[System.Management.Automation.Language.AstVisitAction] VisitParamBlock([System.Management.Automation.Language.ParamBlockAst] $paramBlockAst)
{
throw 'VisitParamBlock not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitNamedBlock([System.Management.Automation.Language.NamedBlockAst] $namedBlockAst)
{
return $this.ExpandExpressionObjects($namedBlockAst.Statements)
}
[System.Management.Automation.Language.AstVisitAction] VisitTypeConstraint([System.Management.Automation.Language.TypeConstraintAst] $typeConstraintAst)
{
throw 'VisitTypeConstraint not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitAttribute([System.Management.Automation.Language.AttributeAst] $attributeAst)
{
throw 'VisitAttribute not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitParameter([System.Management.Automation.Language.ParameterAst] $parameterAst)
{
throw 'VisitParameter not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitTypeExpression([System.Management.Automation.Language.TypeExpressionAst] $typeExpressionAst)
{
throw 'VisitTypeExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitFunctionDefinition([System.Management.Automation.Language.FunctionDefinitionAst] $functionDefinitionAst)
{
throw 'VisitFunctionDefinition not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitStatementBlock([System.Management.Automation.Language.StatementBlockAst] $statementBlockAst)
{
return $this.ExpandExpressionObjects($statementBlockAst.Statements)
}
[System.Management.Automation.Language.AstVisitAction] VisitIfStatement([System.Management.Automation.Language.IfStatementAst] $ifStmtAst)
{
throw 'VisitIfStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitTrap([System.Management.Automation.Language.TrapStatementAst] $trapStatementAst)
{
throw 'VisitTrap not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitSwitchStatement([System.Management.Automation.Language.SwitchStatementAst] $switchStatementAst)
{
throw 'VisitSwitchStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitDataStatement([System.Management.Automation.Language.DataStatementAst] $dataStatementAst)
{
throw 'VisitDataStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitForEachStatement([System.Management.Automation.Language.ForEachStatementAst] $forEachStatementAst)
{
throw 'VisitForEachStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitDoWhileStatement([System.Management.Automation.Language.DoWhileStatementAst] $doWhileStatementAst)
{
throw 'VisitDoWhileStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitForStatement([System.Management.Automation.Language.ForStatementAst] $forStatementAst)
{
throw 'VisitForStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitWhileStatement([System.Management.Automation.Language.WhileStatementAst] $whileStatementAst)
{
throw 'VisitWhileStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitCatchClause([System.Management.Automation.Language.CatchClauseAst] $catchClauseAst)
{
throw 'VisitCatchClause not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitTryStatement([System.Management.Automation.Language.TryStatementAst] $tryStatementAst)
{
throw 'VisitTryStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitBreakStatement([System.Management.Automation.Language.BreakStatementAst] $breakStatementAst)
{
throw 'VisitBreakStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitContinueStatement([System.Management.Automation.Language.ContinueStatementAst] $continueStatementAst)
{
throw 'VisitContinueStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitReturnStatement([System.Management.Automation.Language.ReturnStatementAst] $returnStatementAst)
{
throw 'VisitReturnStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitExitStatement([System.Management.Automation.Language.ExitStatementAst] $exitStatementAst)
{
throw 'VisitExitStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitThrowStatement([System.Management.Automation.Language.ThrowStatementAst] $throwStatementAst)
{
throw 'VisitThrowStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitDoUntilStatement([System.Management.Automation.Language.DoUntilStatementAst] $doUntilStatementAst)
{
throw 'VisitDoUntilStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitAssignmentStatement([System.Management.Automation.Language.AssignmentStatementAst] $assignmentStatementAst)
{
throw 'VisitAssignmentStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitPipeline([System.Management.Automation.Language.PipelineAst] $pipelineAst)
{
return $this.ExpandExpressionObjects($pipelineAst.PipelineElements)
}
[System.Management.Automation.Language.AstVisitAction] VisitCommand([System.Management.Automation.Language.CommandAst] $commandAst)
{
throw 'VisitCommand not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitCommandExpression([System.Management.Automation.Language.CommandExpressionAst] $commandExpressionAst)
{
return $this.ExpandExpression($commandExpressionAst.Expression)
}
[System.Management.Automation.Language.AstVisitAction] VisitCommandParameter([System.Management.Automation.Language.CommandParameterAst] $commandParameterAst)
{
throw 'VisitCommandParameter not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitMergingRedirection([System.Management.Automation.Language.MergingRedirectionAst] $redirectionAst)
{
throw 'VisitMergingRedirection not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitFileRedirection([System.Management.Automation.Language.FileRedirectionAst] $redirectionAst)
{
throw 'VisitFileRedirection not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitBinaryExpression([System.Management.Automation.Language.BinaryExpressionAst] $binaryExpressionAst)
{
$left = [ConstrainedVariableInterpolation]::Parse($binaryExpressionAst.Left, $this.Property).Value
$right = [ConstrainedVariableInterpolation]::Parse($binaryExpressionAst.Right, $this.Property).Value
$r = switch ($binaryExpressionAst.Operator)
{
"Ieq" {
$left -ieq $right
}
"Ine" {
$left -ine $right
}
"Igt" {
$left -igt $right
}
"Ige" {
$left -ige $right
}
"Ilt" {
$left -ilt $right
}
"Ile" {
$left -ile $right
}
"Ilike" {
$left -ilike $right
}
"Inotlike" {
$left -inotlike $right
}
"Imatch" {
$left -imatch $right
}
"Inotmatch" {
$left -inotmatch $right
}
"Ireplace" {
$left -replace $right
}
"Icontains" {
$left -icontains $right
}
"Inotcontains" {
$left -inotcontains $right
}
"Iin" {
$left -iin $right
}
"Inotin" {
$left -inotin $right
}
"Is" {
$left -is $right
}
"IsNot" {
$left -isnot $right
}
default {
$binaryExpressionAst | Out-Host
throw "NotImplementedException"
}
}
#Write-Host $left, $binaryExpressionAst.Operator, $right, "=", "$r"
return $this.ExpandExpression($binaryExpressionAst, $r)
#throw 'VisitBinaryExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitUnaryExpression([System.Management.Automation.Language.UnaryExpressionAst] $unaryExpressionAst)
{
throw 'VisitUnaryExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitConvertExpression([System.Management.Automation.Language.ConvertExpressionAst] $convertExpressionAst)
{
$child = [ConstrainedVariableInterpolation]::Parse($convertExpressionAst.Child, $this.Property)
return $this.ExpandExpression($convertExpressionAst, ($child.Value -as $convertExpressionAst.StaticType))
}
[System.Management.Automation.Language.AstVisitAction] VisitConstantExpression([System.Management.Automation.Language.ConstantExpressionAst] $constantExpressionAst)
{
return $this.ExpandExpression($constantExpressionAst, $constantExpressionAst.Value)
}
[System.Management.Automation.Language.AstVisitAction] VisitStringConstantExpression([System.Management.Automation.Language.StringConstantExpressionAst] $stringConstantExpressionAst)
{
return $this.ExpandExpression($stringConstantExpressionAst, $stringConstantExpressionAst.Value)
}
[System.Management.Automation.Language.AstVisitAction] VisitSubExpression([System.Management.Automation.Language.SubExpressionAst] $subExpressionAst)
{
return $this.ExpandExpression($subExpressionAst.SubExpression)
}
[System.Management.Automation.Language.AstVisitAction] VisitUsingExpression([System.Management.Automation.Language.UsingExpressionAst] $usingExpressionAst)
{
throw 'VisitUsingExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitVariableExpression([System.Management.Automation.Language.VariableExpressionAst] $variableExpressionAst)
{
return $this.ExpandExpression($variableExpressionAst, $this.Property.$($variableExpressionAst.VariablePath.UserPath))
}
[System.Management.Automation.Language.AstVisitAction] VisitMemberExpression([System.Management.Automation.Language.MemberExpressionAst] $memberExpressionAst)
{
# Expand both expressions here
$value = [ConstrainedVariableInterpolation]::Parse($memberExpressionAst.Expression, $this.Property)
$member = [ConstrainedVariableInterpolation]::Parse($memberExpressionAst.Member, $this.Property)
return $this.ExpandExpression($memberExpressionAst, $($value.Value).$($member.Value))
}
[System.Management.Automation.Language.AstVisitAction] VisitInvokeMemberExpression([System.Management.Automation.Language.InvokeMemberExpressionAst] $methodCallAst)
{
throw 'VisitInvokeMemberExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitArrayExpression([System.Management.Automation.Language.ArrayExpressionAst] $arrayExpressionAst)
{
throw 'VisitArrayExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitArrayLiteral([System.Management.Automation.Language.ArrayLiteralAst] $arrayLiteralAst)
{
return $this.ExpandExpressionObjects($arrayLiteralAst.Elements)
}
[System.Management.Automation.Language.AstVisitAction] VisitHashtable([System.Management.Automation.Language.HashtableAst] $hashtableAst)
{
throw 'VisitHashtable not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitScriptBlockExpression([System.Management.Automation.Language.ScriptBlockExpressionAst] $scriptBlockExpressionAst)
{
return $this.ExpandExpressionObjects($scriptBlockExpressionAst.ScriptBlock)
}
[System.Management.Automation.Language.AstVisitAction] VisitParenExpression([System.Management.Automation.Language.ParenExpressionAst] $parenExpressionAst)
{
throw 'VisitParenExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitExpandableStringExpression([System.Management.Automation.Language.ExpandableStringExpressionAst] $expandableStringExpressionAst)
{
return $this.ExpandExpressionList($expandableStringExpressionAst.NestedExpressions)
}
[System.Management.Automation.Language.AstVisitAction] VisitIndexExpression([System.Management.Automation.Language.IndexExpressionAst] $indexExpressionAst)
{
$target = [ConstrainedVariableInterpolation]::Parse($indexExpressionAst.Target, $this.Property)
$index = [ConstrainedVariableInterpolation]::Parse($indexExpressionAst.Index, $this.Property)
return $this.ExpandExpression($indexExpressionAst, $($target.Value)[$index.Value])
}
[System.Management.Automation.Language.AstVisitAction] VisitAttributedExpression([System.Management.Automation.Language.AttributedExpressionAst] $attributedExpressionAst)
{
throw 'VisitAttributedExpression not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitBlockStatement([System.Management.Automation.Language.BlockStatementAst] $blockStatementAst)
{
throw 'VisitBlockStatement not supported'
}
[System.Management.Automation.Language.AstVisitAction] VisitNamedAttributeArgument([System.Management.Automation.Language.NamedAttributeArgumentAst] $namedAttributeArgumentAst)
{
throw 'VisitNamedAttributeArgument not supported'
}
hidden [System.Management.Automation.Language.AstVisitAction] ExpandExpressionObjects([Object] $Elements)
{
#return $this.ExpandExpressionList($Elements)
$r = foreach ($expression in $elements) {
$resolvedExpression = [ConstrainedVariableInterpolation]::Parse($expression, $this.Property)
if ($null -eq $resolvedExpression.Value) {
return $this.ExpandExpression( $this.Ast, $this.Ast.Extent.Text )
}
$resolvedExpression.Value
}
return $this.ExpandExpression( $this.Ast, $r)
}
hidden [System.Management.Automation.Language.AstVisitAction] ExpandExpressionList([Object] $Elements)
{
# Sets the value to a string
$string = $this.Ast.Extent.Text
$r = for ($i = @($elements).Count - 1; $i -ge 0; $i--)
{
$expression = $elements[$i]
$resolvedExpression = [ConstrainedVariableInterpolation]::Parse($expression, $this.Property)
# If there is no resolved value we Failback to No-ResolvedValue
if ($null -eq $resolvedExpression.Value) {
return $this.ExpandExpression( $this.Ast, $this.Ast.Extent.Text )
}
$resolvedString = "{0}" -f ($resolvedExpression.Value -join ", ")
$resolvedString
# Remove and insert expression
$string = $string.Remove(
$expression.Extent.StartOffset - $this.Ast.Extent.StartOffset,
$expression.Extent.EndOffset - $expression.Extent.StartOffset
)
$string = $string.Insert(
$expression.Extent.StartOffset - $this.Ast.Extent.StartOffset,
$resolvedString
)
}
return $this.ExpandExpression( $this.Ast, $string)
}
hidden [System.Management.Automation.Language.AstVisitAction] ExpandExpression([System.Management.Automation.Language.Ast] $Ast)
{
return $this.ExpandExpression($Ast, [ConstrainedVariableInterpolation]::Parse($Ast, $this.Property).Value)
}
hidden [System.Management.Automation.Language.AstVisitAction] ExpandExpression([System.Management.Automation.Language.Ast] $Ast, [object] $Value)
{
$this.Result.Add(
@{
StartOffset = $Ast.Extent.StartOffset
EndOffset = $Ast.Extent.EndOffset
Value = $Value
}
)
return [System.Management.Automation.Language.AstVisitAction]::StopVisit
}
ConstrainedVariableInterpolation([System.Management.Automation.Language.Ast] $Ast, [Hashtable] $Property)
{
$this.Result = New-Object System.Collections.Generic.List[PSCustomObject]
$this.Ast = $Ast;
$this.Property = $Property;
}
static [Object] Parse([System.Management.Automation.Language.Ast] $Ast, [Hashtable]$Property)
{
# We use this parse to expand
$visitor = [ConstrainedVariableInterpolation]::new($Ast, $Property)
$Ast.Visit($visitor)
return $visitor.Result
}
static [Object] Parse([string] $String, [Hashtable]$Property)
{
# Add Properties
$Property['True'] = $true
$Property['False'] = $false
$Property['Null'] = $null
# Double Escape Strings because FUN
# Turn into ScriptBlock
$parseString = $string.Replace("""", """""")
$parseString = "{""$parseString""}"
[System.Management.Automation.Language.Token[]] $astTokens = $null;
[System.Management.Automation.Language.ParseError[]] $astErrors = $null
$astVisitor = [System.Management.Automation.Language.Parser]::ParseInput($parseString, [ref] $astTokens, [ref] $astErrors)
try {
$_resultObjects = [string][ConstrainedVariableInterpolation]::Parse($astVisitor, $Property).Value
#return $_resultObjects
return $_resultObjects.SubString(2, $_resultObjects.Length -4)
}
catch {
throw $_
}
return $String
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment