Djedat

π“Š½ Djedat: Djevko Data Format

Djedat /ˈdΝ‘Κ’edΓ¦t/ (Djevko Data Format) is a data format compatible with JSON: the two can be translated into each other.

Djedat supports JSON literals which allow embedding JSON documents verbatim as values into Djedat.

With custom literals, this functionality can even be extended beyond JSON.

Djedat is more minimal and lightweight than JSON, while being more powerful and flexible at the same time.

Djedat is human-friendly and ergonomic.

Like JSON, Djedat is suitable for data-interchange (thanks to being efficient to process by machines).

Unlike JSON, Djedat is also good for configuration (thanks to its support for comments and ignored entries).

Also unlike JSON, Djedat lends itself naturally to streaming (no need for JSON Lines and similar JSON extensions).

The JavaScript library can also handle Infinity and NaN as numbers.

examples

Djedat
GameConfig[ optional struct name
    window size [[800] [600]]
    window title [PAC-MAN]
    fullscreen [false]
    
    mouse sensitivity [1.4]
    key bindings [
        up [Up]
        down [Down]
        left [Left]
        right [Right]
        
        Uncomment to enable WASD controls
        ;[
        W [Up]
        A [Down]
        S [Left]
        D [Right]
        ]
    ]
    
    difficulty options [
        start difficulty [Easy]
        adaptive [false]
    ]
]

Scene[ class name is optional
    materials [ this is a map
        metal [
            reflectivity [1.0]
        ]
        plastic [
            reflectivity [0.5]
        ]
    ]
    entities [ this is an array
        [
            name [hero]
            material [metal]
        ]
        [
            name [monster]
            material [plastic]
        ]
    ]
]

aaa

Djedat
This is a comment

title [djed example]

owner [
  name [tester]
  dob [`2020-08-05T20:30:01+09:00[Asia/Tokyo][u-ca=japanese]`]
]

database [
  enabled [true]
  quoted [`true`]
  ports [
    [8000]
    [8001]
    [8002]
  ]
  data [ [[delta] [phi]] [3.14] ]
  temp targets [ cpu [79.5] case [72.0] ]
]

servers [
  alpha [
    ip [10.0.0.1]
    role [frontend]
  ]
  beta [
    ip [10.0.0.2]
    role [backend]
  ]

  ;discarded key [[with][a][value]]
  ;[discarded section]
]

embedded documents [
  some json [[json]`
  { 
    "id": "b3df0d",
    "count": 55,
    "props": {
      "return code": "59503a7b",
      "status": "pending"
    },
    "associated ids": [
      "3adf7c",
      "ff0df7",
      "3aa670"
    ],
    "parent": null 
  }
  `]
  more json [[json]`55`]
  json string [[json]`"\n\tsomething\u0000"`]
  json array [[json]`[1, 2, 3, 4, null]`]
]
comment
`key` [
  comment
  `value`
]
key2 [
  yaba
  daba
  seq
]
`` [empty]
inf [Infinity]
nan [NaN]
comment c
k`key k`k 
[v`value v`v]
↓
JSON
{
  "title": "djed example",
  "owner": {
    "name": "tester",
    "dob": "2020-08-05T20:30:01+09:00[Asia/Tokyo][u-ca=japanese]"
  },
  "database": {
    "enabled": true,
    "quoted": "true",
    "ports": [
      8000,
      8001,
      8002
    ],
    "data": [
      [
        "delta",
        "phi"
      ],
      3.14
    ],
    "temp targets": {
      "cpu": 79.5,
      "case": 72
    }
  },
  "servers": {
    "alpha": {
      "ip": "10.0.0.1",
      "role": "frontend"
    },
    "beta": {
      "ip": "10.0.0.2",
      "role": "backend"
    }
  },
  "embedded documents": {
    "some json": {
      "id": "b3df0d",
      "count": 55,
      "props": {
        "return code": "59503a7b",
        "status": "pending"
      },
      "associated ids": [
        "3adf7c",
        "ff0df7",
        "3aa670"
      ],
      "parent": null
    },
    "more json": 55,
    "json string": "\n\tsomething\u0000",
    "json array": [
      1,
      2,
      3,
      4,
      null
    ]
  },
  "key": "value",
  "key2": "",
  "": "empty",
  "inf": null,
  "nan": null,
  "key k": "value v"
}

a value

A value starts either at the beginning of a file (the top-level value) or after a [ (a nested value).

A value ends either at the end of a file (the top-level value) or before a ] (a nested value).

whitespace

The following characters are considered whitespace:

' '     (0x20)  space (SPC)
'\t'    (0x09)  horizontal tab (TAB)
'\n'    (0x0a)  newline (LF)
'\v'    (0x0b)  vertical tab (VT)
'\f'    (0x0c)  feed (FF)
'\r'    (0x0d)  carriage return (CR)

comments

Except the last line of a Djedat document, all lines outside a quoted string that don’t contain [ ] or `, are treated as comments and ignored:
Djedat
this is a comment
and so is this
this is not a comment [
this is also not a comment ]
neither is this `

See also ignored entries for an alternative to comments which allows [`].

quoted text

Text surrounded by backticks ` is called quoted text.
Djedat
`my text`
↓
JSON
"my text"
Quoted text can span multiple lines and contain [ or ].
Djedat
`my text with [brackets]
spanning multiple
lines`
↓
JSON
"my text with [brackets]\nspanning multiple\nlines"

Quoted text can also contain ` provided that it is not followed by zero or more whitespace characters and then [ or ], all on the same line.

`invalid quoted text [`]`
↓
JSON
error
`more invalid text `  [`
↓
JSON
error
If that should be the case, surround the text with '''. Repeat ' as many times as needed for the text to parse correctly:
Djedat
'`now it's valid [`]`'
↓
JSON
"now it's valid [`]"
Djedat
''`also valid [`]`''
↓
JSON
"also valid [`]"
Djedat
'''`valid as well `  [`'''
↓
JSON
"valid as well `  ["
todo: details about heredoc tags, rules; can’t contain , whitespace ignored

Quoted text preceded by any entries other than the [json] entry is an error.

[xml]`error`
↓
JSON
error
key [value]
key 2 [value 2]
`error`
↓
JSON
error

quoted text as JSON literals

Quoted text preceded by the [json] [entry] is interpreted as a JSON literal. It must contain valid JSON.
Djedat
[json]`{"key": 123}`
↓
JSON
{"key": 123}

quoted text -> JSON strings

Quoted text not preceded by any entries is interpreted as a JSON string.
Djedat
`text`
↓
JSON
"text"
By default, no escape sequences are interpreted inside quoted text.
Djedat
`\n\r\f\u0000`
↓
JSON
"\\n\\r\\f\\u0000"
You can change that by preceding your quoted string with \:
Djedat
\`\n\r\f\u0000`
↓
JSON
"\n\r\f\u0000"

This causes the JSON escape syntax to be interpreted. Your strings can still be multiline and contain any Unicode code point:

Djedat
\`
\r\f\u0000
`
↓
JSON
"\n\r\f\u0000\n"
Alternatively, you can restrict strings to the exact JSON syntax, by using JSON literals:
Djedat
[json]`"\n\r\f\u0000"`
↓
JSON
"\n\r\f\u0000"

unquoted lines of text

An unquoted line of text may occur at the end of a value.

If it is not empty and is preceded by any entries, it causes an error.

[entry]
error
↓
JSON
error
key [value]
error
↓
JSON
error
Note that whitespace around an unquoted line is ignored.
Djedat
    relevant part   

The line here is relevant part, without the surrounding whitespace.

An unquoted line which contains only whitespace is considered empty.

unquoted keywords

An [unquoted line] not preceded by any entries may be a keyword, translated into a specific JSON value.

The keywords are as follows.

keywords true and false -> JSON true and false

The keywords true and false, commonly interpreted as booleans, are translated into their JSON equivalents.
Djedat
true
↓
JSON
true
Djedat
false
↓
JSON
false

keyword null -> JSON null

The keyword null is translated into JSON null.
Djedat
null
↓
JSON
null

keyword seq -> empty JSON array

The keyword seq is translated into an empty JSON array.
Djedat
seq
↓
JSON
[]

keyword map -> empty JSON object

The keyword map is translated into an empty JSON object.
Djedat
map
↓
JSON
{}

numbers

An [unquoted line] not preceded by any entries which conforms to the JavaScript Number grammar is interpreted as a JavaScript number.
Djedat
123
↓
JSON
123

When using Djedat directly from JavaScript, Infinity and NaN are translated to proper IEEE754 values.

When translating to JSON, by default they are converted to null, in conformance with semantics of JSON.stringify.

TODO: An option shall be provided to convert Infinty and NaN to strings "Infinity" and "NaN" instead.

entries

There are two kinds of entries: key-value entries and value entries.

Key-value entries look like this:
Djedat
key [value]
Value entries look like this:
Djedat
[value]

Concatenating key-value entries and value entries (mixing them together) is an error:

key 1 [value 1] 
[value 2] 
key 2 [value 3]
[value 4]

ignored entries

Entries can be ignored by prefixing them with ;:
Djedat
;ignored key [ignored value]
;`ignored key` [ignored value]
;[ignored value 2]
These entries are treated as if they weren’t there and can be mixed together with other kinds of entries:
Djedat
;something [something]
[value 1]
[value 2]
Djedat
;[something]
key 1 [value 1]
key 2 [value 2]

Ignored entries can be used as multiline documentation comments and to temporarily exclude certain values from being considered (useful for configuration).

Ignored entries can also be used in combination with quoted text as comments which allow [`]:
Djedat
;[`[ignored]`]
;['`ignored [`]`']

top entry: reserved for metadata

An ignored entry at the very top of a Djedat document has special meaning: it contains metadata for Djedat parsers and other processors. This metadata can alter the standard meaning of the document, introduce pre- or post-processing steps or additional information.

The top entry metadata is optional.

If not specified, it is equivalent to the following default:
Djedat
;[0.1.0]

This declares the Djedat format version used for the document.

The current and default (assumed if undeclared) version is 0.1.0.

Any other forms of the top entry are left unspecified at this time.

maps -> JSON objects

Key-value entries can be concatenated together to form maps:

Djedat
key 1 [value 1]
key 2 [value 2]
key 3 [value 3]
↓
JSON
{
  "key 1": "value 1",
  "key 2": "value 2",
  "key 3": "value 3",
}
The line breaks are not necessary, so this is equivalent:
Djedat
key 1 [value 1] key 2 [value 2] key 3 [value 3]

Map keys must be unique, so this is an error:

key 1 [value 1] 
key 1 [error]
↓
JSON
error
Whitespace around the keys is ignored, so this is also an error:
Djedat
  key 1  [value 1]
key 1[value 2]
To include whitespace around a key, it must be quoted with `:
Djedat
`  key 1  ` [value 1]
↓
JSON
{
  "  key 1  ": "value 1"
}
The empty key must be quoted as well:
Djedat
`` [value of empty key]
↓
JSON
{
  "": "value of empty key"
}
Multiline keys also must be quoted:
Djedat
`multiline
key` [value]
↓
JSON
{
  "multiline\nkey": "value"
}
An empty map is introduced by the [keyword] map:
Djedat
map
↓
JSON
{}
Djedat
key [map]
↓
JSON
{
  "key": {}
}

seqs -> JSON arrays

Value entries can be concatenated together to form sequences (seqs for short):
Djedat
[value 1]
[value 2]
[value 3]
↓
JSON
[
  "value 1",
  "value 2",
  "value 3"
]
The line breaks are not necessary, so this is equivalent:
Djedat
[value 1][value 2][value 3]
Whitespace around the values is ignored, so this is also equivalent:
Djedat
  [value 1]
[value 2] [value 3]
An empty seq is introduced by the [keyword] seq:
Djedat
seq
↓
JSON
[]
Djedat
key [seq]
↓
JSON
{
  "key": []
}

nesting maps and seqs

Maps and seqs are values, so they can be nested with each other and other values:
Djedat
key [
  [value 1]
  [value 2]
  [[1][2][3]]
  [
    key 1 [value 1]
    key 2 [value 2]
  ]
]
↓
JSON
{
  "key": [
    "value 1",
    "value 2",
    [1, 2, 3],
    {
      "key 1": "value 1",
      "key 2": "value 2"
    }
  ]
}