# TOML Implementation in mq
# Based on TOML v1.0.0 specification
let INFINITY = 9223372036854775807
| let NEGATIVE_INFINITY = -9223372036854775808
| let NOT_A_NUMBER = to_number("NaN")
|
def _is_digit(char):
!is_none(char) && test(char, "[0-9]")
end
def _is_whitespace(char):
char == " " || char == "\t"
end
def _is_newline(char):
char == "\n" || char == "\r"
end
def _is_not_a_number(input):
starts_with(input, "nan")
end
def _is_alpha_numeric(char):
test(char, "^[a-zA-Z]+$") || _is_digit(char) || char == "_" || char == "-" || char == "[" || char == "]"
end
def _is_datetime_char(char):
_is_digit(char) || char == "-" || char == ":" || char == "T" || char == "t" || char == "Z" || char == "z" || char == "+" || char == "."
end
def _is_datetime(input):
# Check for date pattern (YYYY-MM-DD) or time pattern (HH:MM:SS)
test(input, "^[0-9]{4}-[0-9]{2}-[0-9]{2}") || test(input, "^[0-9]{2}:[0-9]{2}:[0-9]{2}")
end
def _skip_comment(input):
if (len(input) > 0 && input[0] == "#"):
do
var i = 1
| let result = while (i < len(input) && !_is_newline(input[i])):
i += 1 | i
end
| let skip_count = if (is_none(result)): 1 else: result
| trim(input[skip_count:len(input)])
end
else:
trim(input)
end
def _parse_key(input):
if (len(input) > 0 && (input[0] == "\"" || input[0] == "'")):
do
let quote_char = input[0]
| var i = 1
| let key_chars = ""
| let escaped = false
| let result = while (i < len(input) && (escaped || input[i] != quote_char)):
let result = if (escaped):
[key_chars + input[i], false, i + 1]
elif (input[i] == "\\"):
[key_chars, true, i + 1]
else:
[key_chars + input[i], escaped, i + 1]
| let key_chars = result[0]
| let escaped = result[1]
| i = result[2]
| result
end
# Add 1 to skip the closing quote character
| let key = result[0]
| let consumed = result[2] + 1
| [key, input[consumed:len(input)]]
end
else:
do
var i = 0
| let result = while (i < len(input) && _is_alpha_numeric(input[i])):
i += 1 | i
end
| let consumed = if (is_none(result)): 0 else: result
| let key = input[0:consumed]
| [key, input[consumed:len(input)]]
end
end
def _parse_dotted_key(input):
let keys = []
| let remaining = input
| let is_done = false
| let result = while (!is_done):
let remaining = trim(remaining)
| let key_result = _parse_key(remaining)
| let remaining = trim(key_result[1])
| let keys = keys + key_result[0]
| let is_done = len(remaining) == 0 || remaining[0] != "."
| let remaining = if (!is_done): remaining[1:len(remaining)] else: remaining
| [keys, remaining]
end
| [result[0], result[1]]
end
def _detect_number_type(input):
var i = 0
| let has_dot = false
| let result = while (i < len(input) && (_is_digit(input[i]) || input[i] == "." || input[i] == "+" || input[i] == "-" || input[i] == "_")):
let has_dot = has_dot || input[i] == "."
| i += 1
| [has_dot, i]
end
| if (is_none(result)): has_dot else: result[0]
end
def _is_boolean_start(input):
starts_with(input, "true") || starts_with(input, "false")
end
def _set_nested_value(table, keys, value):
let key = first(keys)
| if (len(keys) == 1):
if (!is_none(table[key]) && is_array(table[key])):
set(table, key, table[key] + value)
else:
set(table, key, value)
else:
set(table, key,
_set_nested_value(
if (table[key]):
table[key]
else: {}, keys[1:len(keys)], value))
end
# Parses a TOML string and returns the parsed data structure.
def _parse_toml_content(input):
def _parse_multiline_string(input, delimiter):
var i = 3
| let value_chars = ""
| let result = while (i + 2 < len(input) && input[i:i + 3] != delimiter):
let value_chars = value_chars + input[i]
| i += 1
| [value_chars, i]
end
| let value = if (is_none(result[0])): "" else: result[0]
| let consumed = if (is_none(result[1])): 4 else: result[1] + 3
| [value, input[consumed:len(input)]]
end
def _parse_string(input):
let quote_char = input[0]
| var i = 1
| var value_chars = ""
| var escaped = false
| let result = while (i < len(input) && (escaped || input[i] != quote_char)):
let result = if (escaped):
[value_chars + input[i], false, i + 1]
elif (input[i] == "\\"):
[value_chars, true, i + 1]
else:
[value_chars + input[i], escaped, i + 1]
| value_chars = result[0]
| escaped = result[1]
| i = result[2]
| result
end
| let value = if (is_none(result[0])): "" else: result[0]
| let consumed = if (is_none(result[2])): 2 else: result[2] + 1
| [value, input[consumed:len(input)]]
end
def _parse_integer(input):
var i = 0
| let has_sign = len(input) > 0 && (input[0] == "+" || input[0] == "-")
| let i = if (has_sign): 1 else: 0
| let result = while (i < len(input) && (_is_digit(input[i]) || input[i] == "_")):
i += 1 | i
end
| let consumed = if (is_none(result)): i else: result
| let value_str = do input[0:consumed] | replace("_", "");
| let value = to_number(value_str)
| [value, input[consumed:len(input)]]
end
def _parse_float(input):
var i = 0
| let has_sign = len(input) > 0 && (input[0] == "+" || input[0] == "-")
| let i = if (has_sign): 1 else: 0
| let result = while (i < len(input) && (_is_digit(input[i]) || input[i] == "." || input[i] == "_")):
i += 1 | i
end
| let consumed = if (is_none(result)): i else: result
| let value_str = do input[0:consumed] | replace("_", "");
| let value = to_number(value_str)
| [value, input[consumed:len(input)]]
end
def _parse_datetime(input):
var i = 0
| let result = while (i < len(input) && _is_datetime_char(input[i])):
i += 1 | i
end
| let consumed = if (is_none(result)): i else: result
| let datetime_str = input[0:consumed]
| [datetime_str, input[consumed:len(input)]]
end
def _parse_array(input):
let remaining = input[1:len(input)]
| let _values = []
| let remaining = trim(remaining)
| let result = while (len(remaining) > 0 && remaining[0] != "]"):
let value_result = _parse_value(remaining)
| let value = value_result[0]
| let remaining = trim(value_result[1])
| let _values = if (is_none(value)): _values else: _values + value
| let remaining = if (len(remaining) > 0 && remaining[0] == ","):
trim(remaining[1:len(remaining)])
else:
remaining
| [_values, remaining]
end
| let _values = if (is_none(result)): _values else: result[0]
| let remaining = if (is_none(result)): remaining else: result[1]
| let remaining = if (len(remaining) > 0 && remaining[0] == "]"): remaining[1:len(remaining)] else: remaining
| [_values, remaining]
end
def _parse_inline_table(input):
let remaining = input[1:len(input)]
| let table = {}
| let remaining = trim(remaining)
| let result = while (len(remaining) > 0 && remaining[0] != "}"):
let key_result = _parse_key(remaining)
| let key = key_result[0]
| let remaining = trim(key_result[1])
| let remaining = if (len(remaining) > 0 && remaining[0] == "="): remaining[1:len(remaining)] else: remaining
| let remaining = trim(remaining)
| let value_result = _parse_value(remaining)
| let value = value_result[0]
| let remaining = value_result[1]
| let table = set(table, key, value)
| let remaining = trim(remaining)
| let result = if (len(remaining) > 0 && remaining[0] == ","):
[table, trim(remaining[1:len(remaining)])]
else:
[table, remaining]
| let table = result[0]
| let remaining = result[1]
| result
end
| let table = result[0]
| let remaining = result[1]
| let remaining = if (len(remaining) > 0 && remaining[0] == "}"): remaining[1:len(remaining)] else: remaining
| [table, remaining]
end
def _parse_value(input):
let remaining = trim(input)
| if (len(remaining) == 0):
[None, remaining]
elif (starts_with(remaining, "\"\"\"")):
_parse_multiline_string(remaining, "\"\"\"")
elif (starts_with(remaining, "'''")):
_parse_multiline_string(remaining, "'''")
elif (len(remaining) > 0 && (remaining[0] == "\"" || remaining[0] == "'")):
_parse_string(remaining)
elif (len(remaining) > 0 && remaining[0] == "["):
_parse_array(remaining)
elif (len(remaining) > 0 && remaining[0] == "{"):
_parse_inline_table(remaining)
elif (_is_boolean_start(remaining)):
if (starts_with(remaining, "true")):
[true, remaining[4:len(remaining)]]
elif (starts_with(remaining, "false")):
[false, remaining[5:len(remaining)]]
else:
[None, remaining]
elif (starts_with(remaining, "inf") || starts_with(remaining, "+inf")):
if (starts_with(remaining, "+inf")):
[INFINITY, remaining[4:len(remaining)]]
else:
[INFINITY, remaining[3:len(remaining)]]
elif (starts_with(remaining, "-inf")):
[NEGATIVE_INFINITY, remaining[4:len(remaining)]]
elif (_is_not_a_number(remaining)):
[NOT_A_NUMBER, remaining[3:len(remaining)]]
elif (_is_datetime(remaining)):
_parse_datetime(remaining)
elif (len(remaining) > 0 && (_is_digit(remaining[0]) || remaining[0] == "+" || remaining[0] == "-")):
if (_detect_number_type(remaining)): _parse_float(remaining) else: _parse_integer(remaining)
else:
[None, remaining]
end
def _parse_key_value_pair(input):
let key_result = _parse_dotted_key(input)
| let keys = key_result[0]
| let remaining = trim(key_result[1])
| let remaining = if (len(remaining) > 0 && remaining[0] == "="): remaining[1:len(remaining)] else: remaining
| let remaining = trim(remaining)
| let value_result = _parse_value(remaining)
| let value = value_result[0]
| let remaining = value_result[1]
| [keys, value, remaining]
end
def _parse_section_header(input):
let is_array_section = starts_with(input, "[[")
| let temp_remaining = if (is_array_section): input[2:len(input)] else: input[1:len(input)]
| let key_result = _parse_dotted_key(temp_remaining)
| let keys = key_result[0]
| let last_key = last(keys)
| let last_key = if (is_array_section && ends_with(last_key, "]]")): last_key[0:-2] elif (ends_with(last_key, "]")): last_key[0:-1] else: last_key
| let temp_remaining = key_result[1]
| [keys[0:-1] + last_key, is_array_section, temp_remaining]
end
| let table = {}
| let current_section = []
| let is_array_section = false
| let remaining = input
| let result = while (len(remaining) > 0):
let remaining = trim(remaining)
| let remaining = _skip_comment(remaining)
| let result = if (len(remaining) == 0 || _is_newline(remaining[0])):
[table, current_section, is_array_section, if (len(remaining) > 0): remaining[1:len(remaining)] else: ""]
elif (remaining[0] == "["): do
let result = _parse_section_header(remaining)
| let _keys = result[0]
| let is_array_table = result[1]
| let temp_remaining = result[2]
| let temp_remaining = trim(temp_remaining)
| [table, _keys, is_array_table, temp_remaining]
end
elif (is_array_section):
do
let values = {}
| let temp_remaining = remaining
| let result = while (len(temp_remaining) > 0 && temp_remaining[0] != "["):
let result = _parse_key_value_pair(temp_remaining)
| let key = first(result[0])
| let value = result[1]
| if (is_empty(key)): break
| let values = insert(values, key, value)
| let temp_remaining = trim(result[2])
| [values, temp_remaining]
end
| [_set_nested_value(table, current_section, [result[0]]), [], false, result[1]]
end
else:
do
let result = _parse_key_value_pair(remaining)
| let keys = result[0]
| let value = result[1]
| let temp_remaining = result[2]
| let updated_table = _set_nested_value(table, current_section + keys, value)
| let temp_remaining = trim(temp_remaining)
| [updated_table, current_section, false, temp_remaining]
end
| let table = result[0]
| let current_section = result[1]
| let is_array_section = result[2]
| let remaining = result[3]
| result
end
| result[0]
end
def _escape_string(str):
replace(str, "\\", "\\\\") | replace("\"", "\\\"") | replace("\n", "\\n") | replace("\t", "\\t")
end
def _add_toml_lines(lines, key, value):
if (is_dict(value)):
do
let body_lines = map(keys(value), fn(k): k + " = " + _toml_stringify_value(value[k]);)
| let last_line = last(body_lines)
| let last_line = if (last_line): last_line + "\n" else: None
| lines + ["[" + key + "]"] + body_lines[0:len(body_lines) - 1] + last_line
end
elif (is_array(value)):
lines + ["[[" + key + "]]"] + if (len(value) == 0): _toml_stringify_value(first(value)) else: map(value, fn(v): _toml_stringify_value(v) + ", ";)
else: lines + [key + " = " + _toml_stringify_value(value)]
end
def _toml_stringify_value(value):
if (is_string(value)):
"\"" + _escape_string(value) + "\""
elif (is_number(value)):
to_string(value)
elif (is_bool(value)):
if (value): "true" else: "false"
elif (is_array(value)):
"[" + join(map(value, _toml_stringify_value), ", ") + "]"
elif (is_dict(value)):
"{" + join(map(keys(value), fn(k): k + " = " + _toml_stringify_value(value[k]);), ", ") + "}"
elif (is_none(value)):
""
else:
to_string(value)
end
# Parses a TOML string and returns the parsed data structure.
def toml_parse(input):
_parse_toml_content(to_string(input))
end
# Converts a data structure to a TOML string representation.
def toml_stringify(data):
let lines = []
| let result = keys(data)
| var i = 0
| let final_lines = while (i < len(result)):
let key = result[i]
| let value = data[key]
| let lines = _add_toml_lines(lines, key, value)
| i += 1
| lines
end
| join(final_lines, "\n")
end
# Converts a data structure to a JSON string representation.
def toml_to_json(data):
if (is_dict(data)):
"{" + join(map(keys(data), fn(k): "\"" + k + "\":" + toml_to_json(data[k]);), ",") + "}"
elif (is_array(data)):
"[" + join(map(data, toml_to_json), ",") + "]"
elif (is_string(data)):
"\"" + _escape_string(data) + "\""
elif (is_number(data)):
to_string(data)
elif (is_bool(data)):
if (data): "true" else: "false"
elif (is_none(data)):
"null"
else:
"\"" + to_string(data) + "\""
end
def _create_array_markdown_table(data):
let headers = keys(first(data))
| let header_row = "| " + join(headers, " | ") + " |"
| let separator_row = "| " + join(map(headers, fn(_): "---";), " | ") + " |"
| let data_rows = map(data, fn(row):
"| " + join(map(headers, fn(header): if (is_none(row[header])): "" else: replace(to_string(row[header]), "\n", "\\n");), " | ") + " |"
end)
| [header_row, separator_row] + data_rows
| join("\n")
end
def _create_dict_markdown_table(data):
let headers = ["Key", "Value"]
| let header_row = "| " + join(headers, " | ") + " |"
| let separator_row = "| " + join(map(headers, fn(_): "---";), " | ") + " |"
| let data_rows = map(keys(data), fn(key):
"| " + key + " | " + (if (is_none(data[key])): "" else: replace(to_string(data[key]), "\n", "\\n")) + " |"
end)
| [header_row, separator_row] + data_rows
| join("\n")
end
def _create_simple_markdown_table(data):
"| Value |\n| --- |\n| " + to_string(data) + " |"
end
# Converts a TOML data structure to a Markdown table.
def toml_to_markdown_table(data):
if (is_array(data)):
_create_array_markdown_table(data)
elif (is_dict(data)):
_create_dict_markdown_table(data)
else:
_create_simple_markdown_table(data)
end