Skip to content

Commit 4093211

Browse files
Align scalar resolution with YAML 1.2.2 core schema (case-sensitive true/false/null)
1 parent a2e28bc commit 4093211

6 files changed

Lines changed: 56 additions & 36 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
## Prerequisites
66

77
This uses the following external resources:
8+
89
- The [PSModule framework](https://github.com/PSModule) for building, testing and publishing the module.
910

1011
## Installation
@@ -25,6 +26,19 @@ The module provides two cmdlets that mirror PowerShell's built-in `ConvertFrom-J
2526
| `ConvertFrom-Yaml` | `ConvertFrom-Yml` | Parse a YAML string into an object. |
2627
| `ConvertTo-Yaml` | `ConvertTo-Yml` | Serialize an object into a YAML string. |
2728

29+
### YAML specification
30+
31+
The module aligns with [**YAML 1.2.2**](https://yaml.org/spec/1.2.2/) (October 2021) — the latest revision of the YAML specification — and follows the [**core schema**](https://yaml.org/spec/1.2.2/#103-core-schema) for scalar resolution.
32+
33+
Practical implications of the core schema:
34+
35+
- `true` and `false` (lowercase only) parse as `[bool]`. `True`, `TRUE`, `yes`, `no`, `on`, `off`, etc. are plain strings.
36+
- `null`, `~`, and an empty value parse as `$null`. `Null`, `NULL` are plain strings.
37+
- Integers and floats parse to their native types using invariant culture.
38+
- Anything else is a string. Quoted strings (`'...'`, `"..."`) always preserve the string type.
39+
40+
The supported YAML subset covers block-style mappings, block-style sequences, nested structures, single- and double-quoted scalars (with `\n`, `\t`, `\r`, `\\`, `\"` escapes in double quotes), and full-line / inline `#` comments. Flow style (`[a, b]`, `{a: 1}`), block scalars (`|`, `>`), anchors, aliases, tags, multi-document streams, and `!!timestamp` are not yet supported.
41+
2842
### Example 1: Parse a YAML string
2943

3044
```powershell

src/functions/private/ConvertFrom-YamlNode.ps1

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,12 @@ function ConvertFrom-YamlScalar {
232232
return (Expand-YamlDoubleQuoted -Text $inner)
233233
}
234234

235-
# Null literals.
236-
if ($value -in @('~', 'null', 'Null', 'NULL')) { return $null }
235+
# Null literal (YAML 1.2.2 core schema): empty, ~, null only. Case-sensitive.
236+
if ($value -ceq '~' -or $value -ceq 'null') { return $null }
237237

238-
# Boolean literals.
239-
if ($value -in @('true', 'True', 'TRUE', 'yes', 'Yes', 'YES', 'on', 'On', 'ON')) {
240-
return $true
241-
}
242-
if ($value -in @('false', 'False', 'FALSE', 'no', 'No', 'NO', 'off', 'Off', 'OFF')) {
243-
return $false
244-
}
238+
# Boolean literal (YAML 1.2.2 core schema): true / false only. Case-sensitive.
239+
if ($value -ceq 'true') { return $true }
240+
if ($value -ceq 'false') { return $false }
245241

246242
# Integer.
247243
$intVal = 0

src/functions/private/ConvertTo-YamlNode.ps1

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -407,18 +407,11 @@ function Test-YamlPlainSafe {
407407
if ($Text.Length -eq 0) { return $false }
408408
if ($Text -ne $Text.Trim()) { return $false }
409409

410-
# Strings that look like reserved literals must be quoted to preserve the string type.
411-
$reserved = @(
412-
'true', 'True', 'TRUE',
413-
'false', 'False', 'FALSE',
414-
'yes', 'Yes', 'YES',
415-
'no', 'No', 'NO',
416-
'on', 'On', 'ON',
417-
'off', 'Off', 'OFF',
418-
'null', 'Null', 'NULL',
419-
'~'
420-
)
421-
if ($Text -in $reserved) { return $false }
410+
# Strings that match YAML 1.2.2 core schema literals must be quoted to preserve string type.
411+
# Comparison is case-sensitive — only the lowercase canonical forms are recognised by parsers.
412+
if ($Text -ceq 'true' -or $Text -ceq 'false' -or $Text -ceq 'null' -or $Text -ceq '~') {
413+
return $false
414+
}
422415

423416
# Strings that parse as a number must be quoted.
424417
$tmpInt = 0L

src/functions/public/ConvertFrom-Yaml.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@
102102
}
103103

104104
$context = [pscustomobject]@{
105-
Lines = $lines
106-
Index = 0
105+
Lines = $lines
106+
Index = 0
107107
AsHashtable = [bool] $AsHashtable
108-
MaxDepth = $Depth
108+
MaxDepth = $Depth
109109
}
110110

111111
$result = ConvertFrom-YamlNode -Context $context -Indent 0 -Depth 0

tests/ConvertFrom-Yaml.Tests.ps1

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,28 @@ Describe 'ConvertFrom-Yaml' {
2929
$result.ratio | Should -BeOfType [double]
3030
}
3131

32-
It 'Parses boolean true variants' {
33-
$result = "a: true`nb: True`nc: yes" | ConvertFrom-Yaml
32+
It 'Parses boolean true' {
33+
$result = 'a: true' | ConvertFrom-Yaml
3434
$result.a | Should -BeTrue
35-
$result.b | Should -BeTrue
36-
$result.c | Should -BeTrue
35+
$result.a | Should -BeOfType [bool]
3736
}
3837

39-
It 'Parses boolean false variants' {
40-
$result = "a: false`nb: False`nc: no" | ConvertFrom-Yaml
38+
It 'Parses boolean false' {
39+
$result = 'a: false' | ConvertFrom-Yaml
4140
$result.a | Should -BeFalse
42-
$result.b | Should -BeFalse
43-
$result.c | Should -BeFalse
41+
$result.a | Should -BeOfType [bool]
42+
}
43+
44+
It 'Treats non-canonical boolean-like words as strings (YAML 1.2.2)' {
45+
# YAML 1.2.2 core schema only recognizes lowercase true/false. Everything else is a string.
46+
$result = "a: True`nb: TRUE`nc: yes`nd: No`ne: on`nf: OFF" | ConvertFrom-Yaml
47+
$result.a | Should -Be 'True'
48+
$result.a | Should -BeOfType [string]
49+
$result.b | Should -Be 'TRUE'
50+
$result.c | Should -Be 'yes'
51+
$result.d | Should -Be 'No'
52+
$result.e | Should -Be 'on'
53+
$result.f | Should -Be 'OFF'
4454
}
4555

4656
It 'Parses null values' {
@@ -50,6 +60,13 @@ Describe 'ConvertFrom-Yaml' {
5060
$result.c | Should -BeNullOrEmpty
5161
}
5262

63+
It 'Treats non-canonical null-like words as strings (YAML 1.2.2)' {
64+
$result = "a: Null`nb: NULL" | ConvertFrom-Yaml
65+
$result.a | Should -Be 'Null'
66+
$result.a | Should -BeOfType [string]
67+
$result.b | Should -Be 'NULL'
68+
}
69+
5370
It 'Parses single-quoted strings preserving content' {
5471
$result = "value: 'true'" | ConvertFrom-Yaml
5572
$result.value | Should -Be 'true'

tests/ConvertTo-Yaml.Tests.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ Describe 'ConvertTo-Yaml' {
6262
Context 'Mappings' {
6363
It 'Renders a flat hashtable' {
6464
$yaml = ([ordered]@{ name = 'Alice'; age = 30 }) | ConvertTo-Yaml
65-
$lines = $yaml.TrimEnd("`r","`n") -split "`r?`n"
65+
$lines = $yaml.TrimEnd("`r", "`n") -split "`r?`n"
6666
$lines[0] | Should -Be 'name: Alice'
6767
$lines[1] | Should -Be 'age: 30'
6868
}
6969

7070
It 'Renders nested mappings with 2-space indent by default' {
7171
$obj = [ordered]@{
7272
person = [ordered]@{
73-
name = 'Alice'
73+
name = 'Alice'
7474
address = [ordered]@{
7575
city = 'Oslo'
7676
}
@@ -200,9 +200,9 @@ Describe 'Round-trip ConvertTo-Yaml | ConvertFrom-Yaml' {
200200
It 'Preserves a nested mapping' {
201201
$obj = [ordered]@{
202202
person = [ordered]@{
203-
name = 'Alice'
203+
name = 'Alice'
204204
address = [ordered]@{
205-
city = 'Oslo'
205+
city = 'Oslo'
206206
country = 'Norway'
207207
}
208208
}

0 commit comments

Comments
 (0)