Files
MontaukOS/template/sysroot/include/montauk/toml.h
T

574 lines
20 KiB
C++

/*
* toml.h
* TOML config file parser
* Supports: strings, integers, booleans, tables, arrays
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/heap.h>
#include <montauk/string.h>
#include <cstdint>
namespace montauk {
namespace toml {
enum class Type { String, Int, Bool, Array, Table };
struct Value;
// Dynamic array of Value pointers (for arrays and table entries)
struct ValueList {
Value** items;
int count;
int cap;
void init() { items = nullptr; count = 0; cap = 0; }
void push(Value* v) {
if (count >= cap) {
int newCap = cap ? cap * 2 : 8;
auto** buf = (Value**)montauk::malloc(newCap * sizeof(Value*));
if (items) {
montauk::memcpy(buf, items, count * sizeof(Value*));
montauk::mfree(items);
}
items = buf;
cap = newCap;
}
items[count++] = v;
}
void destroy(); // forward — defined after Value
};
struct Value {
Type type;
char* key; // owned, dotted path (e.g. "server.port"); null for array elements
union {
char* str; // Type::String — owned
int64_t ival; // Type::Int
bool bval; // Type::Bool
};
ValueList array; // Type::Array elements or Type::Table entries
static Value* make_string(const char* k, const char* s, int slen) {
auto* v = (Value*)montauk::malloc(sizeof(Value));
v->type = Type::String;
v->key = dup(k);
v->str = dupn(s, slen);
v->array.init();
return v;
}
static Value* make_int(const char* k, int64_t n) {
auto* v = (Value*)montauk::malloc(sizeof(Value));
v->type = Type::Int;
v->key = dup(k);
v->ival = n;
v->array.init();
return v;
}
static Value* make_bool(const char* k, bool b) {
auto* v = (Value*)montauk::malloc(sizeof(Value));
v->type = Type::Bool;
v->key = dup(k);
v->bval = b;
v->array.init();
return v;
}
static Value* make_array(const char* k) {
auto* v = (Value*)montauk::malloc(sizeof(Value));
v->type = Type::Array;
v->key = dup(k);
v->ival = 0;
v->array.init();
return v;
}
static Value* make_table(const char* k) {
auto* v = (Value*)montauk::malloc(sizeof(Value));
v->type = Type::Table;
v->key = dup(k);
v->ival = 0;
v->array.init();
return v;
}
void destroy() {
if (key) montauk::mfree(key);
if (type == Type::String && str) montauk::mfree(str);
array.destroy();
montauk::mfree(this);
}
// helpers
static char* dup(const char* s) {
if (!s) return nullptr;
int n = montauk::slen(s);
return dupn(s, n);
}
static char* dupn(const char* s, int n) {
char* d = (char*)montauk::malloc(n + 1);
montauk::memcpy(d, s, n);
d[n] = '\0';
return d;
}
};
inline void ValueList::destroy() {
for (int i = 0; i < count; i++) items[i]->destroy();
if (items) montauk::mfree(items);
items = nullptr;
count = 0;
cap = 0;
}
// ---- Document ----
struct Doc {
ValueList entries; // flat list of all top-level and nested values
void init() { entries.init(); }
void destroy() {
entries.destroy();
}
// Lookup by dotted key path (e.g. "server.port")
Value* get(const char* key) const {
for (int i = 0; i < entries.count; i++) {
if (entries.items[i]->key && montauk::streq(entries.items[i]->key, key))
return entries.items[i];
}
return nullptr;
}
// Typed accessors with defaults
const char* get_string(const char* key, const char* def = "") const {
auto* v = get(key);
return (v && v->type == Type::String) ? v->str : def;
}
int64_t get_int(const char* key, int64_t def = 0) const {
auto* v = get(key);
return (v && v->type == Type::Int) ? v->ival : def;
}
bool get_bool(const char* key, bool def = false) const {
auto* v = get(key);
return (v && v->type == Type::Bool) ? v->bval : def;
}
Value* get_array(const char* key) const {
auto* v = get(key);
return (v && v->type == Type::Array) ? v : nullptr;
}
Value* get_table(const char* key) const {
auto* v = get(key);
return (v && v->type == Type::Table) ? v : nullptr;
}
};
// ---- Parser ----
namespace detail {
struct Parser {
const char* src;
int pos;
int len;
char table_prefix[256]; // current [table] prefix
char peek() const { return pos < len ? src[pos] : '\0'; }
char next() { return pos < len ? src[pos++] : '\0'; }
bool at_end() const { return pos >= len; }
void skip_ws() {
while (pos < len && (src[pos] == ' ' || src[pos] == '\t')) pos++;
}
void skip_line() {
while (pos < len && src[pos] != '\n') pos++;
if (pos < len) pos++; // consume newline
}
void skip_ws_and_newlines() {
while (pos < len) {
char c = src[pos];
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
pos++;
else if (c == '#')
skip_line();
else
break;
}
}
// Build dotted key: prefix.key
void build_key(char* out, int outSz, const char* key, int keyLen) {
int p = 0;
if (table_prefix[0]) {
int plen = montauk::slen(table_prefix);
for (int i = 0; i < plen && p < outSz - 2; i++) out[p++] = table_prefix[i];
out[p++] = '.';
}
for (int i = 0; i < keyLen && p < outSz - 1; i++) out[p++] = key[i];
out[p] = '\0';
}
// Parse a bare key or quoted key, returns length
int parse_key(char* buf, int bufSz) {
skip_ws();
int n = 0;
if (peek() == '"') {
next(); // consume opening quote
while (!at_end() && peek() != '"' && n < bufSz - 1)
buf[n++] = next();
if (peek() == '"') next();
} else {
while (!at_end()) {
char c = peek();
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '_' || c == '-') {
buf[n++] = next();
if (n >= bufSz - 1) break;
} else {
break;
}
}
}
buf[n] = '\0';
return n;
}
// Parse a dotted key (e.g. server.host), returns full length written to buf
int parse_dotted_key(char* buf, int bufSz) {
int total = 0;
// Parse first segment
char seg[128];
int segLen = parse_key(seg, sizeof(seg));
for (int i = 0; i < segLen && total < bufSz - 1; i++)
buf[total++] = seg[i];
// Parse additional dotted segments
while (peek() == '.') {
next(); // consume dot
if (total < bufSz - 1) buf[total++] = '.';
segLen = parse_key(seg, sizeof(seg));
for (int i = 0; i < segLen && total < bufSz - 1; i++)
buf[total++] = seg[i];
}
buf[total] = '\0';
return total;
}
int64_t parse_integer() {
bool neg = false;
if (peek() == '-') { neg = true; next(); }
else if (peek() == '+') { next(); }
// Hex, octal, binary
if (peek() == '0' && pos + 1 < len) {
char c2 = src[pos + 1];
if (c2 == 'x' || c2 == 'X') {
pos += 2;
int64_t val = 0;
while (!at_end()) {
char c = peek();
if (c == '_') { next(); continue; }
int d = -1;
if (c >= '0' && c <= '9') d = c - '0';
else if (c >= 'a' && c <= 'f') d = 10 + c - 'a';
else if (c >= 'A' && c <= 'F') d = 10 + c - 'A';
if (d < 0) break;
val = val * 16 + d;
next();
}
return neg ? -val : val;
}
if (c2 == 'o') {
pos += 2;
int64_t val = 0;
while (!at_end()) {
char c = peek();
if (c == '_') { next(); continue; }
if (c < '0' || c > '7') break;
val = val * 8 + (c - '0');
next();
}
return neg ? -val : val;
}
if (c2 == 'b') {
pos += 2;
int64_t val = 0;
while (!at_end()) {
char c = peek();
if (c == '_') { next(); continue; }
if (c != '0' && c != '1') break;
val = val * 2 + (c - '0');
next();
}
return neg ? -val : val;
}
}
int64_t val = 0;
while (!at_end()) {
char c = peek();
if (c == '_') { next(); continue; }
if (c < '0' || c > '9') break;
val = val * 10 + (c - '0');
next();
}
return neg ? -val : val;
}
// Parse string value (opening quote already detected but not consumed)
// Returns heap-allocated string
char* parse_string_value(int* outLen) {
char quote = next(); // consume opening " or '
bool literal = (quote == '\'');
// Multi-line? (""" or ''')
bool multi = false;
if (pos + 1 < len && src[pos] == quote && src[pos + 1] == quote) {
pos += 2;
multi = true;
// Skip first newline after opening delimiter
if (peek() == '\r') next();
if (peek() == '\n') next();
}
// Collect into temp buffer
char buf[4096];
int n = 0;
while (!at_end() && n < (int)sizeof(buf) - 1) {
if (multi) {
// Check for closing triple-quote
if (pos + 2 < len && src[pos] == quote &&
src[pos+1] == quote && src[pos+2] == quote) {
pos += 3;
break;
}
} else {
if (peek() == quote) { next(); break; }
if (peek() == '\n') break; // unterminated single-line
}
if (!literal && peek() == '\\') {
next(); // consume backslash
char esc = next();
switch (esc) {
case 'n': buf[n++] = '\n'; break;
case 't': buf[n++] = '\t'; break;
case 'r': buf[n++] = '\r'; break;
case '\\': buf[n++] = '\\'; break;
case '"': buf[n++] = '"'; break;
case '\r':
if (peek() == '\n') next();
skip_ws();
break;
case '\n':
skip_ws();
break;
default: buf[n++] = esc; break;
}
} else {
buf[n++] = next();
}
}
*outLen = n;
return Value::dupn(buf, n);
}
Value* parse_value(const char* fullKey) {
skip_ws();
char c = peek();
// String
if (c == '"' || c == '\'') {
int slen = 0;
char* s = parse_string_value(&slen);
auto* v = Value::make_string(fullKey, s, slen);
montauk::mfree(s);
return v;
}
// Boolean
if (montauk::starts_with(src + pos, "true")) {
pos += 4;
return Value::make_bool(fullKey, true);
}
if (montauk::starts_with(src + pos, "false")) {
pos += 5;
return Value::make_bool(fullKey, false);
}
// Array
if (c == '[') {
next(); // consume [
auto* arr = Value::make_array(fullKey);
skip_ws_and_newlines();
while (!at_end() && peek() != ']') {
auto* elem = parse_value(nullptr);
if (elem) arr->array.push(elem);
skip_ws_and_newlines();
if (peek() == ',') next();
skip_ws_and_newlines();
}
if (peek() == ']') next();
return arr;
}
// Inline table
if (c == '{') {
next(); // consume {
auto* tbl = Value::make_table(fullKey);
skip_ws_and_newlines();
while (!at_end() && peek() != '}') {
char key[128];
int keyLen = parse_dotted_key(key, sizeof(key));
if (keyLen == 0) { skip_line(); continue; }
skip_ws();
if (peek() == '=') next();
skip_ws();
// Build full dotted key for inline table entry
char entryKey[256];
int p = 0;
if (fullKey) {
int flen = montauk::slen(fullKey);
for (int i = 0; i < flen && p < 254; i++) entryKey[p++] = fullKey[i];
entryKey[p++] = '.';
}
for (int i = 0; i < keyLen && p < 255; i++) entryKey[p++] = key[i];
entryKey[p] = '\0';
auto* val = parse_value(entryKey);
if (val) tbl->array.push(val);
skip_ws();
if (peek() == ',') next();
skip_ws();
}
if (peek() == '}') next();
return tbl;
}
// Integer (includes negative)
if ((c >= '0' && c <= '9') || c == '+' || c == '-') {
int64_t n = parse_integer();
return Value::make_int(fullKey, n);
}
// Unknown — skip the rest of the line
skip_line();
return nullptr;
}
void parse(Doc* doc) {
table_prefix[0] = '\0';
while (!at_end()) {
skip_ws_and_newlines();
if (at_end()) break;
char c = peek();
// Table header: [name] or [name.sub]
if (c == '[') {
next();
bool array_table = false;
if (peek() == '[') { next(); array_table = true; }
skip_ws();
int plen = parse_dotted_key(table_prefix, sizeof(table_prefix));
skip_ws();
if (peek() == ']') next();
if (array_table && peek() == ']') next();
// Register the table itself
auto* tbl = Value::make_table(table_prefix);
doc->entries.push(tbl);
skip_line();
continue;
}
// Key = value
char key[128];
int keyLen = parse_dotted_key(key, sizeof(key));
if (keyLen == 0) { skip_line(); continue; }
skip_ws();
if (peek() != '=') { skip_line(); continue; }
next(); // consume =
skip_ws();
char fullKey[256];
build_key(fullKey, sizeof(fullKey), key, keyLen);
Value* val = parse_value(fullKey);
if (val) {
doc->entries.push(val);
// For inline tables, also flatten entries into doc
if (val->type == Type::Table) {
for (int i = 0; i < val->array.count; i++) {
// Clone entry into doc's flat list so dotted lookup works
auto* e = val->array.items[i];
Value* clone = nullptr;
if (e->type == Type::String)
clone = Value::make_string(e->key, e->str, montauk::slen(e->str));
else if (e->type == Type::Int)
clone = Value::make_int(e->key, e->ival);
else if (e->type == Type::Bool)
clone = Value::make_bool(e->key, e->bval);
if (clone) doc->entries.push(clone);
}
}
}
// Consume rest of line (trailing comment, etc.)
skip_ws();
if (peek() == '#') skip_line();
else if (peek() == '\r' || peek() == '\n') {
if (peek() == '\r') next();
if (peek() == '\n') next();
}
}
}
};
} // namespace detail
// ---- Public API ----
// Parse a TOML string into a Doc. Caller must call doc.destroy() when done.
inline Doc parse(const char* text) {
Doc doc;
doc.init();
detail::Parser p;
p.src = text;
p.pos = 0;
p.len = montauk::slen(text);
p.table_prefix[0] = '\0';
p.parse(&doc);
return doc;
}
// Parse from a file descriptor (reads entire file into buffer first).
// Requires montauk::syscall read/fstat or similar — omitted here for portability.
// Users can read a file into a buffer and call parse() directly.
} // namespace toml
} // namespace montauk