functions/private/.macro.ps1
|
function .macro { [Alias('.mac')] [PSASM()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Name, [Parameter(ValueFromRemainingArguments = $true)] [object[]]$MacroArgs, [int]$ScopeID = 0 ) if ($MacroArgs.Count -eq 0) { throw "Macro '$Name' requires a scriptblock body" } # Last argument is always the body $Body = $MacroArgs[-1] if (-not ($Body -is [scriptblock])) { throw "Last argument must be a scriptblock in macro '$Name'" } # Get raw call text so we can preserve literal $params $callText = $MyInvocation.Statement # write-host ".macro Invocation Statement: $($callText)" # Extract "(...)" part if present if ($callText -match '\w+\s*\((.*?)\)\s*\{') { $ParamList = $matches[1].Trim() $ParamNames = foreach ($p in ($ParamList -split '\s*,\s*')) { if ($p -match '^(?<name>\$\w+)(?:\s*=\s*(?<default>.+))?$') { # write-host "pName: $($matches['name'])" # write-host "pDefault: $($matches['default'])" [pscustomobject]@{ Name = $matches['name'] Default = $matches['default'] } } } } else { $ParamList = "" $ParamNames = @() } # Extract body text $BodyText = $Body.Ast.Extent.Text if ($BodyText.StartsWith('{') -and $BodyText.EndsWith('}')) { $BodyText = $BodyText.Substring(1, $BodyText.Length - 2).Trim() } # Build the unpacking code $UnpackCode = "" $numDefault = 0 if ($ParamNames.Count -gt 0) { ### So if 0 arguments, you are allowed to pass any number of arguments, which can then be accessed via $args. $assignments = @() for ($i=0;$i -lt $($ParamNames.Count);$i++) { $pName = $ParamNames[$i].Name $pDefault = $ParamNames[$i].Default if ($pDefault) { $numDefault++ $assignments += 'if($macroArgs.Count -gt ' + $i + ') { ' + $pName + ' = $macroArgs[' + $i + '] } else { ' + $pName + ' = $(' + $pDefault + ') }' } else { $assignments += "$pName = `$macroArgs[$i]" } } $UnpackCode = @" if (`$macroArgs.Count -lt $($ParamNames.Count - $numDefault) -or `$macroArgs.Count -gt $($ParamNames.Count)) { throw "Incorrect number of arguments passed to macro '$Name'. Expected $($ParamNames.Count) (``$($ParamNames.Name -join ', `')), got `$(`$macroArgs.Count) (`$(`$macroArgs -join ', '))" } $($assignments -join "`n") "@ } # Build function definition $FunctionText = if ([string]::IsNullOrWhiteSpace($ParamList)) { $BodyText } else { # "[PSASM(macro)] param([object[]]`$macroArgs)`n`$macroArgs.GetType() | ft -auto | out-string | write-host`n`$macroArgs | out-string | write-host`n$UnpackCode`n$BodyText" "[PSASM(macro)] param([object[]]`$macroArgs)`n$UnpackCode`n$BodyText" } $sb = [scriptblock]::Create($FunctionText) # set-item "Function:\script:$Name" -Value $sb $psasm.Macros[$ScopeID] ?? ($psasm.Macros[$ScopeID] = [ordered]@{}) $psasm.Macros[$ScopeID][$Name] = $sb # $msg = Get-Item "Function:\$Name" | fl | Out-String # write-host $msg } |