src/Cyclomatic.ps1

<#
.SYNOPSIS
    Cyclomatic complexity per unit. Depends on Ast.ps1.

.DESCRIPTION
    Cyclomatic complexity = 1 + decision points, where a decision point is each
    if/elseif clause, each switch clause, each for/foreach/while/do loop, each
    catch/trap, each ternary, and each -and/-or in a boolean expression. This is the
    classic control-flow count (independent-path proxy); pairs with cognitive
    complexity, which weights nesting instead.
#>


function Get-PSCxCyclomaticRow {
    # Emit {Key; Amount} decision-point rows across all types. Attributed per unit.
    [OutputType([pscustomobject[]])]
    [CmdletBinding()]
    param([Parameter(Mandatory)] $Ast)

    foreach ($n in $Ast.FindAll({ param($x) $x -is [System.Management.Automation.Language.IfStatementAst] }, $true)) {
        [pscustomobject]@{ Key = Get-PSCxUnitKey -Node $n; Amount = $n.Clauses.Count }
    }
    foreach ($n in $Ast.FindAll({ param($x) $x -is [System.Management.Automation.Language.SwitchStatementAst] }, $true)) {
        [pscustomobject]@{ Key = Get-PSCxUnitKey -Node $n; Amount = $n.Clauses.Count }
    }
    foreach ($tn in 'ForEachStatementAst', 'ForStatementAst', 'WhileStatementAst', 'DoWhileStatementAst', 'DoUntilStatementAst', 'CatchClauseAst', 'TrapStatementAst') {
        foreach ($n in $Ast.FindAll({ param($x) $x.GetType().Name -eq $tn }.GetNewClosure(), $true)) {
            [pscustomobject]@{ Key = Get-PSCxUnitKey -Node $n; Amount = 1 }
        }
    }
    foreach ($n in $Ast.FindAll({ param($x) $x -is [System.Management.Automation.Language.TernaryExpressionAst] }, $true)) {
        [pscustomobject]@{ Key = Get-PSCxUnitKey -Node $n; Amount = 1 }
    }
    foreach ($n in $Ast.FindAll({ param($x) $x -is [System.Management.Automation.Language.BinaryExpressionAst] }, $true)) {
        if ($n.Operator -in 'And', 'Or') { [pscustomobject]@{ Key = Get-PSCxUnitKey -Node $n; Amount = 1 } }
    }
}

function Get-PSCxCyclomaticMap {
    # unit key -> cyclomatic complexity (1 + summed decision points).
    [OutputType([hashtable])]
    [CmdletBinding()]
    param([Parameter(Mandatory)] $Ast)
    $map = @{}
    foreach ($row in Get-PSCxCyclomaticRow -Ast $Ast) {
        $map[$row.Key] = [int]$map[$row.Key] + $row.Amount
    }
    $out = @{}
    foreach ($k in (Get-PSCxUnitTable -Ast $Ast).Keys) { $out[$k] = 1 + [int]$map[$k] }
    return $out
}