Yet Another YAML: more than JSON, less than YAML, and better for it
Ints donβt Float π¦
Arbitrary-precision integers are a first-class type, distinct from floats.
Byte Arrays π¦
Binary data with inline <c0fe> and block > c0fe forms.
Clean Diffs π§Ή
Changes to one value never cascade into adjacent lines.
No Surprises π«
No bare words. No surrogates or control codes. Strict whitespace.
The yay formatter realigns your comments, wraps your
sentences, and makes your data legit. It can also translate to lots of other
formats and languages.*
*Neither a floor wax nor a dessert topping.
At a Glance π
roses-are-red: true # There is no "yes" or "on".
violets-are-blue: false # Violets are violet.
arrays:
- "may"
- "have"
- "many"
- "values"
and-objects-too:
integers-are-distinct: 42
from-their-floating-friends: 6.283 185 307 179 586 # digit grouping
inline:
string: "is concise"
array: [infinity, -infinity, nan]
object: {bigint: 1, float64: 2.0}
bytes: <f33d face>
block:
string: `
This is a string.
There are many like it.
array:
- "But"
- "this"
- "one's"
object:
mine: null
bytes: >
b0 b5 c0 ff # Bob's Coffee
fe fa ca de # Facade.
concatenated:
"I'm not dead yet. "
"I feel happy!"
unicode-code-point: "\u{1F600}" # UTF-16 surrogates are inexpressible
"name with spaces": 'works too'
and-objects-too:
from-their-floating-friends: 6.283185307179586
integers-are-distinct: 42
arrays:
- may
- have
- many
- values
block:
array:
- But
- this
- one's
bytes: !binary sLXA//76yt4=
object:
mine: null
string: |
This is a string.
There are many like it.
concatenated: I'm not dead yet. I feel happy!
inline:
array:
- .inf
- -.inf
- .nan
bytes: !binary 8z36zg==
object:
bigint: 1
float64: 2.0
string: is concise
name with spaces: works too
roses-are-red: true
unicode-code-point: π
violets-are-blue: false
{
"and-objects-too": {
"from-their-floating-friends": 6.283185307179586,
"integers-are-distinct": "#42"
},
"arrays": [
"may",
"have",
"many",
"values"
],
"block": {
"array": [
"But",
"this",
"one's"
],
"bytes": "*b0b5c0fffefacade",
"object": {
"mine": null
},
"string": "This is a string.\nThere are many like it.\n"
},
"concatenated": "I'm not dead yet. I feel happy!",
"inline": {
"array": [
"#Infinity",
"#-Infinity",
"#NaN"
],
"bytes": "*f33dface",
"object": {
"bigint": "#1",
"float64": 2
},
"string": "is concise"
},
"name with spaces": "works too",
"roses-are-red": true,
"unicode-code-point": "π",
"violets-are-blue": false
}
{
"and-objects-too": {
"from-their-floating-friends": 6.283185307179586,
"integers-are-distinct": 42
},
"arrays": ["may", "have", "many", "values"],
"block": {
"array": ["But", "this", "one's"],
"bytes": h'b0b5c0fffefacade',
"object": {
"mine": null
},
"string": "This is a string.\nThere are many like it.\n"
},
"concatenated": "I'm not dead yet. I feel happy!",
"inline": {
"array": [Infinity, -Infinity, NaN],
"bytes": h'f33dface',
"object": {
"bigint": 1,
"float64": 2.0
},
"string": "is concise"
},
"name with spaces": "works too",
"roses-are-red": true,
"unicode-code-point": "π",
"violets-are-blue": false
}
00000000 a9 6f 61 6e 64 2d 6f 62 6a 65 63 74 73 2d 74 6f .oand-objects-to
00000010 6f a2 78 1b 66 72 6f 6d 2d 74 68 65 69 72 2d 66 o.x.from-their-f
00000020 6c 6f 61 74 69 6e 67 2d 66 72 69 65 6e 64 73 fb loating-friends.
00000030 40 19 21 fb 54 44 2d 18 75 69 6e 74 65 67 65 72 @.!.TD-.uinteger
00000040 73 2d 61 72 65 2d 64 69 73 74 69 6e 63 74 18 2a s-are-distinct.*
00000050 66 61 72 72 61 79 73 84 63 6d 61 79 64 68 61 76 farrays.cmaydhav
00000060 65 64 6d 61 6e 79 66 76 61 6c 75 65 73 65 62 6c edmanyfvaluesebl
00000070 6f 63 6b a4 65 61 72 72 61 79 83 63 42 75 74 64 ock.earray.cButd
00000080 74 68 69 73 65 6f 6e 65 27 73 65 62 79 74 65 73 thiseone'sebytes
00000090 48 b0 b5 c0 ff fe fa ca de 66 6f 62 6a 65 63 74 H........fobject
000000a0 a1 64 6d 69 6e 65 f6 66 73 74 72 69 6e 67 78 2a .dmine.fstringx*
000000b0 54 68 69 73 20 69 73 20 61 20 73 74 72 69 6e 67 This is a string
000000c0 2e 0a 54 68 65 72 65 20 61 72 65 20 6d 61 6e 79 ..There are many
000000d0 20 6c 69 6b 65 20 69 74 2e 0a 6c 63 6f 6e 63 61 like it..lconca
000000e0 74 65 6e 61 74 65 64 78 1f 49 27 6d 20 6e 6f 74 tenatedx.I'm not
000000f0 20 64 65 61 64 20 79 65 74 2e 20 49 20 66 65 65 dead yet. I fee
00000100 6c 20 68 61 70 70 79 21 66 69 6e 6c 69 6e 65 a4 l happy!finline.
00000110 65 61 72 72 61 79 83 fb 7f f0 00 00 00 00 00 00 earray..........
00000120 fb ff f0 00 00 00 00 00 00 fb 7f f8 00 00 00 00 ................
00000130 00 00 65 62 79 74 65 73 44 f3 3d fa ce 66 6f 62 ..ebytesD.=..fob
00000140 6a 65 63 74 a2 66 62 69 67 69 6e 74 01 67 66 6c ject.fbigint.gfl
00000150 6f 61 74 36 34 fb 40 00 00 00 00 00 00 00 66 73 oat64.@.......fs
00000160 74 72 69 6e 67 6a 69 73 20 63 6f 6e 63 69 73 65 tringjis concise
00000170 70 6e 61 6d 65 20 77 69 74 68 20 73 70 61 63 65 pname with space
00000180 73 69 77 6f 72 6b 73 20 74 6f 6f 6d 72 6f 73 65 siworks toomrose
00000190 73 2d 61 72 65 2d 72 65 64 f5 72 75 6e 69 63 6f s-are-red.runico
000001a0 64 65 2d 63 6f 64 65 2d 70 6f 69 6e 74 64 f0 9f de-code-pointd..
000001b0 98 80 70 76 69 6f 6c 65 74 73 2d 61 72 65 2d 62 ..pviolets-are-b
000001c0 6c 75 65 f4 lue.
({
"and-objects-too": {
"from-their-floating-friends": 6.283185307179586,
"integers-are-distinct": 42n,
},
"arrays": ["may", "have", "many", "values"],
"block": {
"array": ["But", "this", "one's"],
"bytes": Uint8Array.from([0xb0, 0xb5, 0xc0, 0xff, 0xfe, 0xfa, 0xca, 0xde]),
"object": { "mine": null },
"string": "This is a string.\nThere are many like it.\n",
},
"concatenated": "I'm not dead yet. I feel happy!",
"inline": {
"array": [Infinity, -Infinity, NaN],
"bytes": Uint8Array.from([0xf3, 0x3d, 0xfa, 0xce]),
"object": { "bigint": 1n, "float64": 2 },
"string": "is concise",
},
"name with spaces": "works too",
"roses-are-red": true,
"unicode-code-point": "π",
"violets-are-blue": false,
})
map[string]any{
"and-objects-too": map[string]any{
"from-their-floating-friends": 6.283185307179586,
"integers-are-distinct": big.NewInt(42),
},
"arrays": []any{"may", "have", "many", "values"},
"block": map[string]any{
"array": []any{"But", "this", "one's"},
"bytes": []byte{0xb0, 0xb5, 0xc0, 0xff, 0xfe, 0xfa, 0xca, 0xde},
"object": map[string]any{"mine": nil},
"string": "This is a string.\nThere are many like it.\n",
},
"concatenated": "I'm not dead yet. I feel happy!",
"inline": map[string]any{
"array": []any{math.Inf(1), math.Inf(-1), math.NaN()},
"bytes": []byte{0xf3, 0x3d, 0xfa, 0xce},
"object": map[string]any{"bigint": big.NewInt(1), "float64": 2.0},
"string": "is concise",
},
"name with spaces": "works too",
"roses-are-red": true,
"unicode-code-point": "π",
"violets-are-blue": false,
}
{"and-objects-too": {"from-their-floating-friends": 6.283185307179586,
"integers-are-distinct": 42}, "arrays": ["may", "have", "many", "values"],
"block": {"array": ["But", "this", "one's"],
"bytes": bytes.fromhex("b0b5c0fffefacade"), "object": {"mine": None},
"string": "This is a string.\nThere are many like it.\n"},
"concatenated": "I'm not dead yet. I feel happy!", "inline": {"array":
[float("inf"), float("-inf"), float("nan")],
"bytes": bytes.fromhex("f33dface"), "object": {"bigint": 1, "float64": 2.0},
"string": "is concise"}, "name with spaces": "works too",
"roses-are-red": True, "unicode-code-point": "π",
"violets-are-blue": False}
Value::Object(HashMap::from([
("and-objects-too".into(), Value::Object(HashMap::from([
("from-their-floating-friends".into(), Value::Float(6.283185307179586)),
("integers-are-distinct".into(), Value::Integer(42.into())),
]))),
("arrays".into(), Value::Array(vec![
Value::String("may".into()),
Value::String("have".into()),
Value::String("many".into()),
Value::String("values".into()),
])),
("block".into(), Value::Object(HashMap::from([
("array".into(), Value::Array(vec![
Value::String("But".into()),
Value::String("this".into()),
Value::String("one's".into()),
])),
("bytes".into(), Value::Bytes(vec![0xb0, 0xb5, 0xc0, 0xff, 0xfe, 0xfa, 0xca, 0xde])),
("object".into(), Value::Object(HashMap::from([
("mine".into(), Value::Null),
]))),
("string".into(), Value::String("This is a string.\nThere are many like it.\n".into())),
]))),
("concatenated".into(), Value::String("I'm not dead yet. I feel happy!".into())),
("inline".into(), Value::Object(HashMap::from([
("array".into(), Value::Array(vec![
Value::Float(f64::INFINITY),
Value::Float(f64::NEG_INFINITY),
Value::Float(f64::NAN),
])),
("bytes".into(), Value::Bytes(vec![0xf3, 0x3d, 0xfa, 0xce])),
("object".into(), Value::Object(HashMap::from([
("bigint".into(), Value::Integer(1.into())),
("float64".into(), Value::Float(2.0)),
]))),
("string".into(), Value::String("is concise".into())),
]))),
("name with spaces".into(), Value::String("works too".into())),
("roses-are-red".into(), Value::Bool(true)),
("unicode-code-point".into(), Value::String("π".into())),
("violets-are-blue".into(), Value::Bool(false)),
]))
YAY_OBJECT(
"and-objects-too", YAY_OBJECT(
"from-their-floating-friends", yay_float(6.283185307179586),
"integers-are-distinct", yay_int(42)
),
"arrays", YAY_ARRAY(
yay_string("may"),
yay_string("have"),
yay_string("many"),
yay_string("values")
),
"block", YAY_OBJECT(
"array", YAY_ARRAY(
yay_string("But"),
yay_string("this"),
yay_string("one's")
),
"bytes", yay_bytes_from_hex("b0b5c0fffefacade"),
"object", YAY_OBJECT("mine", yay_null()),
"string", yay_string("This is a string.\nThere are many like it.\n")
),
"concatenated", yay_string("I'm not dead yet. I feel happy!"),
"inline", YAY_OBJECT(
"array", YAY_ARRAY(
yay_float(INFINITY),
yay_float(-INFINITY),
yay_float(NAN)
),
"bytes", yay_bytes_from_hex("f33dface"),
"object", YAY_OBJECT("bigint", yay_int(1), "float64", yay_float(2.0)),
"string", yay_string("is concise")
),
"name with spaces", yay_string("works too"),
"roses-are-red", yay_bool(true),
"unicode-code-point", yay_string("π"),
"violets-are-blue", yay_bool(false)
)
(("and-objects-too" . (("from-their-floating-friends" . 6.283185307179586)
("integers-are-distinct" . 42)))
("arrays" . #("may" "have" "many" "values"))
("block" . (("array" . #("But" "this" "one's"))
("bytes" . (bytevector 176 181 192 255 254 250 202 222))
("object" . (("mine" . 'null)))
("string" . "This is a string.\nThere are many like it.\n")))
("concatenated" . "I'm not dead yet. I feel happy!")
("inline" . (("array" . #(+inf.0 -inf.0 +nan.0))
("bytes" . (bytevector 243 61 250 206))
("object" . (("bigint" . 1) ("float64" . 2.0)))
("string" . "is concise")))
("name with spaces" . "works too")
("roses-are-red" . #t)
("unicode-code-point" . "π")
("violets-are-blue" . #f))
Map.of(
"and-objects-too", Map.of(
"from-their-floating-friends", 6.283185307179586,
"integers-are-distinct", BigInteger.valueOf(42)),
"arrays", List.of("may", "have", "many", "values"),
"block", Map.of(
"array", List.of("But", "this", "one's"),
"bytes", new byte[] {
(byte) 0xb0, (byte) 0xb5, (byte) 0xc0, (byte) 0xff,
(byte) 0xfe, (byte) 0xfa, (byte) 0xca, (byte) 0xde},
"object", Map.of("mine", null),
"string", "This is a string.\nThere are many like it.\n"),
"concatenated", "I'm not dead yet. I feel happy!",
"inline", Map.of(
"array", List.of(
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN),
"bytes", new byte[] {(byte) 0xf3, (byte) 0x3d, (byte) 0xfa, (byte) 0xce},
"object", Map.of("bigint", BigInteger.valueOf(1), "float64", 2.0),
"string", "is concise"),
"name with spaces", "works too",
"roses-are-red", true,
"unicode-code-point", "π",
"violets-are-blue", false
)
YOUR SPONSORSHIP COULD BE RIGHT HERE!
THE SOFTWARE IS FREEβ , BUT AIN'T CHEAP!
inquire within: agile.lion63541@fastmail.com
Command Line Tool πͺ
The yay CLI parses, validates, formats, and converts YAY files.
It reads any supported format and writes to any supported target.
Available as binyay on
npm and
crates.io,
or via Homebrew from the kriskowal/yippee tap.
yay [OPTIONS] [FILE|DIR]
Format & Validate β¨
# Reformat a file to canonical YAY
yay config.yay
# Validate without output
yay --check config.yay
# Validate all .yay files in a directory
yay --check .
Convert Between Formats π
# JSON to YAY
echo '{"a": 1, "b": 2}' | yay -f json
# a: 1.0
# b: 2.0
# YAY to YAML
yay -t yaml config.yay
# YAY to CBOR (binary)
yay -t cbor config.yay -o config.cbor
# CBOR to YAY
yay -f cbor config.cbor
# YAY to CBOR diagnostic notation
yay -t diag config.yay
Code Generation ποΈ
yay -t js config.yay # JS π¨
yay -t go config.yay # Go π
yay -t python config.yay # Python π
yay -t rust config.yay # Rust π¦
yay -t c config.yay # C πͺ
yay -t scheme config.yay # Scheme π
yay -t java config.yay # Java β
Pipe & Stdin βοΈ
# Read from stdin (any format)
echo '42' | yay -t js
# 42n
# Explicit stdin with -
yay -f json - < data.json
# YSON preserves big integers and bytes
echo '<cafe>' | yay -t yson
# "*cafe"
Data Model π€
YAY defines eight value types.
- null
- The
nullkeyword - boolean
trueorfalseβ no "yes", "no", "on", "off"- integer
- Arbitrary-precision big integer
- float
- IEEE 754 binary64, including
nan,infinity,-infinity - string
- Double-quoted (with escapes), single-quoted (literal), or block (backtick)
- bytes
- Inline
<hex>or block>with hex lines - array
- Inline
[a, b]or block with-items - object
- Inline
{k: v}or block withkey: valueproperties
Scalars π
# Null
null
# Booleans
true
false
# Big integers (arbitrary precision)
42
-10
867 5309 # digit grouping with spaces
# Floats (IEEE 754 binary64)
6.283185307179586
.5
1.
-0.0
6.022e23
nan
infinity
-infinity
Strings π§΅
# Double-quoted (with escape sequences)
"Hello, world\n"
"\"\\\/\b\f\n\r\t\u{263A}"
# Single-quoted (literal, no escapes)
'Are you suggesting coconuts migrate?'
# Block string (backtick introducer)
message: `
By Grabthar's hammer,
we live to tell the tale.
# Concatenated quoted strings
confession:
"I'm not dead yet. "
"I feel happy!"
Objects π
# Inline
{name: 'Marvin', mood: 'depressed'}
# Block (the root document is an object)
parrot:
status: "pining for the fjords"
plumage: "beautiful"
# Quoted keys
"key name": 1
empty: {}
Arrays π
# Inline
[42, 404, 418]
["And there was much rejoicing.", "yay."]
# Block
complaints:
- "I didn't vote for you."
- "Help, help, I'm being repressed!"
# Nested
- - "a"
- "b"
- - 1
- 2
Byte Arrays π
# Inline
<f33d face>
<> # empty
# Block (with comments)
data: >
b0 b5 c0 ff # Bob's Coffee
fe fa ca de # Facade.
Comments π¬
# Top-level comments appear before the root value.
# They extend to the end of the line.
answer: 42 # inline comments work too
YSON π‘
YSON is a dialect of JSON that preserves all YAY value types.
Standard JSON cannot represent big integers, byte arrays, or the special float
values infinity, -infinity, and nan.
YSON fills the gap by encoding these as prefixed strings:
# Big integers
"#42" # β 42 (big integer)
"#-7" # β -7
# Special floats
"#Infinity" # β infinity
"#-Infinity" # β -infinity
"#NaN" # β nan
# Byte arrays
"*cafe" # β <cafe>
"*" # β <> (empty)
# Strings starting with # or * are escaped with !
"!#hashtag" # β "#hashtag" (string)
"!*star" # β "*star" (string)
A YSON document is always valid JSON, so any JSON parser can read it. A YSON-aware reader recovers the original YAY types. YSON is itself a subset of the Endo SmallCaps encoding.
yay -t yson config.yay # YAY β YSON
yay -f yson config.yson # YSON β YAY
SHON π
SHON (Shell Object Notation) lets you construct structured data directly from command-line arguments, without writing a file or piping stdin.
Objects
yay [ --name hello --count 42 ]
{count: 42, name: "hello"}
Arrays
Nesting
yay [ --servers [ localhost:8080 localhost:8081 ] --options [ --verbose -t ] ]
options: {verbose: true}
servers: ["localhost:8080", "localhost:8081"]
Convert to JSON
yay -t json [ --x 1.0 --y 2.0 ]
{
"x": 1,
"y": 2
}
Convert to YSON
yay -t yson [ --name hello --values [ 1 2 3 ] ]
{
"name": "hello",
"values": [
"#1",
"#2",
"#3"
]
}
Hex Bytes
yay -t yson -x cafe
"*cafe"
String Escaping
yay [ -- 42 -- -t -- [ ]
["42", "-t", "["]
Inside brackets, all YAY value types are available:
-n (null), -t (true), -f (false),
-I (β), -i (ββ), -N (NaN),
-x (hex bytes), -b (fileβbytes),
-s (fileβstring), -- (string escape),
and --key (object key).
Bare words are strings. Bare numbers are integers or floats.
See SHON.md
for the full specification.
Get Started π
The file extension is .yay.
GitHub Β· Source, grammar, implementations in Rust π¦, Go π, JS π¨, Python π, C π, Scheme π, and Java π.
Use the library:
npm install libyay # JavaScript / TypeScript π¨
cargo add libyay # Rust π¦
pip install libyay # Python π
Install the CLI (binyay):
brew install kriskowal/yippee/yay # Homebrew (via tap)
npm install -g binyay # npm
cargo install binyay # Rust