216 lines
5.6 KiB
Plaintext
216 lines
5.6 KiB
Plaintext
// Simple TOML parser for cell modules
|
|
// Supports basic TOML features needed for the module system
|
|
|
|
function parse_toml(text) {
|
|
if (!is_text(text)) return null
|
|
var lines = text.split('\n')
|
|
var result = {}
|
|
var current_section = result
|
|
var current_section_name = ''
|
|
|
|
for (var i = 0; i < lines.length; i++) {
|
|
var line = lines[i].trim()
|
|
|
|
// Skip empty lines and comments
|
|
if (!line || line.startsWith('#')) continue
|
|
|
|
// Section header
|
|
if (line.startsWith('[') && line.endsWith(']')) {
|
|
var section_path = parse_key_path(line.slice(1, -1))
|
|
current_section = result
|
|
// Reconstruct name for debugging/legacy (not strictly needed for object construction)
|
|
current_section_name = section_path.join('.')
|
|
|
|
for (var j = 0; j < section_path.length; j++) {
|
|
var key = section_path[j]
|
|
if (!current_section[key]) {
|
|
current_section[key] = {}
|
|
}
|
|
current_section = current_section[key]
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Key-value pair
|
|
var eq_index = line.indexOf('=')
|
|
if (eq_index > 0) {
|
|
var key_part = line.substring(0, eq_index).trim()
|
|
var value = line.substring(eq_index + 1).trim()
|
|
|
|
// Handle quoted keys in key-value pairs too if needed?
|
|
// For now assuming simple keys or quoted keys
|
|
var key = parse_key(key_part)
|
|
|
|
// Parse value
|
|
if (value.startsWith('"') && value.endsWith('"')) {
|
|
// String - unescape quotes
|
|
current_section[key] = value.slice(1, -1).replace(/\\"/g, '"')
|
|
} else if (value.startsWith('[') && value.endsWith(']')) {
|
|
// Array
|
|
current_section[key] = parse_array(value)
|
|
} else if (value == 'true' || value == 'false') {
|
|
// Boolean
|
|
current_section[key] = value == 'true'
|
|
} else if (is_number(value)) {
|
|
// Number
|
|
current_section[key] = Number(value)
|
|
} else {
|
|
// Unquoted string
|
|
current_section[key] = value
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
function parse_key(str) {
|
|
if (str.startsWith('"') && str.endsWith('"')) {
|
|
return str.slice(1, -1).replace(/\\"/g, '"')
|
|
}
|
|
return str
|
|
}
|
|
|
|
// Split a key path by dots, respecting quotes
|
|
function parse_key_path(str) {
|
|
var parts = []
|
|
var current = ''
|
|
var in_quote = false
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
var c = str[i]
|
|
if (c == '"' && (i==0 || str[i-1] != '\\')) {
|
|
in_quote = !in_quote
|
|
// We don't verify if it's strictly correct TOML quote usage, just rudimentary
|
|
} else if (c == '.' && !in_quote) {
|
|
parts.push(parse_key(current.trim()))
|
|
current = ''
|
|
continue
|
|
}
|
|
current += c
|
|
}
|
|
if (current.trim().length > 0)
|
|
parts.push(parse_key(current.trim()))
|
|
|
|
return parts
|
|
}
|
|
|
|
function parse_array(str) {
|
|
// Remove brackets
|
|
str = str.slice(1, -1).trim()
|
|
if (!str) return []
|
|
|
|
var items = []
|
|
var current = ''
|
|
var in_quotes = false
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
var char = str[i]
|
|
|
|
if (char == '"' && (i == 0 || str[i-1] != '\\')) {
|
|
in_quotes = !in_quotes
|
|
current += char
|
|
} else if (char == ',' && !in_quotes) {
|
|
items.push(parse_value(current.trim()))
|
|
current = ''
|
|
} else {
|
|
current += char
|
|
}
|
|
}
|
|
|
|
if (current.trim()) {
|
|
items.push(parse_value(current.trim()))
|
|
}
|
|
|
|
return items
|
|
}
|
|
|
|
function parse_value(str) {
|
|
if (str.startsWith('"') && str.endsWith('"')) {
|
|
return str.slice(1, -1).replace(/\\"/g, '"')
|
|
} else if (str == 'true' || str == 'false') {
|
|
return str == 'true'
|
|
} else if (!isNaN(Number(str))) {
|
|
return Number(str)
|
|
} else {
|
|
return str
|
|
}
|
|
}
|
|
|
|
function encode_toml(obj) {
|
|
var result = []
|
|
|
|
function encode_value(value) {
|
|
if (is_text(value)) {
|
|
return '"' + value.replace(/"/g, '\\"') + '"'
|
|
} else if (is_logical(value)) {
|
|
return value ? 'true' : 'false'
|
|
} else if (is_number(value)) {
|
|
return text(value)
|
|
} else if (is_array(value)) {
|
|
var items = []
|
|
for (var i = 0; i < value.length; i++) {
|
|
items.push(encode_value(value[i]))
|
|
}
|
|
return '[' + items.join(', ') + ']'
|
|
}
|
|
return text(value)
|
|
}
|
|
|
|
function quote_key(k) {
|
|
if (k.includes('.') || k.includes('"') || k.includes(' ')) {
|
|
return '"' + k.replace(/"/g, '\\"') + '"'
|
|
}
|
|
return k
|
|
}
|
|
|
|
// First pass: encode top-level simple values
|
|
var keys = array(obj)
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var key = keys[i]
|
|
var value = obj[key]
|
|
if (!is_object(value)) {
|
|
result.push(quote_key(key) + ' = ' + encode_value(value))
|
|
}
|
|
}
|
|
|
|
// Second pass: encode nested objects
|
|
function encode_section(obj, path) {
|
|
var keys = array(obj)
|
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var key = keys[i]
|
|
var value = obj[key]
|
|
|
|
if (is_object(value)) {
|
|
// Nested object - create section
|
|
// We MUST quote the key segment if it has dots, otherwise it becomes a nested table path
|
|
var quoted = quote_key(key)
|
|
var section_path = path ? path + '.' + quoted : quoted
|
|
|
|
result.push('[' + section_path + ']')
|
|
|
|
// First encode direct properties of this section
|
|
var section_keys = array(value)
|
|
for (var j = 0; j < section_keys.length; j++) {
|
|
var sk = section_keys[j]
|
|
var sv = value[sk]
|
|
if (!is_object(sv)) {
|
|
result.push(quote_key(sk) + ' = ' + encode_value(sv))
|
|
}
|
|
}
|
|
|
|
// Then encode nested sections
|
|
encode_section(value, section_path)
|
|
}
|
|
}
|
|
}
|
|
|
|
encode_section(obj, '')
|
|
return result.join('\n')
|
|
}
|
|
|
|
return {
|
|
decode: parse_toml,
|
|
encode: encode_toml
|
|
} |