Subject to change
Diatom is a very simple data serialization language, it tries to be human readable and gets rid of a lot of useless punctuation.
This spec is a newer version of Diatom, designed to be more "general use" than the older format
A simple Diatom file is very similar to JSON, but commas and colons are not required, they are just cosmetics used for readability.
menu {
id file
value "File"
popup {
menuitem [
{ value "New", onclick "CreateNewDoc()" }
{ value "Open", onclick "OpenDoc()" }
{ value "Close", onclick "CloseDoc()" }
]
}
}
Diatom has 5 value types:
- Number: integers
-1 42and decimals3.14 -1.44 - String: normal strings
"Hello, world"can use escape sequences like\n\tetc.; raw strings`Hello, world`cannot - Symbol: an identifier made of alphanumerics and the symbols
_-/?!*, must start with a letter - Array: a list of values
[ 1 2 "hi" no-commas ] - Map: a mapping of identifiers to values
{ name "Diatom", amount 14, is-boxed? no }
There are no booleans or null.
Any value can have a tag before it, tags are symbols preceded by @.
Tags are like type annotations to be used during parsing.
items [
@Potion { name "Grass" description `turns you into grass` }
@Ring { name "Brass Ring" description `its very cold, will dedigitate your finger` }
]
Returns the last error or ok if there was none.
Returns the last tag read or empty if there was none.
Return if the next thing is a tag or symbol, number, string, array or map.
Reads a tag, returns empty string if there is none.
Does not error if tag is absent.
Reads a symbol, returns empty string on failure.
- error
not a symbolif there is no symbol
Reads a string, returns empty string on failure.
- error
not a stringif there is no string - error
unterminated stringif unterminated
Reads a number and expects it to be an integer. If possible should be a big integer, otherwise signed 64 bits should be the default.
- Input
42→ Output42 - Input
42.5→ Output42, errornot an integer - Input
name→ Output0, errornot a number - (no big int) Input
49340983473984832→ Output0, errorinteger overflow
ReadInteger32() → i32ReadInteger64() → i64ReadBigInteger() → bigint
Reads a number. Should be 64 bit floating point.
- Input
42→ Output42.0 - Input
42.5→ Output42.5 - Input
name→ Output0, errornot a number
Used to iterate through an array:
ReadArray(inside) →- if
not inside: try to consume[, otherwise errornot an arrayand→ inside = false - if input has
]consume and→ inside = false - else
→ inside = true
- if
Python example (in python the iterator variant is preferred):
list = []
i = False
while diatom.read_array(i):
n = diatom.read_number()
list.append(n)C example:
double numbers[6] = {0};
int inside = 0;
for (int i = 0; diatomReadArray(diatom, &inside); ++i) {
if (diatomError(diatom)) return false;
if (i >= 6) {
printf("Too many items\n");
return false;
}
double n = diatomReadNumber(diatom);
if (diatomError(diatom)) return false;
numbers[i-1] = n;
}
return true;IterArray() → Iteratorfor languages with iterators
Used to iterate through a map:
ReadKey(inside) →- if
not inside: try to consume{, otherwise errornot a mapand→ "", inside = false - if input has
}consume and→ "", inside = false - try
key = ReadSymbol(), otherwise→ "", inside = false - if input has
}errormap missing valueand→ key, inside = false - else
→ key, inside = true
- if
Python example (in python the iterator variant is preferred):
dict = {}
i = False
key, i = diatom.read_key(i)
while i:
n = diatom.read_number()
dict[key] = n
key, i = diatom.read_key(i)C example:
int inside = 0;
int len;
char *key;
for (int i = 0; key = diatomReadKey(diatom, &inside, &len); ++i) {
if (diatomError(diatom)) return false;
double n = diatomReadNumber(diatom);
if (diatomError(diatom)) return false;
printf("%.*s => %f\n", len, key, n);
}
return true;IterMap() → Iteratorfor languages with iterators
Some functions can take an optional argument to expect a value and error if not found, like:
ReadString("name")ReadSymbol("name")ReadInteger(2)ReadNumber(2.5)
items [
@Potion { name "Grass" description `turns you into grass` }
@Ring { name "Brass Ring" description `its very cold, will dedigitate your finger` }
]
Python, with exceptions and expectations.
ITEM_SCHEMA = {
"name": diatom.read_string,
"description": diatom.read_string,
}
ITEMS_NAME = {
"Potion": Potion,
"Ring": Ring,
}
def read_item():
try:
diatom.read_key("items")
for i in diatom.iter_array():
tag = diatom.read_tag()
constructor = ITEMS_NAME.get(tag, None)
if constructor is None:
raise Exception("Invalid Item")
data = {}
for key in diatom.iter_map():
f = ITEM_SCHEMA.get(key, None)
if f is None: raise Exception("Invalid field")
dict[key] = f()
items.register(constructor(**data))
except:
print("Error parsing document")