Last active
May 9, 2021 22:44
-
-
Save romero126/7015e8d9706b25f83cf09f9bdd0ac5f5 to your computer and use it in GitHub Desktop.
ExpandStringVisitor: Context locked String Interpolation using AstVisitor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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