Transpilers/Keywords/Until.psx.ps1
<# .SYNOPSIS until keyword .DESCRIPTION The until keyword simplifies event loops. until will always run at least once, and will run until a condition is true. .NOTES until will become a ```do {} while ()``` statement in PowerShell. .EXAMPLE { $x = 0 until ($x == 10) { $x $x++ } } |.>PipeScript .EXAMPLE { until "00:00:05" { [DateTime]::Now Start-Sleep -Milliseconds 500 } } | .>PipeScript .EXAMPLE Invoke-PipeScript { $tries = 3 until (-not $tries) { "$tries tries left" $tries-- } } #> [ValidateScript({ $commandAst = $_ return ($commandAst -and $CommandAst.CommandElements[0].Value -eq 'until' -or ( $commandAst.CommandElements.Count -ge 2 -and $commandAst.CommandElements[0].Value -like ':*' -and $commandAst.CommandElements[1].Value -eq 'until' ) ) })] param( [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='CommandAst')] [Management.Automation.Language.CommandAst] $CommandAst ) process { $CommandName, $CommandArgs = $commandAst.CommandElements if ($commandName -like ':*') { $null, $firstArg, $secondArg = $CommandAst.ArgumentList } else { $firstArg, $secondArg = $CommandAst.ArgumentList } # If the first arg is a command expression, it becomes do {} while ($firstArg) if (-not $firstArg -or $firstArg.GetType().Name -notin 'ParenExpressionAst', 'ScriptBlockExpressionAst', 'VariableExpressionAst','MemberExpressionAst','ExpandableStringExpressionAst') { Write-Error "Until must be followed by a Variable, Member, ExpandableString, or Parenthesis Expression" return } if ($secondArg -isnot [Scriptblock]) { Write-Error "Until must be followed by a condition and a ScriptBlock" return } $condition = $firstArg if ($firstArg.GetType().Name -eq 'ParenExpressionAst' -and $firstArg.Pipeline.PipelineElements.Count -eq 1 -and $firstArg.Pipeline.PipelineElements[0].Expression -and $firstArg.Pipeline.PipelineElements[0].Expression.GetType().Name -in 'VariableExpressionAst','MemberExpressionAst','ExpandableStringExpressionAst') { $condition = $firstArg.Pipeline.PipelineElements[0].Expression } elseif ($firstArg.GetType().Name -eq 'ScriptBlockExpressionAst') { $condition = $firstArg -replace '^\{' -replace '\}$' } $conditionScript = [ScriptBlock]::Create($condition) $LoopScript = $secondArg $secondArgScriptBlock = [ScriptBlock]::Create($LoopScript) $conditionScript = $conditionScript | .>Pipescript $untilTranspiled = $secondArgScriptBlock | .>Pipescript $conditionScript = if ($conditionScript -match '^\(') { "(-not $conditionScript)" } else { "(-not ($conditionScript))" } $newScript = @" $(if ($CommandName -like ':*') { "$CommandName "})do { $untilTranspiled } while $conditionScript "@ [ScriptBlock]::Create($newScript) } |