Skip to content

Commit fbf5fa9

Browse files
author
James Brundage
committed
Adding until keyword and tests (#146)
1 parent b27833a commit fbf5fa9

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

Transpilers/Keywords/Until.psx.ps1

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<#
2+
.SYNOPSIS
3+
until keyword
4+
.DESCRIPTION
5+
The until keyword simplifies event loops.
6+
7+
until will always run at least once, and will run until a condition is true.
8+
.NOTES
9+
until will become a ```do {} while ()``` statement in PowerShell.
10+
.EXAMPLE
11+
{
12+
$x = 0
13+
until ($x == 10) {
14+
$x
15+
$x++
16+
}
17+
} |.>PipeScript
18+
.EXAMPLE
19+
{
20+
until "00:00:05" {
21+
[DateTime]::Now
22+
Start-Sleep -Milliseconds 500
23+
}
24+
} | .>PipeScript
25+
.EXAMPLE
26+
Invoke-PipeScript {
27+
$tries = 3
28+
until (-not $tries) {
29+
"$tries tries left"
30+
$tries--
31+
}
32+
}
33+
#>
34+
[ValidateScript({
35+
$commandAst = $_
36+
return ($commandAst -and
37+
$CommandAst.CommandElements[0].Value -eq 'until' -or
38+
(
39+
$commandAst.CommandElements.Count -ge 2 -and
40+
$commandAst.CommandElements[0].Value -like ':*' -and
41+
$commandAst.CommandElements[1].Value -eq 'until'
42+
)
43+
)
44+
})]
45+
param(
46+
[Parameter(Mandatory,ValueFromPipeline,ParameterSetName='CommandAst')]
47+
[Management.Automation.Language.CommandAst]
48+
$CommandAst
49+
)
50+
51+
process {
52+
$CommandName, $CommandArgs = $commandAst.CommandElements
53+
if ($commandName -like ':*') {
54+
$null, $firstArg, $secondArg = $CommandAst.ArgumentList
55+
} else {
56+
$firstArg, $secondArg = $CommandAst.ArgumentList
57+
}
58+
59+
60+
# If the first arg is a command expression, it becomes do {} while ($firstArg)
61+
if (-not $firstArg -or $firstArg.GetType().Name -notin
62+
'ParenExpressionAst', 'ScriptBlockExpressionAst',
63+
'VariableExpressionAst','MemberExpressionAst','ExpandableStringExpressionAst') {
64+
Write-Error "Until must be followed by a Variable, Member, String, or Parenthesis Expression"
65+
return
66+
}
67+
68+
if ($secondArg -isnot [Management.Automation.Language.ScriptBlockExpressionAst]) {
69+
Write-Error "Until must be followed by a condition and a ScriptBlock"
70+
return
71+
}
72+
73+
$condition = $firstArg
74+
if ($firstArg.GetType().Name -eq 'ParenExpressionAst' -and
75+
$firstArg.Pipeline.PipelineElements.Count -eq 1 -and
76+
$firstArg.Pipeline.PipelineElements[0].Expression -and
77+
$firstArg.Pipeline.PipelineElements[0].Expression.GetType().Name -in
78+
'VariableExpressionAst','MemberExpressionAst','ExpandableStringExpressionAst') {
79+
$condition = $firstArg.Pipeline.PipelineElements[0].Expression
80+
}
81+
elseif ($firstArg.GetType().Name -eq 'ScriptBlockExpressionAst') {
82+
$condition = $firstArg -replace '^\{' -replace '\}$'
83+
}
84+
85+
$conditionScript = [ScriptBlock]::Create($condition)
86+
$LoopScript = $secondArg.Extent.ToString() -replace '^\{' -replace '\}$'
87+
88+
$secondArgScriptBlock = [ScriptBlock]::Create($LoopScript)
89+
90+
$conditionScript = $conditionScript | .>Pipescript
91+
$untilTranspiled = $secondArgScriptBlock | .>Pipescript
92+
$conditionScript =
93+
if ($conditionScript -match '^\(') {
94+
"(-not $conditionScript)"
95+
} else {
96+
"(-not ($conditionScript))"
97+
}
98+
99+
100+
$newScript = @"
101+
$(if ($CommandName -like ':*') { "$CommandName "})do {
102+
$untilTranspiled
103+
} while $conditionScript
104+
"@
105+
106+
[ScriptBlock]::Create($newScript)
107+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
describe "'until' keyword" {
2+
it "Is a loop that runs until the condition is false" {
3+
Invoke-PipeScript {
4+
$x = 0
5+
until ($x -ge 10) {
6+
$x++
7+
}
8+
$x
9+
} | Should -Be 10
10+
}
11+
it 'Can have a loop label' {
12+
Invoke-PipeScript {
13+
$x = 0
14+
:thisUntil until ($x -ge 10) {
15+
$x++
16+
break thisUntil
17+
}
18+
$x
19+
} | Should -Be 10
20+
}
21+
}
22+
23+

0 commit comments

Comments
 (0)