# TOON implementation in mq
def _toon_is_numeric_like(s):
let ss = to_string(s)
| is_regex_match(ss, "^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$") || is_regex_match(ss, "^0[0-9]+$")
end
def _toon_escape_string(s):
replace(s, "\\", "\\\\") | replace("\"", "\\\"") | replace("\n", "\\n") | replace("\r", "\\r") | replace("\t", "\\t")
end
def _toon_needs_quote(v, delim):
let s = to_string(v)
| if (s == ""): true
elif (starts_with(s, " ") || ends_with(s, " ")): true
elif (s == "true" || s == "false" || s == "null"): true
elif (_toon_is_numeric_like(s)): true
elif (contains(s, ":") || contains(s, "\"") || contains(s, "\\")): true
elif (contains(s, "[") || contains(s, "]") || contains(s, "{") || contains(s, "}")): true
elif (contains(s, "\n") || contains(s, "\r") || contains(s, "\t")): true
elif (contains(s, delim)): true
elif (starts_with(s, "-")): true
else: false
end
def _toon_stringify_primitive(v, delim):
if (is_string(v)):
if (_toon_needs_quote(v, delim)): "\"" + _toon_escape_string(v) + "\""
else: v
elif (is_number(v)):
do
let s = to_string(v)
| if (s == "-0"): "0" else: s
end
elif (is_bool(v)): if (v): "true" else: "false"
elif (is_none(v)): "null"
else: to_string(v)
end
def _toon_is_primitive(v):
is_string(v) || is_number(v) || is_bool(v) || is_none(v)
end
def _toon_indent(n): repeat(" ", n);
# --- Stringify ---
def _toon_stringify_dict(data, level, delim):
let ind = _toon_indent(level)
| let next_ind = _toon_indent(level + 1)
| let lines = map(entries(data), fn(e):
let k = e[0] | let v = e[1]
| let ks = if (is_regex_match(k, "^[A-Za-z_][A-Za-z0-9_.]*$")): k else: "\"" + _toon_escape_string(k) + "\""
| if (_toon_is_primitive(v)): ks + ": " + _toon_stringify_primitive(v, delim)
else:
do
let value = _toon_stringify_recursive(v, level + 1, delim)
| if (starts_with(value, "[")): ks + ind + value else: ks + ":\n" + next_ind + value
end
end)
| join(lines, "\n" + ind)
end
def _toon_stringify_tabular(data, level, delim):
let next_ind = _toon_indent(level + 1)
| let fk = keys(first(data))
| let header = "[" + to_string(len(data)) + "]{" + join(fk, delim) + "}:"
| let rows = map(data, fn(item):
join(map(fk, fn(k): _toon_stringify_primitive(item[k], delim);), delim);)
| header + "\n" + next_ind + join(rows, "\n" + next_ind)
end
def _toon_stringify_expanded_item(v, level, delim):
if (is_dict(v) && !is_empty(v)):
do
let ks = keys(v) | let fk = ks[0] | let fv = v[fk]
| let fks = if (is_regex_match(fk, "^[A-Za-z_][A-Za-z0-9_.]*$")): fk else: "\"" + _toon_escape_string(fk) + "\""
| let start = if (_toon_is_primitive(fv)): "- " + fks + ": " + _toon_stringify_primitive(fv, delim)
else: "- " + fks + ":\n" + _toon_indent(level + 2) + _toon_stringify_recursive(fv, level + 2, delim)
| let other_ks = slice(ks, 1, len(ks))
| if (is_empty(other_ks)): start
else:
do
let sub_ind = _toon_indent(level + 2)
| let lines = map(other_ks, fn(k):
let ks_sub = if (is_regex_match(k, "^[A-Za-z_][A-Za-z0-9_.]*$")): k else: "\"" + _toon_escape_string(k) + "\""
| let val = v[k]
| if (_toon_is_primitive(val)): ks_sub + ": " + _toon_stringify_primitive(val, delim)
else: ks_sub + ":\n" + _toon_indent(level + 3) + _toon_stringify_recursive(val, level + 3, delim)
end)
| start + "\n" + sub_ind + join(lines, "\n" + sub_ind)
end
end
else: "- " + _toon_stringify_recursive(v, level + 1, delim)
end
def _toon_stringify_array(data, level, delim):
if (all(data, _toon_is_primitive)):
do
let vals = map(data, fn(v): _toon_stringify_primitive(v, delim);)
| "[" + to_string(len(data)) + "]: " + join(vals, delim)
end
else:
do
let fk = if (is_dict(first(data))): keys(first(data)) else: []
| let is_tabular = if (is_empty(fk)): false
else: all(data, fn(item):
let ks = keys(item)
| len(ks) == len(fk) && all(ks, fn(k): contains(fk, k);) && all(values(item), _toon_is_primitive);)
| if (is_tabular): _toon_stringify_tabular(data, level, delim)
else:
do
let nind = _toon_indent(level + 1)
| let items = map(data, fn(v): _toon_stringify_expanded_item(v, level, delim);)
| "[" + to_string(len(data)) + "]:\n" + nind + join(items, "\n" + nind)
end
end
end
def _toon_stringify_recursive(data, level, delim):
if (_toon_is_primitive(data)): _toon_stringify_primitive(data, delim)
elif (is_dict(data)):
if (is_empty(data)): "{}" else: _toon_stringify_dict(data, level, delim)
elif (is_array(data)):
if (is_empty(data)): "[0]:" else: _toon_stringify_array(data, level, delim)
else: to_string(data)
end
# To convert a data structure into a TOON string
def toon_stringify(data): _toon_stringify_recursive(data, 0, ",");
# To parse a TOON string into a data structure
def toon_parse(input):
_toon_parse(to_string(input))
end