private/ConvertFrom-WayforgeYamlMinimal.ps1
|
function ConvertFrom-WayforgeYamlMinimal { <# .SYNOPSIS Minimal internal YAML parser for simple PSWayforge workflow files. .DESCRIPTION Parses a constrained subset of YAML (mappings, sequences, scalars, and comments) without external dependencies. Supports the structure produced by templates/workflow.default.yaml. For complex YAML, install powershell-yaml. .PARAMETER Yaml The YAML string to parse. #> [CmdletBinding()] [OutputType([object])] param( [Parameter(Mandatory)] [string] $Yaml ) $script:lines = $Yaml -split "`r?`n" | ForEach-Object { $line = $_ -replace '\s*#.*$', '' if ($line -match '^\s*$') { return $null } $indent = ($line -replace '^(\s*).*', '$1').Length $content = $line.TrimStart() [PSCustomObject]@{ Indent = $indent; Content = $content } } | Where-Object { $_ -ne $null } $script:index = 0 function ConvertFrom-WayforgeYamlScalar { param([string] $Value) $trimmed = $Value.Trim() if ($trimmed -eq '') { return '' } if ($trimmed -match '^(true|yes)$') { return $true } if ($trimmed -match '^(false|no)$') { return $false } if ($trimmed -match '^-?\d+$') { return [int]::Parse($trimmed) } if ($trimmed -match '^-?\d+\.\d+$') { return [double]::Parse($trimmed) } if (($trimmed.StartsWith('"') -and $trimmed.EndsWith('"')) -or ($trimmed.StartsWith("'") -and $trimmed.EndsWith("'"))) { return $trimmed.Substring(1, $trimmed.Length - 2) } return $trimmed } function Read-MappingKey { param([int] $baseIndent) $line = $script:lines[$script:index] if ($line.Indent -ne $baseIndent -or $line.Content -notmatch '^([^:]+):\s*(.*)$') { throw "Expected mapping key at indentation $baseIndent, got: $($line.Content)" } $key = $matches[1] $value = $matches[2] $script:index++ if ($value -ne '') { return $key, (ConvertFrom-WayforgeYamlScalar -Value $value) } if ($script:index -lt $script:lines.Count -and $script:lines[$script:index].Indent -gt $baseIndent) { return $key, (Read-Block -baseIndent $baseIndent) } return $key, $null } function Read-Block($baseIndent) { if ($script:index -ge $script:lines.Count) { return $null } if ($script:lines[$script:index].Indent -le $baseIndent) { return $null } $blockIndent = $script:lines[$script:index].Indent $mapping = [ordered]@{} $sequence = [System.Collections.Generic.List[object]]::new() $isSequence = $false while ($script:index -lt $script:lines.Count) { $line = $script:lines[$script:index] if ($line.Indent -lt $blockIndent) { break } if ($line.Indent -gt $blockIndent) { throw "Unexpected indentation at line '$($line.Content)'." } if ($line.Content -match '^-\s+(.*)$') { $isSequence = $true $rest = $matches[1] $itemBaseIndent = $line.Indent + 2 $script:index++ if ($rest -match '^([^:]+):\s*(.*)$') { $key = $matches[1] $value = $matches[2] $item = [ordered]@{ $key = if ($value -ne '') { ConvertFrom-WayforgeYamlScalar -Value $value } else { $null } } if ($value -eq '' -and $script:index -lt $script:lines.Count -and $script:lines[$script:index].Indent -gt $itemBaseIndent) { $item[$key] = Read-Block -baseIndent $itemBaseIndent } elseif ($value -ne '') { while ($script:index -lt $script:lines.Count -and $script:lines[$script:index].Indent -eq $itemBaseIndent) { $k, $v = Read-MappingKey -baseIndent $itemBaseIndent $item[$k] = $v } } $sequence.Add($item) } else { $scalar = ConvertFrom-WayforgeYamlScalar -Value $rest $sequence.Add($scalar) } } elseif ($line.Content -match '^([^:]+):\s*(.*)$') { if ($isSequence) { throw "Mixed mapping and sequence at indentation $blockIndent." } $key, $value = Read-MappingKey -baseIndent $blockIndent $mapping[$key] = $value } else { throw "Unrecognized YAML line: $($line.Content)" } } if ($isSequence) { return ,$sequence.ToArray() } return $mapping } $result = Read-Block -baseIndent -1 return $result } |