Skip to content

Commit b4bc395

Browse files
fix boxed complex-key projection
Read and write internal value boxes via raw PSObject properties so empty arrays and nested complex keys are preserved without pipeline-style flattening. Add a regression for nested empty-sequence keys and lock conformance accounting to the new zero-fail corpus counts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent f5bdef1 commit b4bc395

3 files changed

Lines changed: 47 additions & 23 deletions

File tree

src/functions/private/ConvertFrom-YamlNode.ps1

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ function ConvertFrom-YamlNode {
4040
$frame.Node = $effective
4141

4242
if ($effective.Kind -eq 'Scalar') {
43-
$frame.Holder.Value = (Resolve-YamlScalar -Node $effective).Value
43+
$frame.Holder.PSObject.Properties['Value'].Value = (Resolve-YamlScalar -Node $effective).Value
4444
[void] $stack.Pop()
4545
continue
4646
}
4747
if ($Cache.ContainsKey($effective.Id)) {
48-
$frame.Holder.Value = $Cache[$effective.Id]
48+
$frame.Holder.PSObject.Properties['Value'].Value = $Cache[$effective.Id]
4949
[void] $stack.Pop()
5050
continue
5151
}
@@ -70,7 +70,7 @@ function ConvertFrom-YamlNode {
7070
$frame.State = 'PropertyKey'
7171
}
7272
$Cache[$effective.Id] = $frame.Result
73-
$frame.Holder.Value = $frame.Result
73+
$frame.Holder.PSObject.Properties['Value'].Value = $frame.Result
7474
continue
7575
}
7676

@@ -96,7 +96,7 @@ function ConvertFrom-YamlNode {
9696
continue
9797
}
9898
if ($frame.State -eq 'SequenceValue') {
99-
$frame.Result[$frame.Index] = $frame.Child.Value
99+
$frame.Result[$frame.Index] = $frame.Child.PSObject.Properties['Value'].Value
100100
$frame.Index++
101101
$frame.State = 'Sequence'
102102
continue
@@ -128,10 +128,11 @@ function ConvertFrom-YamlNode {
128128
continue
129129
}
130130
if ($frame.State -eq 'OmapKeyValue') {
131-
$frame.Key = if ($null -eq $frame.Child.Value) {
132-
[System.DBNull]::Value
131+
$keyValue = $frame.Child.PSObject.Properties['Value'].Value
132+
if ([object]::ReferenceEquals($keyValue, $null)) {
133+
$frame.Key = [System.DBNull]::Value
133134
} else {
134-
$frame.Child.Value
135+
$frame.Key = $keyValue
135136
}
136137
$entryNode = $frame.Node.Items[$frame.Index]
137138
while ($entryNode.Kind -eq 'Alias') {
@@ -153,7 +154,7 @@ function ConvertFrom-YamlNode {
153154
continue
154155
}
155156
if ($frame.State -eq 'OmapValue') {
156-
$frame.Result.Add($frame.Key, $frame.Child.Value)
157+
$frame.Result.Add($frame.Key, $frame.Child.PSObject.Properties['Value'].Value)
157158
$frame.Index++
158159
$frame.State = 'OmapKey'
159160
continue
@@ -180,10 +181,11 @@ function ConvertFrom-YamlNode {
180181
continue
181182
}
182183
if ($frame.State -eq 'DictionaryKeyValue') {
183-
$frame.Key = if ($null -eq $frame.Child.Value) {
184-
[System.DBNull]::Value
184+
$keyValue = $frame.Child.PSObject.Properties['Value'].Value
185+
if ([object]::ReferenceEquals($keyValue, $null)) {
186+
$frame.Key = [System.DBNull]::Value
185187
} else {
186-
$frame.Child.Value
188+
$frame.Key = $keyValue
187189
}
188190
if ($frame.Node.Tag -ceq 'tag:yaml.org,2002:set') {
189191
$frame.Result.Add($frame.Key, $null)
@@ -207,7 +209,7 @@ function ConvertFrom-YamlNode {
207209
continue
208210
}
209211
if ($frame.State -eq 'DictionaryValue') {
210-
$frame.Result.Add($frame.Key, $frame.Child.Value)
212+
$frame.Result.Add($frame.Key, $frame.Child.PSObject.Properties['Value'].Value)
211213
$frame.Index++
212214
$frame.State = 'DictionaryKey'
213215
continue
@@ -234,7 +236,7 @@ function ConvertFrom-YamlNode {
234236
continue
235237
}
236238
if ($frame.State -eq 'PropertyKeyValue') {
237-
$frame.Key = $frame.Child.Value
239+
$frame.Key = $frame.Child.PSObject.Properties['Value'].Value
238240
if ($frame.Key -isnot [string] -or [string]::IsNullOrEmpty($frame.Key)) {
239241
$keyNode = $frame.Node.Entries[$frame.Index].Key
240242
throw (New-YamlException -Start $keyNode.Start -End $keyNode.End `
@@ -268,13 +270,13 @@ function ConvertFrom-YamlNode {
268270
$frame.Result.PSObject.Properties.Add(
269271
[System.Management.Automation.PSNoteProperty]::new(
270272
[string] $frame.Key,
271-
$frame.Child.Value
273+
$frame.Child.PSObject.Properties['Value'].Value
272274
)
273275
)
274276
$frame.Index++
275277
$frame.State = 'PropertyKey'
276278
}
277279
}
278280

279-
New-YamlValueBox -Value $root.Value
281+
New-YamlValueBox -Value $root.PSObject.Properties['Value'].Value
280282
}

tests/Conformance.Tests.ps1

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,18 @@ Describe 'Released yaml-test-suite corpus accounting' {
4848
}
4949

5050
It 'accounts for parser representation/event comparisons' {
51-
@($suiteResults | Where-Object EventResult -EQ 'Pass').Count | Should -Be 181
51+
@($suiteResults | Where-Object EventResult -EQ 'Pass').Count | Should -Be 307
5252
@($suiteResults | Where-Object EventResult -EQ 'PolicyDifference').Count |
53-
Should -Be 14
54-
@($suiteResults | Where-Object EventResult -EQ 'Fail').Count | Should -Be 113
53+
Should -Be 1
54+
@($suiteResults | Where-Object EventResult -EQ 'Fail').Count | Should -Be 0
5555
@($suiteResults | Where-Object EventResult -EQ 'NotApplicable').Count |
5656
Should -Be 94
57+
@(
58+
$suiteResults |
59+
Where-Object EventResult -EQ 'PolicyDifference' |
60+
Select-Object -ExpandProperty Case |
61+
Sort-Object
62+
) | Should -Be @('6CK3')
5763
}
5864

5965
It 'accounts for JSON construction comparisons' {
@@ -66,19 +72,19 @@ Describe 'Released yaml-test-suite corpus accounting' {
6672
}
6773

6874
It 'accounts for out.yaml representation comparisons' {
69-
@($suiteResults | Where-Object OutYamlResult -EQ 'Pass').Count | Should -Be 240
75+
@($suiteResults | Where-Object OutYamlResult -EQ 'Pass').Count | Should -Be 241
7076
@($suiteResults | Where-Object OutYamlResult -EQ 'PolicyDifference').Count |
7177
Should -Be 0
7278
@($suiteResults | Where-Object OutYamlResult -EQ 'Fail').Count | Should -Be 0
7379
@($suiteResults | Where-Object OutYamlResult -EQ 'NotApplicable').Count |
74-
Should -Be 162
80+
Should -Be 161
7581
}
7682

7783
It 'accounts for emitter and round-trip comparisons' {
78-
@($suiteResults | Where-Object EmitResult -EQ 'Pass').Count | Should -Be 232
84+
@($suiteResults | Where-Object EmitResult -EQ 'Pass').Count | Should -Be 306
7985
@($suiteResults | Where-Object EmitResult -EQ 'PolicyDifference').Count |
80-
Should -Be 12
81-
@($suiteResults | Where-Object EmitResult -EQ 'Fail').Count | Should -Be 62
86+
Should -Be 0
87+
@($suiteResults | Where-Object EmitResult -EQ 'Fail').Count | Should -Be 0
8288
@($suiteResults | Where-Object EmitResult -EQ 'NotApplicable').Count |
8389
Should -Be 96
8490
}

tests/ConvertFrom-Yaml.Tests.ps1

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,22 @@ copy: *source
254254
$entry.Value | Should -Be 'value'
255255
}
256256

257+
It 'preserves empty sequence keys inside nested complex mapping keys' {
258+
$result = '? []: x' | ConvertFrom-Yaml -AsHashtable
259+
$result.Count | Should -Be 1
260+
$outerKey = @($result.Keys)[0]
261+
$outerValue = $result[$outerKey]
262+
263+
$outerKey | Should -BeOfType [System.Collections.Specialized.OrderedDictionary]
264+
$outerKey.Count | Should -Be 1
265+
$innerEntry = $outerKey.GetEnumerator() | Select-Object -First 1
266+
$innerKey = $innerEntry.Key
267+
, $innerKey | Should -BeOfType [object[]]
268+
$innerKey.Count | Should -Be 0
269+
$innerEntry.Value | Should -Be 'x'
270+
$outerValue | Should -BeNullOrEmpty
271+
}
272+
257273
It 'matches standard tags ordinally and treats case variants as unknown' {
258274
$result = "integer: !!INT 12`nset: !!SET {one: null}" |
259275
ConvertFrom-Yaml -AsHashtable

0 commit comments

Comments
 (0)