@@ -91,38 +91,45 @@ process {
9191 @ (
9292 # check each ScriptBlock attribute
9393 foreach ($attrAst in $ScriptBlock.Ast.ParamBlock.Attributes ) {
94-
94+ # and see if it is a real type.
9595 $attrRealType =
9696 if ($attrAst.TypeName.GetReflectionType ) {
9797 $attrAst.TypeName.GetReflectionType ()
9898 } elseif ($attrAst.TypeName.ToString ) {
9999 $attrAst.TypeName.ToString () -as [type ]
100100 }
101-
101+
102+ # If it is not a real type,
102103 if (-not $attrRealType ) {
103- $attrAst
104+ $attrAst # we will transpile it with the whole script block as input.
104105 }
105106 }
106107 )
107108
109+ # Now, we strip away any Attribute Based Composition.
108110 $replacements = [Ordered ]@ {}
109111 $myOffset = 0
110- foreach ($moreTo in $moreToPreTranspile ) {
112+ # To do this effeciently, we collect all of the attributes left to pretranspile
113+ foreach ($moreTo in $moreToPreTranspile ) {
111114 $TranspilerAttributes += $moreTo.Extent.ToString ()
112115 $start = $scriptText.IndexOf ($moreTo.extent.text , $myOffset )
113116 $end = $start + $moreTo.Extent.Text.Length
114- $replacements [" $start ,$end " ] = ' '
117+ $replacements [" $start ,$end " ] = ' ' # and replace each of them with a blank.
115118 }
116119
120+ # get the updated script,
117121 $UpdatedScript = Update-PipeScript - ScriptReplacement $replacements - ScriptBlock $ScriptBlock
118-
119- $scriptBlock = $UpdatedScript
122+ $scriptBlock = $UpdatedScript # and replace $ScriptBlock.
120123 }
121124
125+
126+ # Now we set the to the contents of the scriptblock.
122127 $scriptText = " $scriptBlock "
123-
124128
125- $PreTranspile =
129+ # If there was no ScriptBlock, return
130+ if (-not $ScriptBlock ) { return }
131+
132+ $PreTranspile =
126133 if ($transpilerAttributes ) {
127134 [ScriptBlock ]::Create(
128135 ($TranspilerAttributes -join [Environment ]::NewLine) + $ (
@@ -133,8 +140,6 @@ process {
133140 )
134141 }
135142
136- if (-not $ScriptBlock ) { return }
137-
138143 # If there were any attributes that can be pre-transpiled, convert them.
139144 if ($PreTranspile.Ast.ParamBlock.Attributes ) {
140145 # Get the list of transpilation attributes
@@ -206,9 +211,11 @@ process {
206211
207212 $itemTypeName = $item.GetType ().Fullname
208213 if (-not $script :TranspilerAstTypes [$itemTypeName ]) {
209- $script :TranspilerAstTypes [$itemTypeName ] = Get-Transpiler - CouldPipe $item |
210- Where-Object {
211- $_.ExtensionCommand.CouldRun (@ {} + $_.ExtensionParameter )
214+ $script :TranspilerAstTypes [$itemTypeName ] =
215+ foreach ($couldPipe in Get-Transpiler - CouldPipe $item ) {
216+ if ($couldPipe.ExtensionCommand.CouldRun (@ {} + $couldPipe.ExtensionParameter )) {
217+ $couldPipe
218+ }
212219 }
213220 }
214221
@@ -228,44 +235,102 @@ process {
228235 }
229236
230237 # If we found any matching pipescripts
231- if ($pipescripts ) {
232- :NextPipeScript
233- foreach ($ps in $pipescripts ) {
234- $pipeScriptOutput = Invoke-PipeScript - InputObject $item - CommandInfo $ps.ExtensionCommand
235- foreach ($pso in $pipeScriptOutput ) {
236- if ($pso -is [Collections.IDictionary ]) {
237- $psoCopy = [Ordered ]@ {} + $pso
238- foreach ($kv in @ ($psoCopy.GetEnumerator ())) {
239- if ($kv.Key -is [Management.Automation.Language.Ast ]) {
240- $astReplacements [$kv.Key ] = $kv.Value
241- $psoCopy.Remove ($kv.Key )
242- } elseif ($kv.Key -match ' ^\d,\d$' ) {
243- $textReplacements [" $ ( $kv.Key ) " ] = $kv.Value
244- $psoCopy.Remove ($kv.Key )
245- }
238+ if ($pipescripts ) {
239+ # try to run each one.
240+ :NextPipeScript foreach ($ps in $pipescripts ) {
241+ # If it had output
242+ $pipeScriptOutput = Invoke-PipeScript - InputObject $item - CommandInfo $ps.ExtensionCommand
243+ # Walk over each potential output
244+ foreach ($pso in $pipeScriptOutput ) {
245+ # If the output was a dictionary, treat it as a series of replacements.
246+ if ($pso -is [Collections.IDictionary ]) {
247+ $psoCopy = [Ordered ]@ {} + $pso
248+ foreach ($kv in @ ($psoCopy.GetEnumerator ())) {
249+ # If the key was an AST element
250+ if ($kv.Key -is [Management.Automation.Language.Ast ]) {
251+ # replace the element with it's value
252+ $astReplacements [$kv.Key ] = $kv.Value
253+ $psoCopy.Remove ($kv.Key )
254+ } elseif ($kv.Key -match ' ^\d+,\d+$' ) {
255+ # otherwise, it the key was a pair of digits, replace that span.
256+ $Replacements [" $ ( $kv.Key ) " ] = $kv.Value
257+ $psoCopy.Remove ($kv.Key )
246258 }
247- if ($psoCopy.Count ) {
248- $updateSplats += $psoCopy
249- }
250259 }
251- # If we have ouput from any of the scripts (and we have not yet replaced anything)
252- elseif ($pso -and -not $AstReplacements [$item ])
253- {
254- # determine the end of this AST element
255- $start = $scriptText.IndexOf ($item.Extent.Text , $myOffset )
256- $end = $start + $item.Extent.Text.Length
257- $skipUntil = $end # set SkipUntil
258- $AstReplacements [$item ] = $pso # and store the replacement.
260+ if ($psoCopy.Count ) {
261+ $updateSplats += $psoCopy
262+ }
263+ }
264+ # If we have ouput from any of the scripts (and we have not yet replaced anything)
265+ elseif ($pso -and -not $AstReplacements [$item ])
266+ {
267+ # determine the end of this AST element
268+ $start = $scriptText.IndexOf ($item.Extent.Text , $myOffset )
269+ $end = $start + $item.Extent.Text.Length
270+ $skipUntil = $end # set SkipUntil
271+ $AstReplacements [$item ] = $pso # and store the replacement.
272+
273+ # region Special Properties
274+ # Because PowerShell can attach properties to any object,
275+ # we can use the presence of attached properties to change context around the replacement.
276+
277+ # .SkipUntil or .IgnoreUntil can specify a new index or AST end point
278+ foreach ($toSkipAlias in ' SkipUntil' , ' IgnoreUntil' ) {
279+ foreach ($toSkipUntil in $pso .$toSkipAlias ) {
280+ if ($toSkipUntil -is [int ] -and $toSkipUntil -gt $end ) {
281+ $skipUntil = $toSkipUntil
282+ } elseif ($toSkipUntil -is [Management.Automation.Language.Ast ]) {
283+ $newSkipStart = $scriptText.IndexOf ($toSkipUntil.Extent.Text , $myOffset )
284+ if ($newSkipStart -ne -1 ) {
285+ $end = $newSkipStart + $toSkipUntil.Extent.Text.Length
286+ if ($end -gt $skipUntil ) {
287+ $skipUntil = $end
288+ }
289+ if ($toSkipUntil -ne $item ) {
290+ $AstReplacements [$toSkipUntil ] = ' '
291+ }
292+ }
293+ }
294+ }
295+ }
296+
297+ # .ToRemove,.RemoveAST, or .RemoveElement will remove AST elements or ranges
298+ foreach ($toRemoveAlias in ' ToRemove' , ' RemoveAST' , ' RemoveElement' ) {
299+ foreach ($toRemove in $pso .$toRemoveAlias ) {
300+ if ($toRemove -is [Management.Automation.Language.Ast ]) {
301+ $AstReplacements [$toRemove ] = ' '
302+ } elseif ($toRemove -match ' ^\d+,\d+$' ) {
303+ $Replacements [$toRemove ] = ' '
304+ }
305+ }
306+ }
307+
308+ # .ToReplace,.ReplaceAST or .ReplaceElement will replace elements or ranges.
309+ foreach ($toReplaceAlias in ' ToReplace' , ' ReplaceAST' , ' ReplaceElement' ) {
310+ foreach ($toReplace in $pso .$toReplaceAlias ) {
311+ if ($toReplace -isnot [Collections.IDictionary ]) {
312+ continue
313+ }
314+ foreach ($tr in $toReplace.GetEnumerator ()) {
315+ if ($tr.Key -is [Management.Automaton.Language.Ast ]) {
316+ $AstReplacements [$tr.Key ] = $tr.Value
317+ } elseif ($tr.Key -match ' ^\d+,\d+$' ) {
318+ $textReplacements [" $ ( $tr.Key ) " ] = $tr.Value
319+ }
320+ }
321+ }
259322 }
323+ # endregion Special Properties
260324 }
261- # If the transpiler had output, do not process any more transpilers.
262- if ($pipeScriptOutput ) { break }
263- }
325+ }
326+ # If the transpiler had output, do not process any more transpilers.
327+ if ($pipeScriptOutput ) { break }
328+ }
264329 }
265330 }
266331
267- $newScript =
268- if ($AstReplacements.Count ) {
332+ $newScript =
333+ if ($AstReplacements.Count ) {
269334 Update-PipeScript - ScriptBlock $ScriptBlock - ScriptReplacement $replacements - AstReplacement $AstReplacements
270335 } elseif ($updateSplats ) {
271336 foreach ($upSplat in $updateSplats ) {
0 commit comments