Private/Conditions.ps1

# Decides !if conditions. They are read by PsdUtil and never run as PowerShell.

function Test-PsdCondition {
    param($Cond, $Ev)
    switch ($Cond.T) {
        'Or' { if (Test-PsdCondition $Cond.L $Ev) { return $true }; return (Test-PsdCondition $Cond.R $Ev) }
        'And' { if (-not (Test-PsdCondition $Cond.L $Ev)) { return $false }; return (Test-PsdCondition $Cond.R $Ev) }
        'Not' { return (-not (Test-PsdCondition $Cond.X $Ev)) }
        'Truth' { return (Test-PsdTruth (Get-PsdCondValue $Cond.X $Ev)) }
        'Cmp' {
            $l = Get-PsdCondValue $Cond.L $Ev
            $r = Get-PsdCondValue $Cond.R $Ev
            return (Compare-PsdCondValues $Cond $l $r $Ev)
        }
    }
    $false
}

function Get-PsdCondValue {
    param($X, $Ev)
    if ($X.T -eq 'Lit') { return , $X.V }
    $r = Invoke-PsdExpr $X.Expr $Ev $X
    , $r.Value
}

function Test-PsdTruth {
    param($Value)
    if ($null -eq $Value) { return $false }
    if ($Value -is [bool]) { return $Value }
    if ($Value -is [string]) { return ($Value.Length -gt 0) }
    if (Test-PsdNumber $Value) { return ($Value -ne 0) }
    if ($Value -is [System.Collections.IDictionary]) { return ($Value.PSBase.Count -gt 0) }
    if (Test-PsdList $Value) { return ($Value.Count -gt 0) }
    $true
}

function Test-PsdCondEqual {
    param($A, $B)
    if ((Test-PsdContainer $A) -or (Test-PsdContainer $B)) { return (Test-PsdEqual $A $B) }
    if ($null -eq $A) { return ($null -eq $B) }
    try { return [bool]($A -eq $B) } catch { return $false }
}

function Compare-PsdCondValues {
    param($Cond, $L, $R, $Ev)
    $ctx = $Ev.Doc.Ctx
    $op = $Cond.Op
    $bad = {
        param($msg, $code = 'PSD402')
        Add-PsdNodeError $ctx $code $msg $Ev.Node $Cond.Start ($Cond.End - $Cond.Start)
        $false
    }
    switch ($op) {
        '-eq' { return (Test-PsdCondEqual $L $R) }
        '-ne' { return (-not (Test-PsdCondEqual $L $R)) }
        '-in' {
            if ($R -is [System.Collections.IDictionary]) { return (& $bad "-in needs a list on the right, but it's a map.") }
            foreach ($x in (Get-PsdItems $R)) { if (Test-PsdCondEqual $L $x) { return $true } }
            return $false
        }
    }
    if ((Test-PsdContainer $L) -or (Test-PsdContainer $R)) {
        return (& $bad "$op can't compare $(Get-PsdKindText $L) with $(Get-PsdKindText $R). It works on text and numbers.")
    }
    switch ($op) {
        '-like' { return ((Format-PsdScalarText $L) -like (Format-PsdScalarText $R)) }
        '-match' {
            try { $rx = New-PsdRegex (Format-PsdScalarText $R) $ctx }
            catch { return (& $bad "$(Format-PsdValueShort $R) isn't a valid regex: $($_.Exception.InnerException.Message)" 'PSD403') }
            try { return $rx.IsMatch((Format-PsdScalarText $L)) }
            catch [System.Text.RegularExpressions.RegexMatchTimeoutException] {
                Add-PsdLimitError $ctx 'RegexTimeout' 'by the regex in this condition' $Ev.Node
                return $false
            }
        }
    }
    try {
        switch ($op) {
            '-gt' { return [bool]($L -gt $R) }
            '-ge' { return [bool]($L -ge $R) }
            '-lt' { return [bool]($L -lt $R) }
            '-le' { return [bool]($L -le $R) }
        }
    }
    catch {
        return (& $bad "$op can't compare $(Format-PsdValueShort $L) with $(Format-PsdValueShort $R).")
    }
    $false
}