Skip to content

Commit 593a910

Browse files
StartAutomatingStartAutomating
authored andcommitted
Adding VariableExpressionAst.GetVariableType() (#156)
1 parent ae450c8 commit 593a910

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

PipeScript.types.ps1xml

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ $this.Parent.PipelineElements.IndexOf($this)
199199

200200
</GetScriptBlock>
201201
</ScriptProperty>
202+
<ScriptProperty>
203+
<Name>ResolvedCommand</Name>
204+
<GetScriptBlock>
205+
$ExecutionContext.SessionState.InvokeCommand.GetCommand($this.CommandElements[0].ToString(), 'All')
206+
</GetScriptBlock>
207+
</ScriptProperty>
202208
</Members>
203209
</Type>
204210
<Type>
@@ -300,6 +306,179 @@ if ($this.variablePath.userPath -in 'true', 'false', 'null') {
300306
$this
301307
}
302308

309+
</Script>
310+
</ScriptMethod>
311+
<ScriptMethod>
312+
<Name>GetAssignments</Name>
313+
<Script>
314+
&lt;#
315+
.SYNOPSIS
316+
Gets assignments of a variable
317+
.DESCRIPTION
318+
Searches the abstract syntax tree for assignments of the variable.
319+
.EXAMPLE
320+
{
321+
$x = 1
322+
$y = 2
323+
$x * $y
324+
}.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetAssignments()
325+
.EXAMPLE
326+
{
327+
[int]$x, [int]$y = 1, 2
328+
$x * $y
329+
}.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetAssignments()
330+
.EXAMPLE
331+
{
332+
param($x, $y)
333+
$x * $y
334+
}.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetAssignments()
335+
#&gt;
336+
param()
337+
338+
$astVariableName = "$this"
339+
$variableFoundAt = @{}
340+
foreach ($parent in $this.GetLineage()) {
341+
$parent.FindAll({
342+
param($ast)
343+
$IsAssignment =
344+
(
345+
$ast -is [Management.Automation.Language.AssignmentStatementAst] -and
346+
$ast.Left.Find({
347+
param($leftAst)
348+
$leftAst -is [Management.Automation.Language.VariableExpressionAST] -and
349+
$leftAst.Extent.ToString() -eq $astVariableName
350+
}, $false)
351+
) -or (
352+
$ast -is [Management.Automation.Language.ParameterAst] -and
353+
$ast.Name.ToString() -eq $astVariableName
354+
)
355+
356+
if ($IsAssignment -and -not $variableFoundAt[$ast.Extent.StartOffset]) {
357+
$variableFoundAt[$ast.Extent.StartOffset] = $ast
358+
$ast
359+
}
360+
}, $false)
361+
}
362+
363+
</Script>
364+
</ScriptMethod>
365+
<ScriptMethod>
366+
<Name>GetVariableType</Name>
367+
<Script>
368+
&lt;#
369+
.SYNOPSIS
370+
Gets a Variable's Likely Type
371+
.DESCRIPTION
372+
Determines the type of a variable.
373+
374+
This looks for the closest assignment statement and uses this to determine what type the variable is likely to be.
375+
.NOTES
376+
Subject to revision and improvement. While this covers many potential scenarios, it does not always
377+
.EXAMPLE
378+
{
379+
[int]$x = 1
380+
$y = 2
381+
$x + $y
382+
}.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetVariableType()
383+
.EXAMPLE
384+
{
385+
$x = Get-Process
386+
$x + $y
387+
}.Ast.EndBlock.Statements[-1].PipelineElements[0].Expression.Left.GetVariableType()
388+
#&gt;
389+
if ($this.VariablePath.userPath -eq 'psBoundParmeters') {
390+
return [Management.Automation.PSBoundParametersDictionary]
391+
}
392+
$assignments = $this.GetAssignments()
393+
$closestAssignment = $assignments[0]
394+
395+
# Our easiest scenario is that the variable is assigned in a parameter
396+
if ($closestAssignment -is [Management.Automation.Language.ParameterAst]) {
397+
# If so, the .StaticType will give us our variable type.
398+
return $closestAssignment.StaticType
399+
}
400+
401+
# Our next simple scenario is that the closest assignment is declaring a hashtable
402+
if ($closestAssignment.Right.Expression -is [Management.Automation.Language.HashtableAst]) {
403+
return [hashtable]
404+
}
405+
406+
# The left can be a convert expression.
407+
if ($closestAssignment.Left -is [Management.Automation.Language.ConvertExpressionAst]) {
408+
# If the left was [ordered]
409+
if ($closestAssignment.Left.Type.Tostring() -eq '[ordered]') {
410+
return [Collections.specialized.OrderedDictionary] # return an OrderedDictionary
411+
} else {
412+
# If the left side's type can be reflected
413+
$reflectedType = $closestAssignment.Left.Type.TypeName.GetReflectionType()
414+
if ($reflectedType) {
415+
return $reflectedType # return it.
416+
}
417+
else {
418+
# otherwise, return the left's static type.
419+
return $closestAssignment.Left.StaticType
420+
}
421+
}
422+
}
423+
424+
# Determine if the left side is multiple assignment
425+
$isMultiAssignment =$closestAssignment.Left -is [Management.Automation.Language.ArrayLiteralAst]
426+
427+
# If the left side is not multiple assignment, but the right side is an array
428+
if (-not $isMultiAssignment -and
429+
$closestAssignment.Right.Expression -is [Management.Automation.ArrayExpressionAst]) {
430+
# then the object is an array.
431+
return [Object[]]
432+
}
433+
434+
# Next, if the right as a convert expression
435+
if ($closestAssignment.Right.Expression -is [Management.Automation.Language.ConvertExpressionAst]) {
436+
# If it was '[ordered]'
437+
if ($closestAssignment.Right.Expression.Type.Tostring() -eq '[ordered]') {
438+
# return an ordered dictionary
439+
return [Collections.specialized.OrderedDictionary]
440+
} else {
441+
# Otherwise, see if we have a reflected type.
442+
$reflectedType = $closestAssignment.Right.Expression.Type.TypeName.GetReflectionType()
443+
if ($reflectedType) {
444+
return $reflectedType # If we do, return it.
445+
}
446+
else {
447+
# If we don't, return the static type of the expression
448+
return $closestAssignment.Right.Expression.StaticType
449+
}
450+
}
451+
}
452+
453+
454+
455+
456+
# The right side could be a pipeline
457+
if ($closestAssignment.Right -is [Management.Automation.Language.PipelineAst]) {
458+
# If so, walk backwards thru the pipeline
459+
for ($pipelineElementIndex = $closestAssignment.Right.PipelineElements.Count - 1;
460+
$pipelineElementIndex -ge 0;
461+
$pipelineElementIndex--) {
462+
$commandInfo = $closestAssignment.Right.PipelineElements[$pipelineElementIndex].ResolvedCommand
463+
# If the command had an output type, return it.
464+
if ($commandInfo.OutputType) {
465+
return $commandInfo.OutputType.Type
466+
}
467+
}
468+
}
469+
470+
471+
472+
473+
474+
475+
# If we don't know, return nothing
476+
return
477+
478+
479+
480+
481+
303482
</Script>
304483
</ScriptMethod>
305484
</Members>

0 commit comments

Comments
 (0)