1
1
function Get-Turtle {
2
2
<#
3
+ . SYNOPSIS
4
+ Gets Turtle in PowerShell
5
+ . DESCRIPTION
6
+ Gets, sets, and moves a turtle object in PowerShell.
7
+ . NOTES
8
+ Each argument can be the name of a member of the turtle object.
9
+
10
+ After a member name is encountered, subsequent arguments will be passed to the member as parameters.
3
11
. EXAMPLE
4
12
turtle square 50
5
13
. EXAMPLE
@@ -8,6 +16,7 @@ function Get-Turtle {
8
16
turtle polygon 10 6
9
17
. EXAMPLE
10
18
turtle ('forward', 10, 'rotate', 120 * 3)
19
+
11
20
#>
12
21
[CmdletBinding (PositionalBinding = $false )]
13
22
[Alias (' turtle' )]
@@ -18,17 +27,12 @@ function Get-Turtle {
18
27
if (-not $script :TurtleTypeData ) {
19
28
$script :TurtleTypeData = Get-TypeData - TypeName Turtle
20
29
}
21
- $methodNames = @ (foreach ($memberName in $script :TurtleTypeData.Members.Keys ) {
22
- if ($script :TurtleTypeData.Members [$memberName ] -is
23
- [Management.Automation.Runspaces.ScriptMethodData ]) {
24
- $memberName
25
- }
26
- })
30
+ $memberNames = @ ($script :TurtleTypeData.Members.Keys )
27
31
28
32
if ($wordToComplete ) {
29
- return $methodNames -like " $wordToComplete *"
33
+ return $memberNames -like " $wordToComplete *"
30
34
} else {
31
- return $methodNames
35
+ return $memberNames
32
36
}
33
37
})]
34
38
[Parameter (ValueFromRemainingArguments )]
@@ -43,68 +47,143 @@ function Get-Turtle {
43
47
$InputObject
44
48
)
45
49
46
- begin {
47
- $turtleType = Get-TypeData - TypeName Turtle
48
- $memberNames = @ (foreach ($memberName in $turtleType.Members.Keys ) {
49
- if (
50
- ($turtleType.Members [$memberName ] -is [Management.Automation.Runspaces.ScriptMethodData ]) -or
51
- (
52
- $turtleType.Members [$memberName ] -is
53
- [Management.Automation.Runspaces.AliasPropertyData ] -and
54
- $turtleType.Members [
55
- $turtleType.Members [$memberName ].ReferencedMemberName
56
- ] -is [Management.Automation.Runspaces.ScriptMethodData ]
57
- )
58
- ) {
59
- $memberName
60
- }
61
- })
50
+ begin {
51
+ # Get information about our turtle pseudo-type.
52
+ $turtleType = Get-TypeData - TypeName Turtle
53
+ # any member name is a potential command
54
+ $memberNames = $turtleType.Members.Keys
62
55
63
- $memberNames = $memberNames | Sort-Object @ {Expression = { $_.Length };Descending = $true }, Name
56
+ # We want to sort the member names by length, in case we need them in a pattern or want to sort quickly.
57
+ $memberNames = $memberNames | Sort-Object @ {Expression = { $_.Length };Descending = $true }, name
58
+ # Create a new turtle object in case we have no turtle input.
64
59
$currentTurtle = [PSCustomObject ]@ {PSTypeName = ' Turtle' }
65
60
}
66
61
67
- process {
68
-
62
+ process {
69
63
if ($PSBoundParameters.InputObject -and
70
64
$PSBoundParameters.InputObject.pstypenames -eq ' Turtle' ) {
71
65
$currentTurtle = $PSBoundParameters.InputObject
66
+ } elseif ($PSBoundParameters.InputObject ) {
67
+ # If input was passed, and it was not a turtle, pass it through.
68
+ return $PSBoundParameters.InputObject
72
69
}
73
70
74
- $currentMethod = $null
75
71
76
- $wordsAndArguments = foreach ($arg in $ArgumentList ) {
72
+ # First we want to split each argument into words.
73
+ # This way, it is roughly the same if you say:
74
+ # * `turtle 'forward 10'`
75
+ # * `turtle forward 10`
76
+ # * `turtle 'forward', 10`
77
+ $wordsAndArguments = @ (foreach ($arg in $ArgumentList ) {
78
+ # If the argument is a string, split it by whitespace.
77
79
if ($arg -is [string ]) {
78
80
$arg -split ' \s{1,}'
79
81
} else {
82
+ # otherwise, leave the argument alone.
80
83
$arg
81
84
}
82
- }
85
+ })
83
86
84
- :findCommand for ($argIndex = 0 ; $argIndex -lt $wordsAndArguments.Length ; $argIndex ++ ) {
87
+ # Now that we have a series of words, we can process them.
88
+ # We want to keep track of the current member,
89
+ # and continue to the next word until we find a member name.
90
+ $currentMember = $null
91
+ $outputTurtle = $false
92
+
93
+ # To do this in one pass, we will iterate through the words and arguments.
94
+ # We use an indexed loop so we can skip past claimed arguments.
95
+ for ($argIndex = 0 ; $argIndex -lt $wordsAndArguments.Length ; $argIndex ++ ) {
85
96
$arg = $wordsAndArguments [$argIndex ]
86
- if ($arg -in $memberNames ) {
87
- $currentMethod = $arg
88
- for (
89
- $methodArgIndex = $argIndex + 1 ;
90
- $methodArgIndex -lt $wordsAndArguments.Length -and
91
- $wordsAndArguments [$methodArgIndex ] -notin $memberNames ;
92
- $methodArgIndex ++ ) {
97
+ # If the argument is not in the member names list, we can complain about it.
98
+ if ($arg -notin $memberNames ) {
99
+ if (-not $currentMember -and $arg -is [string ]) {
100
+ Write-Warning " Unknown command '$arg '."
93
101
}
94
- # Command without parameters
95
- if ($methodArgIndex -eq $argIndex ) {
96
- $argList = @ ()
97
- $currentTurtle = $currentTurtle .$currentMethod.Invoke ()
102
+ continue
103
+ }
104
+
105
+
106
+ # If we have a current member, we can invoke it or get it.
107
+ $currentMember = $arg
108
+ # We can also begin looking for arguments
109
+ for (
110
+ # at the next index.
111
+ $methodArgIndex = $argIndex + 1 ;
112
+ # We will continue until we reach the end of the words and arguments,
113
+ $methodArgIndex -lt $wordsAndArguments.Length -and
114
+ $wordsAndArguments [$methodArgIndex ] -notin $memberNames ;
115
+ $methodArgIndex ++ ) {
116
+ }
117
+ # Now we know how long it took to get to the next member name.
118
+
119
+ # And we can determine if we have any parameters
120
+ $argList =
121
+ if ($methodArgIndex -eq ($argIndex + 1 )) {
122
+ @ ()
98
123
}
99
124
else {
100
- $argList = $wordsAndArguments [($argIndex + 1 ).. ($methodArgIndex - 1 )]
101
- $currentTurtle = $currentTurtle .$currentMethod.Invoke ($argList )
102
- # "$($currentMethod) $($argList -join ' ')"
125
+ $wordsAndArguments [($argIndex + 1 ).. ($methodArgIndex - 1 )]
103
126
$argIndex = $methodArgIndex - 1
104
127
}
128
+
129
+ # Look up the member information for the current member.
130
+ $memberInfo = $turtleType.Members [$currentMember ]
131
+ # If it's an alias
132
+ if ($memberInfo.ReferencedMemberName ) {
133
+ # try to resolve it.
134
+ $currentMember = $memberInfo.ReferencedMemberName
135
+ $memberInfo = $turtleType.Members [$currentMember ]
136
+ }
137
+
138
+
139
+ # Now we want to get the output from the step.
140
+ $stepOutput =
141
+ if (
142
+ # If the member is a method, let's invoke it.
143
+ $memberInfo -is [Management.Automation.Runspaces.ScriptMethodData ] -or
144
+ $memberInfo -is [Management.Automation.PSMethod ]
145
+ ) {
146
+ # If we have arguments,
147
+ if ($argList ) {
148
+ # pass them to the method.
149
+ $currentTurtle .$currentMember.Invoke ($argList )
150
+ } else {
151
+ # otherwise, just invoke the method with no arguments.
152
+ $currentTurtle .$currentMember.Invoke ()
153
+ }
154
+ } else {
155
+ # If the member is a property, we can get it or set it.
156
+
157
+ # If we have any arguments,
158
+ if ($argList ) {
159
+ # lets try to set it.
160
+ $currentTurtle .$currentMember = $argList
161
+ } else {
162
+ # otherwise, lets get the property
163
+ $currentTurtle .$currentMember
164
+ }
165
+ }
166
+
167
+ # If the output is not a turtle object, we can output it.
168
+ # NOTE: This will lead to multiple types of output in the pipeline.
169
+ # Luckily, this should be one of the few cases where this does not annoy too much.
170
+ # Properties being returned will largely be strings or numbers.
171
+ if (-not ($stepOutput.pstypenames -eq ' Turtle' )) {
172
+ # Output the step
173
+ $stepOutput
174
+ # and set the output turtle to false.
175
+ $outputTurtle = $false
176
+ } else {
177
+ # Set the current turtle to the step output.
178
+ $currentTurtle = $stepOutput
179
+ # and output it later (presumably).
180
+ $outputTurtle = $true
105
181
}
106
182
}
107
-
108
- return $currentTurtle
183
+
184
+ # If the last members returned a turtle object, we can output it.
185
+ if ($outputTurtle ) {
186
+ return $currentTurtle
187
+ }
109
188
}
110
189
}
0 commit comments