Skip to content

Instantly share code, notes, and snippets.

@ElianFabian
Last active July 16, 2025 14:48
Show Gist options
  • Save ElianFabian/813f924808a0ebf8d10f197fbf0de717 to your computer and use it in GitHub Desktop.
Save ElianFabian/813f924808a0ebf8d10f197fbf0de717 to your computer and use it in GitHub Desktop.
Basic PowerShell to GlovePIE transpiler. For a better debugging experience with AST: https://github.com/lzybkr/ShowPSAst. For more information about GlovePIE: https://github.com/GlovePIEPreservation/GlovePIE/wiki.
<#
NOTES:
----- Something that we could do to make this more powerful would be to first transpile to a low level PowerShell code and then transpile to GlovePIE.
- Numeric types in GlovePIE are int32 and float, so we should use them instead of
other alternatives.
- When we wait inside an if, let's say it's the first line, it will not execute until
the rest of the code is executed or there is a wait.
- If an if has not finished due to a pending wait then the if will be ignored in that execution
- There's an 'int' function that seems to truncate float numbers (this does not convert strings to int).
- There's a 'float' that converts an int to float and convert strings to floats.
- GlovePIE supports hex notation for numbers (eg.g 0x10) but not binary (e.g. 0b10).
#>
# This will be useful for testing
# function Get-DeterministicGuid {
# param (
# [Parameter(Mandatory)]
# [int] $Index,
# [string] $Seed = "MySeed"
# )
# $hashInput = "$Seed-$Index"
# $hashBytes = [System.Text.Encoding]::UTF8.GetBytes($hashInput)
# $md5 = [System.Security.Cryptography.MD5]::Create()
# $guidBytes = $md5.ComputeHash($hashBytes)
# return [guid]::new($guidBytes)
# }
$script:InlinedVariableMap = @{}
# name, time, indentationLevel
$script:WaitTimes = @()
# This is to optimize the GlovePIE output, to avoid adding
# unnecessary wait calls.
$script:LastWaitIndex = -1
$script:IndentationSize = 4
function GetIndentation([uint] $IndentationLevel) {
return ' ' * ($IndentationLevel * $script:IndentationSize)
}
function NewUuid {
return (New-Guid).Guid.Replace('-', '')
}
# Rudimentary way to implement line number transpilation constant
# The caveat of this implementation is that we don't know the line number
# at transpilation time, this is not a big deal actually, but we could not use it
# for inline foreach statements
$lineNumberVariableUuid = "lineNumber_$(NewUuid)"
$GlovePie = @{
FloatMaxValue = '(3.402823 * 10^38)'
FloatMinValue = '(-3.402823 * 10^38)'
FloatEpsilon = '(1.401298 * 10^-45)'
FloatNaN = '(0/0)'
FloatPositiveInfinity = '(1/0)'
FloatNegativeInfinity = '(-1/0)'
FloatE = '2.718282'
FloatPi = '3.141593'
FloatTau = '6.283185'
IntMaxValue = '2147483647'
IntMinValue = '-2147483648'
}
$AliasToFullName = @{
"byte" = "System.Byte"
"sbyte" = "System.SByte"
"short" = "System.Int16"
"ushort" = "System.UInt16"
"int" = "System.Int32"
"int32" = "System.Int32"
"int64" = "System.Int64"
"uint" = "System.UInt32"
"long" = "System.Int64"
"ulong" = "System.UInt64"
"float" = "System.Single"
"double" = "System.Double"
"decimal" = "System.Decimal"
"object" = "System.Object"
"bool" = "System.Boolean"
"char" = "System.Char"
"string" = "System.String"
"void" = "System.Void"
}
# Another option would be: Invoke-Expression "[$Type].FullName"
function ResolveTypeName([string] $Type) {
if ([type]::GetType($Type)) {
return $Type
}
return $AliasToFullName[$Type]
}
function GetTypeCode([string] $Type) {
$resolvedType = ResolveTypeName $Type
switch ($resolvedType) {
'System.String' { 1 }
'System.Int32' { 2 }
'System.Float' { 3 }
'System.Numerics.Vector3' { 4 }
default { 0 }
}
}
# TODO: think more about the null implementation in GlovePIE
function GetVariable([string] $Variable) {
if ($script:InlinedVariableMap.Count -gt 0 -and $script:InlinedVariableMap.ContainsKey($Variable)) {
return $script:InlinedVariableMap.$Variable
}
switch ($Variable) {
# true is the same as 1, except the toString()
'true' { 'true' }
# false is the same as 0, except the toString()
'false' { 'false' }
# null is the same as 0 in GlovePIE
'null' { 'null' }
'GlovePieLineNumber' { $lineNumberVariableUuid }
default {
"var.$Variable"
}
}
}
function GetOperator ([string] $Operator) {
switch ($Operator) {
'Equals' { '=' }
'Plus' { '+' }
'PlusPlus' { '@plus-plus' }
'PostfixPlusPlus' { '@postfix-plus-plus' }
'PlusEquals' { '@plus-equals' }
'Minus' { '-' }
'MinusMinus' { '@minus-minus' }
'PostfixMinusMinus' { '@postfix-minus-minus' }
'MinusEquals' { '@minus-equals' }
'Multiply' { '*' }
'MultiplyEquals' { '@multiply-equals' }
'Divide' { '/' }
'DivideEquals' { '@divide-equals' }
'Rem' { '%' }
'Cgt' { '>' }
'Cge' { '>=' }
'Ilt' { '<' }
'Ile' { '<=' }
'Ceq' { '==' }
'Cne' { '!=' }
'Igt' { '>' }
'Ige' { '>=' }
'Clt' { '<' }
'Cle' { '<=' }
# NOTES: Actually this is not always a good equivalent, but I think
# its useful to be able to use the approximate versions of equality.
'Ieq' { '~=' }
'Ine' { '~!=' }
'Iin' { '@in' }
'Cin' { '@in' }
'Inotin' { '@not-in' }
'Cnotin' { '@not-in' }
'Icontains' { '@contains' }
'Ccontains' { '@contains' }
'Inotcontains' { '@not-contains' }
'Cnotcontains' { '@not-contains' }
'DotDot' { '@dot-dot' }
'Is' { '@is' }
'Isnot' { '@is-not' }
'And' { 'and' }
'Or' { 'or' }
'Xor' { 'xor' }
'Not' { 'not' }
'Band' { '&' }
'Bor' { '|' }
'Shr' { 'shr' }
'Shl' { 'shl' }
'Bnot' { '@bnot' }
'Bxor' { '@bxor' }
default {
"|$($Operator)|"
}
}
}
function GetCommand (
[System.Management.Automation.Language.CommandElementAst[]] $Command,
[uint32] $IndentationLevel
) {
$first = $Command[0]
switch ($first) {
{ $_[0].Value.StartsWith('.\') -or $_[0].Value.StartsWith('./') } {
"Execute '$_'"
}
'Write-Host' {
$rawArguments = $Ast.CommandElements | Select-Object -Skip 1
$arguments = foreach ($element in $rawArguments) {
$result = Invoke-AstTranspilation $element -IndentationLevel $IndentationLevel
if ($result -eq $GlovePie.Null) {
""
}
else {
$result
}
}
"DebugPrint $(($arguments | ForEach-Object { $_ }) -join ' ')"
}
'Start-Sleep' {
$rawArguments = $Ast.CommandElements | Select-Object -Skip 1
# $arguments = foreach ($element in $rawArguments) {
# $result = Invoke-AstTranspilation $element -IndentationLevel $IndentationLevel
# if ($result -eq $GlovePie.Null) {
# "''"
# }
# else {
# $result
# }
# }
$waitTime = Invoke-AstTranspilation $Command[1] -IndentationLevel $IndentationLevel
$wait = @{
name = "wait_$(NewUuid)"
time = $waitTime
indentationLevel = $IndentationLevel
}
$script:WaitTimes += $wait
@(
"var.$($wait.name) = $($wait.time)"
"$(GetIndentation $IndentationLevel)wait var.$($wait.name)"
"$(GetIndentation $IndentationLevel)var.$($wait.name) = 0"
) -join "`n"
}
'pie' {
$value = (Invoke-AstTranspilation -Ast $Command[1] -IndentationLevel $IndentationLevel).EndBlock
[scriptblock]::Create($value).Invoke($IndentationLevel)
}
'pieln' {
$value = (Invoke-AstTranspilation -Ast $Command[1] -IndentationLevel $IndentationLevel).EndBlock
[scriptblock]::Create($value).Invoke($IndentationLevel)
}
'executeOnce' {
$variableName = "skip_$(NewUuid)"
$stringBuilder = [System.Text.StringBuilder]::new()
$script:LastWaitIndex = $script:WaitTimes.Count - 1
$stringBuilder.Append("$(GetIndentation $IndentationLevel)") > $null
$stringBuilder.AppendLine("if not var.$variableName then") > $null
$stringBuilder.AppendLine("$(GetIndentation ($IndentationLevel + 1))var.$variableName = true") > $null
$stringBuilder.Append((Invoke-AstTranspilation -Ast $Command[1].ScriptBlock -IndentationLevel ($IndentationLevel + 1))) > $null
$stringBuilder.Append("$(GetIndentation $IndentationLevel)") > $null
$stringBuilder.AppendLine('endif') > $null
for ($i = 0; $i -lt $script:WaitTimes.Count; $i++) {
$wait = $script:WaitTimes[$i]
if ($i -le $script:LastWaitIndex -and ($wait.indentationLevel -le ($IndentationLevel + 1))) {
if ($IndentationLevel -ne 0) {
continue
}
}
$stringBuilder.Append((GetIndentation $IndentationLevel)) > $null
$stringBuilder.AppendLine("wait var.$($wait.name)") > $null
}
$stringBuilder.ToString()
}
Default {
"?????()"
}
}
}
# TODO: see if there's is shorter way to work with types (I'm lazy to do it now)
function GetTypeCondition([string] $Type, [string] $Value, [switch] $Not) {
if ($Not) {
$notStr = "not "
}
$sanitizedType = ResolveTypeName $Type
switch ($sanitizedType) {
'System.String' {
"$notStr((sign($Value) == 0 and (($Value + 1) - $Value) != 1 and len($Value * 0) > 0) || (len($Value) == 0 and $Value != 0))"
}
'System.Int32' {
"$notStr((($Value + 1) - $Value) == 1 and not (sign($Value) == 0 and ($Value) < 0) and ($Value * 0 + 1)[3] == 0 and len(floor($Value)) == len($Value))"
}
'System.Single' {
"$notStr((($Value + 1) - $Value) == 1 and not (sign($Value) == 0 and ($Value) < 0) and ($Value * 0 + 1)[3] == 0 and len(floor($Value)) != len($Value))"
}
'System.Numerics.Vector3' {
"$notStr(len($Value * 0) == 0 and ($Value * 0 + 1)[3] > 0)"
}
default {
if ($Not) {
'true'
}
else { 'false' }
}
}
}
function ConvertArgToRadiansStr($Arg) {
# return "((($Arg)/360) * $($GlovePie.FloatTau))"
return "(($Arg) * $($GlovePie.FloatTau)/360)"
}
function ConvertArgToDegreesStr($Arg) {
# return "((($Arg)/$($GlovePie.FloatTau)) * 360)"
return "(($Arg) * 360/$($GlovePie.FloatTau))"
}
function GetInvokeMemeber(
[object] $Expression,
[string] $Member,
[bool] $IsStatic,
[object[]] $ArgumentList
) {
if ($ArgumentList) {
$arg0 = $ArgumentList[0]
$arg1 = $ArgumentList[1]
$arg2 = $ArgumentList[2]
}
$sanitizedMember = $member.Trim("'").Trim('"')
# TODO: we could check that that argument count is valid
$type = ResolveTypeName $Expression.FullName
if (-not $IsStatic) {
$result = switch ($sanitizedMember) {
'Equals' {
# The left doesn't support parenthesis in GlovePIE
"$Expression == ($arg0)"
}
'ToString' {
"($Expression + '')"
}
'GetType' {
"1 * $(GetTypeCondition -Type 'System.String' -Value $Expression) + 2 * $(GetTypeCondition -Type 'System.Int32' -Value $Expression) + 3 * $(GetTypeCondition -Type 'System.Single' -Value $Expression) + 4 * $(GetTypeCondition -Type 'System.Numerics.Vector3' -Value $Expression)"
}
'Length' {
"abs($Expression)"
}
'LengthSquared' {
# "(($Expression)[1])^2 + (($Expression)[2])^2 + (($Expression)[3])^2)"
"(abs($Expression) * abs($Expression))"
}
default {
# Write-Error "$Expression.$Member not supported"
# "($Expression.$Member() /* not supported */)"
switch ($type) {
'System.Numerics.Vector3' {
switch ($sanitizedMember) {
'Length' {
"sqrt((($arg1)[1] - ($arg0)[1])^2 + (($arg1)[2] - ($arg0)[2])^2 + (($arg1)[3] - ($arg0)[3])^2)"
}
'LengthSquared' {
"(($arg1)[1] - ($arg0)[1])^2 + (($arg1)[2] - ($arg0)[2])^2 + (($arg1)[3] - ($arg0)[3])^2)"
}
}
}
}
}
}
return $result
}
$result = switch ($type) {
'System.Numerics.Vector3' {
switch ($sanitizedMember) {
# TODO: Add the other functions
'Abs' {
"[abs(($arg0)[1]), abs(($arg0)[2]), abs(($arg0)[3])]"
}
'Add' {
"(($arg0) + ($arg1))"
}
'Cross' {
"(($arg0) cross ($arg1))"
}
'Dot' {
"(($arg0) dot ($arg1))"
}
'Equals' {
"(($arg0) == ($arg1))"
}
'Distance' {
"sqrt((($arg1)[1] - ($arg0)[1])^2 + (($arg1)[2] - ($arg0)[2])^2 + (($arg1)[3] - ($arg0)[3])^2)"
}
'DistanceSquared' {
"(($arg1)[1] - ($arg0)[1])^2 + (($arg1)[2] - ($arg0)[2])^2 + (($arg1)[3] - ($arg0)[3])^2)"
}
'Divide' {
if ($arg1 -is [float] -or $arg1 -is [int]) {
"(($arg0) / ($arg1))"
}
else {
"[(($arg1)[1] / ($arg0)[1]), (($arg1)[2] / ($arg0)[2]), (($arg1)[3] / ($arg0)[3])]"
}
}
'Multiply' {
"[(($arg1)[1] * ($arg0)[1]), (($arg1)[2] * ($arg0)[2]), (($arg1)[3] * ($arg0)[3])]"
}
'Negate' {
"not($arg0)"
}
'new' {
"[($arg0), ($arg1), ($arg2)]"
}
'Normalize' {
"(($arg0) / abs($arg0))"
}
'Subtract' {
"(($arg0) - ($arg1))"
}
default {
Write-Error "[$Type] not supported"
"([$Type]::$Member() /* not supported */)"
}
}
}
'System.String' {
switch ($sanitizedMember) {
'IsNullOrEmpty' {
"len($arg0) == 0"
}
'Equals' {
"$arg0 == ($arg1)"
}
default {
Write-Error "[$Type] not supported"
"([$Type]::$Member() /* not supported */)"
}
}
}
'System.Int32' {
switch ($sanitizedMember) {
'Abs' {
"abs($arg0)"
}
'Clamp' {
"min(max(($arg0), ($arg1)), ($arg2))"
}
'CopySign' {
"abs($arg0) * sign($arg1)"
}
'CreateChecked' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'CreateSaturating' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'CreateTruncating' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'DivRem' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'Equals' {
"($arg0) == ($arg1)"
}
'IsEvenInteger' {
"not (odd($arg0))"
}
'IsNegative' {
"($arg0 < 0)"
}
'IsOddInteger' {
"odd($arg0)"
}
'IsPositive' {
"($arg0 > 0)"
}
'IsPow2' {
"(($arg0) != 0 and (($arg0) & (($arg0) - 1)) == 0)"
}
'LeadingZeroCount' {
"(31 - floor(log2(($arg0) | 1)))"
}
'Parse' {
"int(float($arg0))"
}
default {
Write-Error "[$Type] not supported"
"([$Type]::$Member() /* not supported */)"
}
}
}
'System.Single' {
# NOTE: SinPi and similar functions won't return accurate results when transpiled to GlovePIE
# due to lack of precision. Keep this in mind for cases when the result is actually infinite.
# Maybe we could workaround this by checking the given value, if it is something like
# 0.5, 1, 1.5, 2, ... We could just return the exact value.
switch ($sanitizedMember) {
'Abs' {
"abs($arg0)"
}
'Acos' {
ConvertArgToRadiansStr "acos($arg0) rad"
}
'Acosh' {
ConvertArgToRadiansStr "acosh($arg0) rad"
}
'AcosPi' {
"$(ConvertArgToRadiansStr "acos($arg0) rad")/$($GlovePie.FloatPi)"
}
'Asin' {
ConvertArgToRadiansStr "asin($arg0) rad"
}
'Asinh' {
ConvertArgToRadiansStr "asinh($arg0) rad"
}
'AsinPi' {
"$(ConvertArgToRadiansStr "asin($arg0) rad")/$($GlovePie.FloatPi)"
}
'Atan' {
ConvertArgToRadiansStr "atan($arg0) rad"
}
'Atan2' {
ConvertArgToRadiansStr "atan2(($arg0), ($arg1)) rad"
}
'Atan2Pi' {
"$(ConvertArgToRadiansStr "atan2(($arg0), ($arg1)) rad")/$($GlovePie.FloatPi)"
}
'Atanh' {
ConvertArgToRadiansStr "atanh($arg0) rad"
}
'AtanPi' {
"$(ConvertArgToRadiansStr "atan($arg0) rad")/$($GlovePie.FloatPi)"
}
'BitDecrement' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'BitIncrement' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'Cbrt' {
"($arg0)^(1/3)"
}
'Ceiling' {
"ceil($arg0)"
}
'Clamp' {
"min(max(($arg0), ($arg1)), ($arg2))"
}
'CopySign' {
"abs($arg0) * sign($arg1)"
}
'Cos' {
"cos($($arg0) rad)"
}
'Cosh' {
"cosh($($arg0) rad)"
}
'CosPi' {
"(cos($($arg0) * $($GlovePie.FloatPi) rad))"
}
'CreateChecked' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'CreateSaturating' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'CreateTruncating' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'DegreesToRadians' {
ConvertArgToRadiansStr $arg0
}
'Equals' {
"($arg0) == ($arg1)"
}
'Exp' {
"exp($arg0)"
}
'Exp10' {
"(10^($arg0))"
}
'Exp10M1' {
"(10^($arg0) - 1)"
}
'Exp2' {
"(2^($arg0))"
}
'Exp2M1' {
"(2^($arg0) - 1)"
}
'ExpM1' {
"(exp($arg0) - 1)"
}
'Floor' {
"floor($arg0)"
}
'FusedMultiplyAdd' {
# This is not an accurate implementation of FusedMultiplyAdd
"((($arg0) * ($arg1)) + ($arg2)) /* ~FusedMultiplyAdd */"
}
'Hypot' {
"sqrt(($arg0) * ($arg0) + ($arg1) * ($arg1))"
}
'Ieee754Remainder' {
"(($arg0) - ($arg1) * round(($arg0) / ($arg1)))"
}
'ILogB' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'IsEvenInteger' {
"not (odd($arg0))"
}
'IsFinite' {
"(not isInfinite($arg0) and not(isNan($arg0)))"
}
'IsInfinity' {
"isInfinite($arg0)"
}
'IsInteger' {
"(truncate($arg0) == ($arg0))"
}
'IsNaN' {
"isNaN($arg0)"
}
'IsNegative' {
"($arg0 < 0)"
}
'IsNegativeInfinity' {
"($arg0 == $($GlovePie.FloatNegativeInfinity))"
}
'IsOddInteger' {
"odd($arg0)"
}
'IsPositive' {
"($arg0 > 0)"
}
'IsPositiveInfinity' {
"($arg0 == $($GlovePie.FloatPositiveInfinity))"
}
'IsPow2' {
"(($arg0) != 0 and (($arg0) & (($arg0) - 1)) == 0)"
}
'IsRealNumber' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'IsSubnormal' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'Lerp' {
$a = $arg0
$b = $arg1
$t = $arg2
"(($a) + (($b) - ($a))*($t))"
}
'Log' {
if ($ArgumentList.Count -eq 2) {
"logN($arg0, $arg1)"
}
elseif ($ArgumentList.Count -eq 1) {
"ln($arg0)"
}
else {
Write-Error "log does not supporte parameter count of $($ArgumentList.Count)"
"ln(error)"
}
}
'Log10' {
"log10($arg0)"
}
'Log10P1' {
"log10(($arg0) + 1)"
}
'Log2' {
"log2($arg0)"
}
'Log2P1' {
"log2(($arg0) + 1)"
}
'LogP1' {
"lnXP1($arg0)"
}
#region NOTE: [float]::Max*() and [float]::Min*() aren't exactly equal to max() and min()
'Max' {
"max(($arg0), ($arg1) /* Max */)"
}
'MaxMagnitude' {
"max(($arg0), ($arg1) /* MaxMagnitude */)"
}
'MaxMagnitudeNumber' {
"max(($arg0), ($arg1) /* MaxMagnitudeNumber */)"
}
'MaxNumber' {
"max(($arg0), ($arg1) /* MaxNumber */)"
}
'Min' {
"min(($arg0), ($arg1))"
}
'MinMagnitude' {
"min(($arg0), ($arg1) /* MinMagnitude */)"
}
'MinMagnitudeNumber' {
"min(($arg0), ($arg1) /* MinMagnitudeNumber */)"
}
'MinNumber' {
"min(($arg0), ($arg1) /* MinNumber */)"
}
#endregion
'Parse' {
"float($arg0)"
}
'Pow' {
"($arg0)^($arg1)"
}
'RadiansToDegrees' {
ConvertArgToDegreesStr $arg0
}
'ReciprocalEstimate' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'ReciprocalSqrtEstimate' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'ReferenceEquals' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'RootN' {
"(($arg0)^($arg1))"
}
'Round' {
"round($arg0)"
}
'ScaleB' {
"(($arg0) * 2^($arg1))"
}
'Sign' {
"sign($arg0)"
}
'Sin' {
"sin($arg0 rad)"
}
'SinCos' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'SinCosPi' {
Write-Error "[$Expression]::$Member not supported"
"($Member() /* not supported */)"
}
'Sinh' {
"sinh($arg0 rad)"
}
'SinPi' {
"sin($arg0 rad * $($GlovePie.FloatPi))"
}
'Sqrt' {
"sqrt($arg0)"
}
'Tan' {
"tan($arg0 rad)"
}
'Tanh' {
"tanh($arg0 rad)"
}
'TanPi' {
"tan($arg0 rad * $($GlovePie.FloatPi))"
}
'Truncate' {
"trunc($arg0)"
}
'TryParse' {
Write-Error "[float]::SinCosPi not supported"
"([$Type]::$Member() /* not supported */)"
}
default {
Write-Error "[$Type] not supported"
"([$Type]::$Member() /* not supported */)"
}
}
}
default {
Write-Error "Type [$Expression] not supported"
"($Expression::$Member() /* not supported */)"
}
}
return $result
}
# Low level implementation of 'elseif', actually we don't need this because
# GlovePIE supports elseif natively, but we'll keep this for future reference.
# function Invoke-IfStatementTranspilation-Old {
# [OutputType([string])]
# param (
# [object[]] $IfSentenceTuple,
# [AllowNull()]
# [object] $ElseClause,
# [string] $IndentationLevel
# )
# if ($IfSentenceTuple.Count -eq 0) {
# return
# }
# $sentenceList = [System.Collections.Generic.Queue[object]]::new($IfSentenceTuple)
# $sentenceTuple = $sentenceList.Dequeue()
# $pipelineAst = $sentenceTuple.Item1
# $statementBlock = $sentenceTuple.Item2
# $stringBuilder = [System.Text.StringBuilder]::new()
# $stringBuilder.Append($IndentationLevel) > $null
# $stringBuilder.Append('if (') > $null
# $conditionStr = Invoke-AstTranspilation $pipelineAst -IndentationLevel "$IndentationLevel"
# $stringBuilder.Append($conditionStr) > $null
# $stringBuilder.AppendLine(') {') > $null
# $blockStr = Invoke-AstTranspilation $statementBlock -IndentationLevel "$IndentationLevel "
# $stringBuilder.AppendLine($blockStr) > $null
# $stringBuilder.Append($IndentationLevel) > $null
# $stringBuilder.Append('} ') > $null
# if ($sentenceList.Count -eq 0 -and $ElseClause) {
# $stringBuilder.AppendLine('else {') > $null
# $elseClauseStr = Invoke-AstTranspilation $ElseClause -IndentationLevel "$IndentationLevel "
# $stringBuilder.AppendLine("$elseClauseStr") > $null
# $stringBuilder.Append("$IndentationLevel") > $null
# $stringBuilder.Append('}') > $null
# return $stringBuilder.ToString()
# }
# if ($sentenceList.Count -gt 0) {
# $stringBuilder.AppendLine('else {') > $null
# $elseClauseStr = Invoke-IfStatementTranspilation -IfSentenceTuple ($sentenceList.ToArray()) -ElseClause $ElseClause -IndentationLevel "$IndentationLevel "
# $stringBuilder.AppendLine($elseClauseStr) > $null
# $stringBuilder.Append($IndentationLevel) > $null
# $stringBuilder.Append('}') > $null
# }
# return $stringBuilder.ToString()
# }
# NOTES: I prefer using curly braces, but at least on GlovePIE 0.43 it
# shows error for specific cases where endif does not.
function Invoke-IfStatementTranspilation {
param (
[object[]] $IfSentenceTuple,
[AllowNull()]
[object] $ElseClause,
[uint32] $IndentationLevel
)
if ($IfSentenceTuple.Count -eq 0) {
return
}
$sentenceList = [System.Collections.Generic.Queue[object]]::new($IfSentenceTuple)
$sentenceTuple = $sentenceList.Dequeue()
$pipelineAst = $sentenceTuple.Item1
$statementBlock = $sentenceTuple.Item2
$script:LastWaitIndex = $script:WaitTimes.Count - 1
$stringBuilder = [System.Text.StringBuilder]::new()
$stringBuilder.Append((GetIndentation $IndentationLevel)) > $null
$stringBuilder.Append('if ') > $null
$conditionStr = Invoke-AstTranspilation $pipelineAst #-IndentationLevel $IndentationLevel
$stringBuilder.Append($conditionStr) > $null
$stringBuilder.AppendLine(' then') > $null
$blockStr = Invoke-AstTranspilation $statementBlock -IndentationLevel ($IndentationLevel + 1)
$stringBuilder.Append($blockStr) > $null
$stringBuilder.Append((GetIndentation $IndentationLevel)) > $null
while ($sentenceList.Count -gt 0) {
$sentenceTuple = $sentenceList.Dequeue()
$pipelineAst = $sentenceTuple.Item1
$statementBlock = $sentenceTuple.Item2
# $script:LastWaitIndex = $script:WaitTimes.Count - 1
$conditionStr = Invoke-AstTranspilation $pipelineAst #-IndentationLevel $IndentationLevel
$stringBuilder.AppendLine("else if $conditionStr then") > $null
$blockStr = Invoke-AstTranspilation $statementBlock -IndentationLevel ($IndentationLevel + 1)
$stringBuilder.Append($blockStr) > $null
$stringBuilder.Append((GetIndentation $IndentationLevel)) > $null
}
if ($ElseClause) {
# $script:LastWaitIndex = $script:WaitTimes.Count - 1
$stringBuilder.AppendLine('else') > $null
$elseClauseStr = Invoke-AstTranspilation $ElseClause -IndentationLevel ($IndentationLevel + 1)
$stringBuilder.Append("$elseClauseStr") > $null
$stringBuilder.Append((GetIndentation $IndentationLevel)) > $null
}
$stringBuilder.Append('endif') > $null
$stringBuilder.AppendLine() > $null
for ($i = 0; $i -lt $script:WaitTimes.Count; $i++) {
$wait = $script:WaitTimes[$i]
if ($i -le $script:LastWaitIndex -and ($wait.indentationLevel -le ($IndentationLevel + 1))) {
# NOTE: Adding this makes it works perfectly (At least for now, but I have to understand this better)
if ($IndentationLevel -ne 0) {
continue
}
}
$stringBuilder.Append((GetIndentation $IndentationLevel)) > $null
$stringBuilder.AppendLine("wait var.$($wait.name)") > $null
}
return $stringBuilder.ToString()
}
function Invoke-AstTranspilation {
[OutputType([string])]
param (
[System.Management.Automation.Language.ast] $Ast,
# TODO: Instead of indentation we could use Depth as int, that way we'll get:
# - Easy to check the level of nesting
# - To be able to change the indentation size with a variable
[uint32] $IndentationLevel = 0
)
switch ($Ast.GetType().Name) {
'ScriptBlockExpressionAst' {
return $Ast.ScriptBlock
}
'ScriptBlockAst' {
return Invoke-AstTranspilation $Ast.EndBlock -IndentationLevel $IndentationLevel
}
'StatementBlockAst' {
if ($Ast.Statements.Count -eq 0) {
return
}
$stringBuilder = [System.Text.StringBuilder]::new()
foreach ($statement in $Ast.Statements) {
$stringBuilder.AppendLine((Invoke-AstTranspilation $statement -IndentationLevel $IndentationLevel)) > $null
}
return $stringBuilder.ToString()
}
'NamedBlockAst' {
if ($Ast.Statements.Count -eq 0) {
return
}
$stringBuilder = [System.Text.StringBuilder]::new()
foreach ($statement in $Ast.Statements) {
$stringBuilder.AppendLine((Invoke-AstTranspilation $statement -IndentationLevel $IndentationLevel)) > $null
}
return $stringBuilder.ToString()
}
'AssignmentStatementAst' {
$left = Invoke-AstTranspilation -Ast $Ast.Left -IndentationLevel $IndentationLevel
$operator = GetOperator $Ast.Operator
if ($Ast.Right.GetType().FullName -eq 'System.Management.Automation.Language.PipelineAst') {
$right = (Invoke-AstTranspilation $Ast.Right.PipelineElements[0] -IndentationLevel $IndentationLevel).Trim()
}
else {
$right = Invoke-AstTranspilation $Ast.Right.Expression -IndentationLevel $IndentationLevel
}
$result = switch ($operator) {
'@plus-equals' { "$left = $left + $right" }
'@minus-equals' { "$left = $left - $right" }
'@multiply-equals' { "$left = $left * $right" }
'@divide-equals' { "$left = $left / $right" }
default {
"$left = $right"
}
}
return (GetIndentation $IndentationLevel) + $result
}
'ConvertExpressionAst' {
# I don't know what to do with the type, # so we'll just return the expression
# But if in future we need to do something with the type, I guess we should
# check for ConvertExpressionAst in AssignmentStatementAst
return @(
"/* $($Ast.Type) */"
Invoke-AstTranspilation -Ast $Ast.Child -IndentationLevel $IndentationLevel
) -join ' '
}
'IfStatementAst' {
return Invoke-IfStatementTranspilation -IfSentenceTuple $Ast.Clauses -ElseClause $Ast.ElseClause -IndentationLevel $IndentationLevel
}
'SwitchStatementAst' {
# Flags (SwitchFlags) -CaseSensitive with no flags this must be converted to if, with flag with elseif
# Condition PipelineBaseAst
# Clauses (StatementBlockAst[])
# Deafult (StatementBlockAst)
}
'PipelineAst' {
$stringBuilder = [System.Text.StringBuilder]::new()
foreach ($element in $Ast.PipelineElements) {
$stringBuilder.Append((Invoke-AstTranspilation $element -IndentationLevel $IndentationLevel)) > $null
}
return (GetIndentation $IndentationLevel) + $stringBuilder.ToString()
}
'CommandExpressionAst' {
return Invoke-AstTranspilation $Ast.Expression -IndentationLevel $IndentationLevel
}
'UnaryExpressionAst' {
$operator = GetOperator $Ast.TokenKind
$operand = Invoke-AstTranspilation -Ast $Ast.Child -IndentationLevel $IndentationLevel
$result = switch ($operator) {
'@plus-plus' { "$operand = $operand + 1" }
'@postfix-plus-plus' { "$operand = $operand + 1" }
'@minus-minus' { "$operand = $operand - 1" }
'@postfix-minus-minus' { "$operand = $operand - 1" }
'@bnot' { "-($operand + 1)" }
'+' { $operand }
default {
"($operator $operand)"
}
}
return (GetIndentation $IndentationLevel) + $result
}
'BinaryExpressionAst' {
$left = Invoke-AstTranspilation $Ast.Left -IndentationLevel $IndentationLevel
$right = Invoke-AstTranspilation $Ast.Right -IndentationLevel $IndentationLevel
$operator = GetOperator $Ast.Operator
if ($operator -eq '==' -or $operator -eq '~=') {
if ($left -is [System.Management.Automation.Language.ITypeName] -and $right -is [System.Management.Automation.Language.ITypeName]) {
$leftType = ResolveTypeName $left.FullName
$rightType = ResolveTypeName $right.FullName
$leftCode = GetTypeCode $leftType
$rightCode = GetTypeCode $rightType
return "$leftCode == $rightCode"
}
elseif ($left -is [System.Management.Automation.Language.ITypeName]) {
$type = ResolveTypeName $left.FullName
$code = GetTypeCode $type
return "$code == $right"
}
elseif ($right -is [System.Management.Automation.Language.ITypeName]) {
$type = ResolveTypeName $right.FullName
$code = GetTypeCode $type
return "$left == $code"
}
}
$result = switch ($operator) {
'@bxor' {
"($left | $right) & -(($left & $right) + 1)"
}
'@in' {
if ($right -is [array]) {
# "InSet(($left),$($right -join ','))"
"($left) is in ($($right -join ','))"
}
elseif ($right -is [hashtable]) {
"($($right.Start)) <= ($left) and ($left) <= ($($right.End))"
}
else {
"in // Could not transpile: $right"
}
}
'@not-in' {
if ($right -is [array]) {
# "(not InSet(($left),$($right -join ',')))"
"not (($left) is in ($($right -join ',')))"
}
elseif ($right -is [hashtable]) {
"($left) < ($($right.Start)) or ($($right.End)) < ($left)"
}
else {
"in // Could not transpile: $right"
}
}
'@contains' {
# "InSet(($right),$($left -join ','))"
"($left) is in ($($right -join ','))"
}
'@not-contains' {
# "(not InSet(($right),$($left -join ',')))"
"not (($left) is in ($($right -join ',')))"
}
'@dot-dot' {
@{
Start = $left
End = $right
}
}
'@is' {
GetTypeCondition -Type $right.Name -Value $left
}
'@is-not' {
GetTypeCondition -Type $right.Name -Value $left -Not
}
default {
"($left) $operator ($right)"
}
}
return $result
}
'VariableExpressionAst' {
$name = $Ast.VariablePath.UserPath
return GetVariable $name
}
'ConstantExpressionAst' {
# The double toString() of '125.0' is '125', which it's a problem
# since we want to explictly indicate that is a float.
if ($Ast.Value -is [double]) {
$doubleStr = $Ast.Value.ToString([System.Globalization.CultureInfo]::InvariantCulture)
if (-not $doubleStr.Contains('.')) {
return "$($Ast.Value).0"
}
}
return $Ast.Value
}
'StringConstantExpressionAst' {
switch ($Ast.StringConstantType) {
'BareWord' {
return $Ast.Value
}
'DoubleQuoted' {
return '"' + $Ast.Value + '"'
}
'SingleQuoted' {
return "'" + $Ast.Value + "'"
}
'DoubleQuotedHereString' {
# TODO: analyze this case
return $Ast.Value
}
'SingleQuotedHereString' {
# TODO: analyze this case
return $Ast.Value
}
}
}
'ExpandableStringExpressionAst' {
$start = $Ast.Extent.StartLineNumber - 1
$quote = switch ($Ast.StringConstantType) {
'BareWord' { '' }
'DoubleQuoted' { '"' }
'SingleQuoted' { "'" }
'DoubleQuotedHereString' {
# TODO: analyze this case
''
}
'SingleQuotedHereString' {
# TODO: analyze this case
''
}
}
$stringBuilder = [System.Text.StringBuilder]::new($Ast.Value)
for ($i = $Ast.NestedExpressions.Count - 1; $i -gt -1; $i--) {
$expression = $Ast.NestedExpressions[$i]
switch ($expression.GetType().Name) {
'SubExpressionAst' {
# In some cases like 'Write-Host "not 15: $(-bnot 15)"' there is extra space on the left, I don't know why
$expressionStr = (Invoke-AstTranspilation -Ast $expression.SubExpression -IndentationLevel $IndentationLevel).Trim()
$rawExpression = $expression.ToString()
$start = $expression.SubExpression.Extent.StartColumnNumber - $Ast.Extent.StartColumnNumber - 3
$length = $rawExpression.Length
$stringBuilder.Replace("$rawExpression", "$quote + ($($expressionStr)) + $quote", $start, $length) > $null
}
'VariableExpressionAst' {
$variable = Invoke-AstTranspilation -Ast $expression -IndentationLevel $IndentationLevel
$start = $expression.Extent.StartColumnNumber - $Ast.Extent.StartColumnNumber - 1
$length = $expression.VariablePath.UserPath.Length + 1
$stringBuilder.Replace("`$$($expression.VariablePath.UserPath)", "$quote + $variable + $quote", $start, $length) > $null
}
}
}
$stringBuilder.Insert(0, $quote) > $null
$stringBuilder.Append($quote) > $null
return $stringBuilder.ToString()
}
'ArrayExpressionAst' {
return $Ast.SubExpression.Statements[0].PipelineElements[0].Expression.Elements | ForEach-Object {
Invoke-AstTranspilation -Ast $_ -IndentationLevel $IndentationLevel
}
}
# 'ArrayLiteralAst' {
# return $Ast.Elements | ForEach-Object {
# Invoke-AstTranspilation -Ast $_ -IndentationLevel $IndentationLevel
# }
# }
'SubExpressionAst' {
return Invoke-AstTranspilation -Ast $Ast.SubExpression -IndentationLevel $IndentationLevel
}
'ParenExpressionAst' {
return '(' + (Invoke-AstTranspilation -Ast $Ast.Pipeline) + ')'
}
'MemberExpressionAst' {
$expression = Invoke-AstTranspilation -Ast $Ast.Expression -IndentationLevel $IndentationLevel
$member = Invoke-AstTranspilation -Ast $Ast.Member -IndentationLevel $IndentationLevel
$sanitizedMember = $member.Trim("'").Trim('"')
$type = ResolveTypeName $expression.FullName
$result = if ($Ast.Static) {
switch ($type) {
'System.Numerics.Vector3' {
switch ($sanitizedMember) {
'One' {
'[1, 1, 1]'
}
'Zero' {
"[0, 0, 0]"
}
'UnitX' {
"[1, 0, 0]"
}
'UnitY' {
"[0, 1, 0]"
}
'UnitZ' {
"[0, 0, 1]"
}
}
}
'System.String' {
switch ($sanitizedMember) {
'Empty' {
"''"
}
}
}
'System.Int32' {
switch ($sanitizedMember) {
'MaxValue' { $GlovePie.IntMaxValue }
'MinValue' { $GlovePie.IntMinValue }
default {
Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
'null'
}
}
}
'System.Single' {
switch ($sanitizedMember) {
'MaxValue' { $GlovePie.FloatMaxValue }
'MinValue' { $GlovePie.FloatMinValue }
'Epsilon' { $GlovePie.FloatEpsilon }
'NaN' { $GlovePie.FloatNaN }
'PositiveInfinity' { $GlovePie.FloatPositiveInfinity }
'NegativeInfinity' { $GlovePie.FloatNegativeInfinity }
'E' { $GlovePie.FloatE }
'Pi' { $GlovePie.FloatPi }
'Tau' { $GlovePie.FloatTau }
default {
Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
'null'
}
}
}
}
}
else {
switch ($type) {
'System.String' {
switch ($member) {
'Length' {
"len($expression)"
}
default {
Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
'null'
}
}
}
'System.Numerics.Vector3' {
switch ($member) {
'X' {
"($expression)[1]"
}
'Y' {
"($expression)[2]"
}
'Z' {
"($expression)[3]"
}
default {
Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
'null'
}
}
}
default {
# NOTE: since it's not possible to know the type of a variable (at least yet) we allow to call
# any property from any kind of object.
# TODO: X,Y, and Z are also setters, so we should implement that as well
switch ($member) {
'Length' {
"len($expression)"
}
'X' {
"($expression)[1]"
}
'Y' {
"($expression)[2]"
}
'Z' {
"($expression)[3]"
}
default {
Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
'null'
}
}
}
}
# switch ($expression.GetType().Name) {
# 'string' {
# switch ($member) {
# 'Length' {
# "len($expression)"
# }
# default {
# Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
# 'null'
# }
# }
# }
# default {
# Write-Error "Unsupported member '$sanitizedMember' for type '$type'"
# 'null'
# }
# }
}
return $result
}
'TypeExpressionAst' {
return $Ast.TypeName
}
'InvokeMemberExpressionAst' {
$expression = Invoke-AstTranspilation -Ast $Ast.Expression -IndentationLevel $IndentationLevel
$member = Invoke-AstTranspilation -Ast $Ast.Member -IndentationLevel $IndentationLevel
$sanitizedMember = $member.Trim("'").Trim('"')
$arguments = $Ast.Arguments | Where-Object { $_ } | ForEach-Object {
Invoke-AstTranspilation -Ast $_ -IndentationLevel $IndentationLevel
}
return GetInvokeMemeber -Expression $expression -IsStatic $Ast.Static -Member $sanitizedMember -ArgumentList $arguments
}
'CommandAst' {
$result = switch ($Ast.InvocationOperator) {
'Unknown' { GetCommand $Ast.CommandElements -IndentationLevel $IndentationLevel }
'Ampersand' {
$stringBuilder = [System.Text.StringBuilder]::new()
foreach ($element in $Ast.CommandElements) {
$expressionStr = Invoke-AstTranspilation -Ast $element -IndentationLevel $IndentationLevel
if (-not $expressionStr.StartsWith("'") -and -not $expressionStr.StartsWith('"')) {
$expressionStr = "'$expressionStr'"
}
$stringBuilder.Append($expressionStr) > $null
}
"Execute $($stringBuilder.ToString())"
}
default {
Write-Warning "Unsupported operator: '$($Ast.InvocationOperator)'"
}
}
# NOTE: We don't want to add the indentation to better control it from the 'pie' scriptblock argument
if ($Ast.CommandElements -and $Ast.CommandElements[0].Value -in @('pie', 'executeOnce')) {
return $result
}
return $result
}
'CommandParameterAst' {
# TODO: Implement
# ParameterName
# Argument
}
'FunctionDefinitionAst' {
# TODO: think of future implementation
Write-Warning "Cannot process function '$($Ast.Name)'"
}
'ForEachStatementAst' {
$stringBuilder = [System.Text.StringBuilder]::new()
$variableName = $Ast.Variable.VariablePath.UserPath
$enumerable = Invoke-AstTranspilation -Ast $Ast.Condition.PipelineElements[0].Expression -IndentationLevel $IndentationLevel
if ($enumerable -is [array]) {
$enumerator = $enumerable
}
elseif ($enumerable -is [hashtable]) {
if ($enumerable.Start.ToString().StartsWith("'")) {
$quote = "'"
}
elseif ($enumerable.Start.ToString().StartsWith('"')) {
$quote = '"'
}
$start = $enumerable.Start.ToString().Trim("'").Trim('"')
$end = $enumerable.End.ToString().Trim("'").Trim('"')
$enumerator = $start..$end
}
else {
Write-Error "Unsupported enumerable type in foreach-statement: $_"
return ''
}
foreach ($element in $enumerator) {
$sanitizedElement = if ($element.GetType().FullName -eq 'System.Char') {
"'$element'"
}
else {
$element
}
$script:InlinedVariableMap.$variableName = $sanitizedElement
$body = Invoke-AstTranspilation -Ast $Ast.Body -IndentationLevel $IndentationLevel
$stringBuilder.Append($body) > $null
}
$script:InlinedVariableMap.Remove($variableName)
return $stringBuilder.ToString()
}
'IndexExpressionAst' {
$target = Invoke-AstTranspilation -Ast $Ast.Target -IndentationLevel $IndentationLevel
$index = Invoke-AstTranspilation -Ast $Ast.Index -IndentationLevel $IndentationLevel
$sanitizedIndex = switch ($index.GetType().FullName) {
'System.Int32' {
$index + 1
}
'System.Collections.Hashtable' {
"$($index.Start + 1)..$($index.End + 1)"
}
default {
Write-Error "Index is not supported for type '$_'"
}
}
return "$target[$sanitizedIndex]"
}
'ExitStatementAst' {
# 'ExitProgram' does not exist immediately, we need to wait a little.
# Since we've implemented sync waits we can do this very easily.
if ($IndentationLevel -eq 0) {
return @(
(GetIndentation $IndentationLevel) + "ExitProgram"
(GetIndentation $IndentationLevel) + "wait 1"
) -join "`n"
}
$wait = @{
name = "wait_$(NewUuid)"
time = 1
indentationLevel = $IndentationLevel
}
$script:WaitTimes += $wait
return @(
(GetIndentation $IndentationLevel) + "var.$($wait.name) = $($wait.time)"
(GetIndentation $IndentationLevel) + 'ExitProgram'
(GetIndentation $IndentationLevel) + "wait var.$($wait.name)"
(GetIndentation $IndentationLevel) + "var.$($wait.name) = 0"
) -join "`n"
}
'ThrowStatementAst' {
if ($Ast.Pipeline) {
$message = Invoke-AstTranspilation -Ast $Ast.Pipeline
}
else {
$message = "''"
}
if ($IndentationLevel -eq 0) {
return @(
(GetIndentation $IndentationLevel) + "DebugPrint '❌ ERROR at line $lineNumberVariableUuid`: ' + $message"
(GetIndentation $IndentationLevel) + "ExitProgram"
(GetIndentation $IndentationLevel) + "wait 1"
) -join "`n"
}
$wait = @{
name = "wait_$(NewUuid)"
time = 1
indentationLevel = $IndentationLevel
}
$script:WaitTimes += $wait
return @(
(GetIndentation $IndentationLevel) + "DebugPrint '❌ ERROR at line $lineNumberVariableUuid`: ' + $message"
(GetIndentation $IndentationLevel) + "var.$($wait.name) = $($wait.time)"
(GetIndentation $IndentationLevel) + 'ExitProgram'
(GetIndentation $IndentationLevel) + "wait var.$($wait.name)"
(GetIndentation $IndentationLevel) + "var.$($wait.name) = 0"
) -join "`n"
}
default {
Write-Warning "Unknown type: $($_.GetType().Name) from $_"
}
}
}
$scriptPath = "$PSScriptRoot/Test.ps1"
$rawContent = Get-Content -LiteralPath "$scriptPath" -Raw
$ast = [scriptblock]::Create($rawContent).Ast
$result = Invoke-AstTranspilation $ast
if (-not $result) {
$result = "// Could not transpile`n"
}
else {
$lineNumber = 0
$result = ($result -split "`n" | ForEach-Object {
$lineNumber++
$_.Replace($lineNumberVariableUuid, $lineNumber)
}
) -join "`n"
}
New-Item -Path "$PSScriptRoot/Test.ps1.PIE" -Value $result -Force
# Stop-Process -Name GlovePIE -Force -ErrorAction SilentlyContinue
# & "C:\Users\empresa\Documents\Elian\Proyectos\GlovePIE-master\GlovePIE-0.43\GlovePIE.exe" "$PSScriptRoot/Test.ps1.PIE"
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "."
$watcher.Filter = "Test.ps1"
$watcher.NotifyFilter = [System.IO.NotifyFilters]'LastWrite'
$watcher.EnableRaisingEvents = $true
$jobUuid = "0d7eb6dc-c017-480e-a75d-05f75ed8c179"
Unregister-Event -SourceIdentifier $jobUuid -ErrorAction SilentlyContinue
Remove-Job -Name $jobUuid -Force -ErrorAction SilentlyContinue
Register-ObjectEvent -InputObject $watcher -EventName Changed -SourceIdentifier $jobUuid -Action {
& .\Ast.ps1
} | Out-Null
Write-Host "Test.ps1.PIE will be updated everytime the Test.ps1 file is saved" -ForegroundColor Cyan
pieln { param ($indentionLevel)
@(
'/*'
' This is a multiline'
' comment from Powershell'
'*/'
) -join "`n"
}
executeOnce {
foreach ($x in 1..15) {
Write-Host
}
pieln {
'// This is a comment'
}
foreach ($n in 1..10) {
Write-Host "n: $n, lineNumber: $GlovePieLineNumber"
}
$waitTime = pie {
# It seems that in RandomRange(a, b), b is exclusive
'RandomRange(1, 5)'
}
Write-Host "waitTime: $waitTime"
if ($waitTime -ceq 4) {
throw "waitTime can't be 4"
}
$random = pie {
'RandomRange(0, 100)'
}
Write-Host "random: $random"
if ($random % 2 -ceq 0) {
Start-Sleep 0
foreach ($n in 1000..1010) {
Write-Host "n: $n"
}
}
foreach ($n in 11..20) {
Write-Host "n: $n"
}
Start-Sleep $waitTime
foreach ($n in 21..30) {
Write-Host "n: $n"
}
exit
}
/*
This is a multiline
comment from Powershell
*/
if not var.skip_99ed01b6be6f45d19cf5a92c7fbc6900 then
var.skip_99ed01b6be6f45d19cf5a92c7fbc6900 = true
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
DebugPrint
// This is a comment
DebugPrint "n: " + 1 + ", lineNumber: " + 24 + ""
DebugPrint "n: " + 2 + ", lineNumber: " + 25 + ""
DebugPrint "n: " + 3 + ", lineNumber: " + 26 + ""
DebugPrint "n: " + 4 + ", lineNumber: " + 27 + ""
DebugPrint "n: " + 5 + ", lineNumber: " + 28 + ""
DebugPrint "n: " + 6 + ", lineNumber: " + 29 + ""
DebugPrint "n: " + 7 + ", lineNumber: " + 30 + ""
DebugPrint "n: " + 8 + ", lineNumber: " + 31 + ""
DebugPrint "n: " + 9 + ", lineNumber: " + 32 + ""
DebugPrint "n: " + 10 + ", lineNumber: " + 33 + ""
var.waitTime = RandomRange(1, 5)
DebugPrint "waitTime: " + var.waitTime + ""
if (var.waitTime) == (4) then
DebugPrint '❌ ERROR at line 38: ' + "waitTime can't be 4"
var.wait_44d2b5bb92b946d6b2c10041f86c470b = 1
ExitProgram
wait var.wait_44d2b5bb92b946d6b2c10041f86c470b
var.wait_44d2b5bb92b946d6b2c10041f86c470b = 0
endif
wait var.wait_44d2b5bb92b946d6b2c10041f86c470b
var.random = RandomRange(0, 100)
DebugPrint "random: " + var.random + ""
if ((var.random) % (2)) == (0) then
var.wait_56d8b015a06a4311a3432167074592e6 = 0
wait var.wait_56d8b015a06a4311a3432167074592e6
var.wait_56d8b015a06a4311a3432167074592e6 = 0
DebugPrint "n: " + 1000 + ""
DebugPrint "n: " + 1001 + ""
DebugPrint "n: " + 1002 + ""
DebugPrint "n: " + 1003 + ""
DebugPrint "n: " + 1004 + ""
DebugPrint "n: " + 1005 + ""
DebugPrint "n: " + 1006 + ""
DebugPrint "n: " + 1007 + ""
DebugPrint "n: " + 1008 + ""
DebugPrint "n: " + 1009 + ""
DebugPrint "n: " + 1010 + ""
endif
wait var.wait_56d8b015a06a4311a3432167074592e6
DebugPrint "n: " + 11 + ""
DebugPrint "n: " + 12 + ""
DebugPrint "n: " + 13 + ""
DebugPrint "n: " + 14 + ""
DebugPrint "n: " + 15 + ""
DebugPrint "n: " + 16 + ""
DebugPrint "n: " + 17 + ""
DebugPrint "n: " + 18 + ""
DebugPrint "n: " + 19 + ""
DebugPrint "n: " + 20 + ""
var.wait_d2cc449fc85f49d48b736b0aef264314 = var.waitTime
wait var.wait_d2cc449fc85f49d48b736b0aef264314
var.wait_d2cc449fc85f49d48b736b0aef264314 = 0
DebugPrint "n: " + 21 + ""
DebugPrint "n: " + 22 + ""
DebugPrint "n: " + 23 + ""
DebugPrint "n: " + 24 + ""
DebugPrint "n: " + 25 + ""
DebugPrint "n: " + 26 + ""
DebugPrint "n: " + 27 + ""
DebugPrint "n: " + 28 + ""
DebugPrint "n: " + 29 + ""
DebugPrint "n: " + 30 + ""
var.wait_fe031c8204cb4cfeb45c6bcc0d011f87 = 1
ExitProgram
wait var.wait_fe031c8204cb4cfeb45c6bcc0d011f87
var.wait_fe031c8204cb4cfeb45c6bcc0d011f87 = 0
endif
wait var.wait_44d2b5bb92b946d6b2c10041f86c470b
wait var.wait_56d8b015a06a4311a3432167074592e6
wait var.wait_d2cc449fc85f49d48b736b0aef264314
wait var.wait_fe031c8204cb4cfeb45c6bcc0d011f87
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment