diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 8ab0327..56092dd 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,6 +4,7 @@ "name": "all", "defines": [], "includePath": [ + "src", "include", "../tlibc/include", "${default}" diff --git a/README.md b/README.md index bad787f..e481cc3 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ A fork of [libtoml](https://github.com/brglng/libtoml) rewritten to use [tlibc]( ``` git clone https://timerix.ddns.net/git/Timerix/tlibtoml.git ``` -2. Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild.git). -3. Clone [tlibc](https://timerix.ddns.net/git/Timerix/tlibtoml). By default +2. Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild). +3. Clone [tlibc](https://timerix.ddns.net/git/Timerix/tlibc). By default `dependencies/tlibc.config` expects that `tlibc/` is present in the same directory as `tlibtoml/`. If you cloned it to another directory, change `DEPENDENCIES_DIR` in `tlibtoml/project.user.config`. ``` diff --git a/include/tlibtoml/toml.h b/include/tlibtoml/toml.h index fad4a41..867c558 100644 --- a/include/tlibtoml/toml.h +++ b/include/tlibtoml/toml.h @@ -9,30 +9,28 @@ extern "C" { #endif #include -#include "tlibc/std.h" +#include "tlibc/tlibc.h" +#include "tlibc/time.h" +#include "tlibc/errors.h" #include "tlibc/string/str.h" #include "tlibc/collections/HashMap.h" -typedef struct tm TomlDateTime; +typedef DateTime TomlDateTime; typedef HashMap(TomlValue) TomlTable; -typedef enum { - TOML_OK, - TOML_ERR, - TOML_ERR_OS, - TOML_ERR_NOMEM, - TOML_ERR_SYNTAX, - TOML_ERR_UNICODE -} TomlErrCode; - -typedef struct { - TomlErrCode code; - NULLABLE(char*) message; -} TomlErr; +typedef enum TlibtomlError { + TLIBTOML_OK, + TLIBTOML_ERR, + TLIBTOML_ERR_OS, + TLIBTOML_ERR_NOMEM, + TLIBTOML_ERR_SYNTAX, + TLIBTOML_ERR_UNICODE +} TlibtomlError; +ErrorCodePage_declare(TLIBTOML); typedef struct TomlValue TomlValue; -typedef struct { +typedef struct TomlArray { TomlValue* elements; u64 len; u64 _capacity; @@ -40,70 +38,94 @@ typedef struct { typedef enum { - TOML_INVALID_TYPE, - TOML_TABLE, - TOML_ARRAY, - TOML_STRING, - TOML_INTEGER, - TOML_FLOAT, - TOML_DATETIME, - TOML_BOOLEAN, + TLIBTOML_INVALID_TYPE, + TLIBTOML_TABLE, + TLIBTOML_ARRAY, + TLIBTOML_STRING, + TLIBTOML_INTEGER, + TLIBTOML_FLOAT, + TLIBTOML_DATETIME, + TLIBTOML_BOOLEAN, } TomlType; struct TomlValue { TomlType type; union { - TomlTable* table; - TomlArray* array; - str s; i64 i; f64 f; - TomlDateTime* dt; bool b; + str* s; + TomlArray* array; + TomlTable* table; + TomlDateTime* dt; } value; }; -const TomlErr* toml_err(void); -void toml_err_clear(void); +////////////////////////////////////////////////////////////////////////////// +// // +// toml.c // +// // +////////////////////////////////////////////////////////////////////////////// -TomlTable* toml_table_new(void); -void toml_table_free(TomlTable* self); - -void toml_table_set(TomlTable* self, str key, TomlValue value); -TomlValue* toml_table_get(const TomlTable* self, str key); -TomlTable* toml_table_get_table(const TomlTable* self, str key); -TomlArray* toml_table_get_array(const TomlTable* self, str key); -str toml_table_get_str(const TomlTable* self, str key); -i64 toml_table_get_integer(const TomlTable* self, str key); -f64 toml_table_get_float(const TomlTable* self, str key); -TomlDateTime* toml_table_get_datetime(const TomlTable* self, str key); -bool toml_table_get_bool(const TomlTable* self, str key); - -TomlArray* toml_array_new(void); -void toml_array_free(TomlArray* self); -void toml_array_append(TomlArray* self, TomlValue value); - -TomlValue TomlValue_new(TomlType type); -TomlValue TomlValue_new_string(TomlType type); -TomlValue TomlValue_new_table(void); -TomlValue TomlValue_new_array(void); -TomlValue TomlValue_new_integer(i64 integer); -TomlValue TomlValue_new_float(f64 flt); -TomlValue TomlValue_new_datetime(void); -TomlValue TomlValue_new_bool(bool b); -TomlValue TomlValue_from_str(str s); -void TomlValue_destroy(TomlValue* self); - -TomlTable* toml_load_str(str s); -TomlTable* toml_load_file(FILE* file); -TomlTable* toml_load_filename(cstr filename); +Result(TomlTable*) toml_load_str(str s); +Result(TomlTable*) toml_load_file(FILE* file); +Result(TomlTable*) toml_load_filename(cstr filename); /* TODO: implement dump functions str toml_dump_str(const TomlTable* self, TomlErr *err); void toml_dump_file(const TomlTable* self, FILE* file, TomlErr *err); */ + +////////////////////////////////////////////////////////////////////////////// +// // +// TomlTable.c // +// // +////////////////////////////////////////////////////////////////////////////// + +TomlTable* TomlTable_new(void); +void TomlTable_free(TomlTable* self); + +void TomlTable_set(TomlTable* self, str key, TomlValue value); +NULLABLE(TomlValue*) TomlTable_get(const TomlTable* self, str key); +Result(TomlTable*) TomlTable_get_table(const TomlTable* self, str key); +Result(TomlArray*) TomlTable_get_array(const TomlTable* self, str key); +Result(str*) TomlTable_get_str(const TomlTable* self, str key); +Result(i64) TomlTable_get_integer(const TomlTable* self, str key); +Result(f64) TomlTable_get_float(const TomlTable* self, str key); +Result(bool) TomlTable_get_bool(const TomlTable* self, str key); +Result(TomlDateTime*) TomlTable_get_datetime(const TomlTable* self, str key); + + +////////////////////////////////////////////////////////////////////////////// +// // +// TomlArray.c // +// // +////////////////////////////////////////////////////////////////////////////// + +TomlArray* TomlArray_new(void); +void TomlArray_free(TomlArray* self); +void TomlArray_append(TomlArray* self, TomlValue value); + + +////////////////////////////////////////////////////////////////////////////// +// // +// TomlValue.c // +// // +////////////////////////////////////////////////////////////////////////////// + +TomlValue TomlValue_new(TomlType type); +TomlValue TomlValue_new_table(void); +TomlValue TomlValue_new_array(void); +TomlValue TomlValue_new_integer(i64 integer); +TomlValue TomlValue_new_float(f64 flt); +TomlValue TomlValue_new_datetime(void); +TomlValue TomlValue_new_bool(bool b); +/// copies the string +TomlValue TomlValue_new_string(str s); +void TomlValue_destroy(TomlValue* self); + #ifdef __cplusplus } #endif diff --git a/project.config b/project.config index 2da86e8..da42b01 100644 --- a/project.config +++ b/project.config @@ -37,7 +37,7 @@ OBJDIR="obj" OUTDIR="bin" STATIC_LIB_FILE="$PROJECT.a" -INCLUDE="-I./include -I../tlibc/include" +INCLUDE="-Isrc -Iinclude -I../tlibc/include" # OS-specific options case "$OS" in diff --git a/src/TomlArray.c b/src/TomlArray.c new file mode 100644 index 0000000..4cb3ab4 --- /dev/null +++ b/src/TomlArray.c @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlArray* TomlArray_new(void) +{ + TomlArray* array = malloc(sizeof(TomlArray)); + array->elements = NULL; + array->len = 0; + array->_capacity = 0; + return array; +} + +void TomlArray_free(TomlArray* self) +{ + if (self == NULL) + return; + + for (u64 i = 0; i < self->len; i++) { + TomlValue_destroy(&self->elements[i]); + } + free(self->elements); + free(self); +} + +void TomlArray_expand_if_necessary(TomlArray* self) +{ + if (self->len + 1 > self->_capacity) { + u64 new_capacity = self->_capacity > 0 ? self->_capacity * 2 : 8; + void* p = realloc(self->elements, sizeof(TomlValue) * new_capacity); + self->elements = p; + self->_capacity = new_capacity; + } +} + +void TomlArray_append(TomlArray* self, TomlValue value) +{ + TomlArray_expand_if_necessary(self); + self->elements[self->len++] = value; +} diff --git a/src/TomlTable.c b/src/TomlTable.c new file mode 100644 index 0000000..9351ab5 --- /dev/null +++ b/src/TomlTable.c @@ -0,0 +1,96 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlTable* TomlTable_new(void) +{ + TomlTable* table = malloc(sizeof(TomlTable)); + HashMap_construct(table, TomlValue, (FreeFunction)TomlValue_destroy); + return table; +} + +void TomlTable_free(TomlTable* self) +{ + if(!self) + return; + HashMap_destroy(self); + free(self); +} + +void TomlTable_set(TomlTable* self, str key, TomlValue value) +{ + assert(key.data != NULL); + HashMap_pushOrUpdate(self, key, &value); +} + +TomlValue* TomlTable_get(const TomlTable* self, const str key) +{ + assert(key.data != NULL); + TomlValue* value = HashMap_tryGetPtr(self, key); + return value; +} + +Result(TomlTable*) TomlTable_get_table(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_TABLE); + Return RESULT_VALUE(p, v->value.table); +} + +Result(TomlArray*) TomlTable_get_array(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_ARRAY); + Return RESULT_VALUE(p, v->value.array); +} + +Result(str*) TomlTable_get_str(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_STRING); + Return RESULT_VALUE(p, v->value.s); +} + +Result(i64) TomlTable_get_integer(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_INTEGER); + Return RESULT_VALUE(i, v->value.i); +} + +Result(f64) TomlTable_get_float(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_FLOAT); + Return RESULT_VALUE(f, v->value.f); +} + +Result(TomlDateTime*) TomlTable_get_datetime(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_DATETIME); + Return RESULT_VALUE(p, v->value.dt); +} + +Result(bool) TomlTable_get_bool(const TomlTable* self, str key) +{ + Deferral(1); + TomlValue* v = TomlTable_get(self, key); + try_assert(v != NULL); + try_assert(v->type == TLIBTOML_BOOLEAN); + Return RESULT_VALUE(i, v->value.b); +} diff --git a/src/TomlValue.c b/src/TomlValue.c new file mode 100644 index 0000000..583c04a --- /dev/null +++ b/src/TomlValue.c @@ -0,0 +1,115 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue TomlValue_new(TomlType type) +{ + TomlValue value = {0}; + value.type = type; + switch (type) { + default: + assert(false && "invalid type"); + break; + case TLIBTOML_TABLE: + value.value.table = NULL; + break; + case TLIBTOML_ARRAY: + value.value.array = NULL; + break; + case TLIBTOML_STRING: + value.value.s = NULL; + break; + case TLIBTOML_INTEGER: + value.value.i = 0; + break; + case TLIBTOML_FLOAT: + value.value.f = 0.0; + break; + case TLIBTOML_BOOLEAN: + value.value.b = false; + break; + case TLIBTOML_DATETIME: + value.value.dt = (TomlDateTime*)malloc(sizeof(TomlDateTime)); + memset(value.value.dt, 0, sizeof(TomlDateTime)); + break; + } + return value; +} + +TomlValue TomlValue_from_str(str s) +{ + TomlValue value = {0}; + value.value.s = str_copy(s); + value.type = TLIBTOML_STRING; + return value; +} + +TomlValue TomlValue_new_table(void) +{ + TomlValue value = {0}; + value.value.table = TomlTable_new(); + value.type = TLIBTOML_TABLE; + return value; +} + +TomlValue TomlValue_new_array(void) +{ + TomlValue value = {0}; + value.value.array = TomlArray_new(); + value.type = TLIBTOML_ARRAY; + return value; +} + +TomlValue TomlValue_new_integer(i64 integer) +{ + TomlValue value = {0}; + value.value.i = integer; + value.type = TLIBTOML_INTEGER; + return value; +} + +TomlValue TomlValue_new_float(f64 float_) +{ + TomlValue value = {0}; + value.value.f = float_; + value.type = TLIBTOML_FLOAT; + return value; +} + +TomlValue TomlValue_new_datetime(void) +{ + return TomlValue_new(TLIBTOML_BOOLEAN); +} + +TomlValue TomlValue_new_bool(bool b) +{ + TomlValue value = {0}; + value.value.b = b; + value.type = TLIBTOML_BOOLEAN; + return value; +} + +void TomlValue_destroy(TomlValue* self) +{ + if (self == NULL) + return; + + switch (self->type) { + case TLIBTOML_STRING: + str_destroy(self->value.s); + break; + case TLIBTOML_TABLE: + TomlTable_free(self->value.table); + break; + case TLIBTOML_ARRAY: + TomlArray_free(self->value.array); + break; + case TLIBTOML_DATETIME: + free(self->value.dt); + break; + default: + break; + } +} diff --git a/src/toml.c b/src/toml.c deleted file mode 100644 index 95ae38d..0000000 --- a/src/toml.c +++ /dev/null @@ -1,1421 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include -#include "tlibtoml/toml.h" -#include "tlibc/errors.h" -#include "tlibc/string/StringBuilder.h" -#include "tlibc/collections/HashMap.h" -#include "tlibc/collections/List.h" - -static ATTRIBUTE_THREAD_LOCAL TomlErr g_err = { TOML_OK, NULL }; - -const TomlErr* toml_err(void) -{ - return &g_err; -} - -void toml_err_clear(void) -{ - free(g_err.message); - g_err.code = TOML_OK; -} - -static inline void toml_err_set(TomlErrCode code, cstr format, ...) - ATTRIBUTE_CHECK_FORMAT_PRINTF(2, 3); - -static inline void toml_err_set(TomlErrCode code, cstr format, ...) -{ - if(g_err.code != TOML_OK) - toml_err_clear(); - va_list args; - va_start(args, format); - g_err.code = code; - g_err.message = vsprintf_malloc(format, args); - va_end(args); - - printfe("ERROR: %s\n", toml_err()->message); - fflush(stderr); -} - - - -TomlTable* toml_table_new(void) -{ - TomlTable* table = malloc(sizeof(TomlTable)); - HashMap_construct(table, TomlValue, (FreeFunction)TomlValue_destroy); - return table; -} - -void toml_table_free(TomlTable* self) -{ - if(!self) - return; - HashMap_destroy(self); - free(self); -} - -void toml_table_set(TomlTable* self, str key, TomlValue value) -{ - assert(key.data != NULL); - HashMap_pushOrUpdate(self, key, &value); -} - -TomlValue* toml_table_get(const TomlTable* self, const str key) -{ - assert(key.data != NULL); - TomlValue* value = HashMap_tryGetPtr(self, key); - return value; -} - -TomlTable* toml_table_get_table(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_TABLE); - return v->value.table; -} - -TomlArray* toml_table_get_array(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_ARRAY); - return v->value.array; -} - -str toml_table_get_str(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_STRING); - return v->value.s; -} - -i64 toml_table_get_integer(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_INTEGER); - return v->value.i; -} - -f64 toml_table_get_float(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_FLOAT); - return v->value.f; -} - -TomlDateTime* toml_table_get_datetime(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_DATETIME); - return v->value.dt; -} - -bool toml_table_get_bool(const TomlTable* self, str key) -{ - TomlValue* v = toml_table_get(self, key); - assert(v != NULL); - assert(v->type == TOML_BOOLEAN); - return v->value.b; -} - -TomlArray *toml_array_new(void) -{ - TomlArray* array = malloc(sizeof(TomlArray)); - array->elements = NULL; - array->len = 0; - array->_capacity = 0; - return array; -} - -void toml_array_free(TomlArray* self) -{ - if (self == NULL) - return; - - for (u64 i = 0; i < self->len; i++) { - TomlValue_destroy(&self->elements[i]); - } - free(self->elements); - free(self); -} - -void toml_array_expand_if_necessary(TomlArray* self) -{ - if (self->len + 1 > self->_capacity) { - u64 new_capacity = self->_capacity > 0 ? self->_capacity * 2 : 8; - void* p = realloc(self->elements, sizeof(TomlValue) * new_capacity); - self->elements = p; - self->_capacity = new_capacity; - } -} - -void toml_array_append(TomlArray* self, TomlValue value) -{ - toml_array_expand_if_necessary(self); - self->elements[self->len++] = value; -} - -TomlValue TomlValue_new(TomlType type) -{ - TomlValue value = {0}; - value.type = type; - switch (type) { - default: - assert(false && "invalid type"); - break; - case TOML_TABLE: - value.value.table = NULL; - break; - case TOML_ARRAY: - value.value.array = NULL; - break; - case TOML_STRING: - value.value.s = str_null; - break; - case TOML_INTEGER: - value.value.i = 0; - break; - case TOML_FLOAT: - value.value.f = 0.0; - break; - case TOML_BOOLEAN: - value.value.b = false; - break; - case TOML_DATETIME: - value.value.dt = (TomlDateTime*)malloc(sizeof(TomlDateTime)); - memset(value.value.dt, 0, sizeof(TomlDateTime)); - break; - } - return value; -} - -TomlValue TomlValue_from_str(str s) -{ - TomlValue value = {0}; - value.value.s = str_copy(s); - value.type = TOML_STRING; - return value; -} - -TomlValue TomlValue_new_table(void) -{ - TomlValue value = {0}; - value.value.table = toml_table_new(); - value.type = TOML_TABLE; - return value; -} - -TomlValue TomlValue_new_array(void) -{ - TomlValue value = {0}; - value.value.array = toml_array_new(); - value.type = TOML_ARRAY; - return value; -} - -TomlValue TomlValue_new_integer(i64 integer) -{ - TomlValue value = {0}; - value.value.i = integer; - value.type = TOML_INTEGER; - return value; -} - -TomlValue TomlValue_new_float(f64 float_) -{ - TomlValue value = {0}; - value.value.f = float_; - value.type = TOML_FLOAT; - return value; -} - -TomlValue TomlValue_new_datetime(void) -{ - return TomlValue_new(TOML_BOOLEAN); -} - -TomlValue TomlValue_new_bool(bool b) -{ - TomlValue value = {0}; - value.value.b = b; - value.type = TOML_BOOLEAN; - return value; -} - -void TomlValue_destroy(TomlValue* self) -{ - if (self == NULL) - return; - - switch (self->type) { - case TOML_STRING: - str_destroy(self->value.s); - break; - case TOML_TABLE: - toml_table_free(self->value.table); - break; - case TOML_ARRAY: - toml_array_free(self->value.array); - break; - case TOML_DATETIME: - free(self->value.dt); - break; - default: - break; - } -} - -typedef struct _TomlParser { - cstr begin; - cstr end; - cstr ptr; - i32 lineno; - i32 colno; - cstr filename; -} TomlParser; - -TomlParser* toml_parser_new(str s, cstr filename) -{ - TomlParser* parser = malloc(sizeof(TomlParser)); - parser->begin = s.data; - parser->end = s.data + s.size; - parser->ptr = s.data; - parser->lineno = 1; - parser->colno = 1; - parser->filename = filename; - return parser; -} - -void toml_parser_free(TomlParser* self) -{ - if (self == NULL) - return; - - free(self); -} - -void toml_move_next(TomlParser* self) -{ - if (self->ptr < self->end) { - if (*self->ptr == '\n') { - self->lineno++; - self->colno = 1; - } else { - self->colno++; - } - self->ptr++; - } -} - -void toml_next_n(TomlParser* self, i32 n) -{ - for (i32 i = 0; i < n; i++) { - toml_move_next(self); - } -} - -str toml_parse_bare_key(TomlParser* self) -{ - cstr s = self->ptr; - u64 len = 0; - - while (self->ptr < self->end) { - char ch = *self->ptr; - - if (!(isalnum(ch) || ch == '_' || ch == '-')) - break; - - len++; - toml_move_next(self); - } - - return str_copy(str_construct((char*)s, len, false)); -} - -char toml_hex_char_to_int(char ch) -{ - assert(isxdigit(ch)); - if (isdigit(ch)) { - return ch - '0'; - } else if (islower(ch)) { - return ch - 'a' + 10; - } else if (isupper(ch)) { - return ch - 'A' + 10; - } - return 0; -} - -i32 toml_encode_unicode_scalar(StringBuilder* sb_ptr, TomlParser* parser, i32 n) -{ - u32 scalar = 0; - - if (parser->ptr + n > parser->end) { - toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", - parser->filename, parser->lineno, parser->colno); - return TOML_ERR_UNICODE; - } - - for (i32 i = 0; i < n; i++) { - char ch = *parser->ptr; - if (isxdigit(ch)) { - scalar = scalar * 16 + (u32)toml_hex_char_to_int(ch); - toml_move_next(parser); - } else { - toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", - parser->filename, parser->lineno, parser->colno); - return TOML_ERR_UNICODE; - } - } - - if ((scalar >= 0xd800 && scalar <= 0xdfff) || - (scalar >= 0xfffe && scalar <= 0xffff)) { - toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", - parser->filename, parser->lineno, parser->colno); - return TOML_ERR_UNICODE; - } - - if (scalar <= 0x7f) { - StringBuilder_append_char(sb_ptr, (char)scalar); - return 0; - } - - if (scalar <= 0x7ff) { - StringBuilder_append_char(sb_ptr, (char)(0xc0 | (scalar >> 6))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); - return 0; - } - - if (scalar <= 0xffff) { - StringBuilder_append_char(sb_ptr, (char)(0xe0 | (scalar >> 12))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); - return 0; - } - - if (scalar <= 0x1fffff) { - StringBuilder_append_char(sb_ptr, (char)(0xf0 | (scalar >> 18))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 12) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); - return 0; - } - - if (scalar <= 0x3ffffff) { - StringBuilder_append_char(sb_ptr, (char)(0xf8 | (scalar >> 24))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 18) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 12) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); - return 0; - } - - if (scalar <= 0x7fffffff) { - StringBuilder_append_char(sb_ptr, (char)(0xfc | (scalar >> 30))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 24) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 18) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 12) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); - StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); - return 0; - } - - toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", - parser->filename, parser->lineno, parser->colno); - - return TOML_ERR_UNICODE; -} - -str toml_parse_basic_string(TomlParser* self) -{ - StringBuilder sb = StringBuilder_alloc(0); - - while (self->ptr < self->end &&* self->ptr != '\"' &&* self->ptr != '\n') { - char ch1 = *self->ptr; - if (ch1 == '\\') { - if (self->ptr >= self->end) break; - - toml_move_next(self); - char ch2 = *self->ptr; - - if (ch2 == '\"') { - StringBuilder_append_char(&sb, '\"'); - toml_move_next(self); - } else if (ch2 == 'b') { - StringBuilder_append_char(&sb, '\b'); - toml_move_next(self); - } else if (ch2 == 't') { - StringBuilder_append_char(&sb, '\t'); - toml_move_next(self); - } else if (ch2 == 'n') { - StringBuilder_append_char(&sb, '\n'); - toml_move_next(self); - } else if (ch2 == 'f') { - StringBuilder_append_char(&sb, '\f'); - toml_move_next(self); - } else if (ch2 == 'r') { - StringBuilder_append_char(&sb, '\r'); - toml_move_next(self); - } else if (ch2 == '\\') { - StringBuilder_append_char(&sb, '\\'); - toml_move_next(self); - } else if (ch2 == 'u') { - toml_move_next(self); - if (toml_encode_unicode_scalar(&sb, self, 4) != 0) { - StringBuilder_destroy(&sb); - return str_null; - } - } else if (ch2 == 'U') { - toml_move_next(self); - if (toml_encode_unicode_scalar(&sb, self, 8) != 0) { - StringBuilder_destroy(&sb); - return str_null; - } - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid escape character", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - return str_null; - } - } else { - StringBuilder_append_char(&sb, ch1); - toml_move_next(self); - } - } - - if (self->ptr >= self->end ||* self->ptr != '\"' ||* self->ptr == '\n') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated basic string", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - return str_null; - } - - toml_move_next(self); - - return StringBuilder_getStr(&sb); -} - -str toml_parse_literal_string(TomlParser* self) -{ - StringBuilder sb = StringBuilder_alloc(0); - - while (self->ptr < self->end &&* self->ptr != '\'' &&* self->ptr != '\n') { - StringBuilder_append_char(&sb,* self->ptr); - toml_move_next(self); - } - - if (self->ptr >= self->end ||* self->ptr != '\'' ||* self->ptr == '\n') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated literal string", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - return str_null; - } - - toml_move_next(self); - - return StringBuilder_getStr(&sb); -} - -TomlValue toml_parse_basic_string_value(TomlParser* self) -{ - str s = toml_parse_basic_string(self); - //TODO: error handling - assert(s.data != NULL); - - TomlValue value = TomlValue_new(TOML_STRING); - value.value.s = s; - return value; -} - -TomlValue toml_parse_literal_string_value(TomlParser* self) -{ - str s = toml_parse_literal_string(self); - //TODO: error handling - assert(s.data != NULL); - - TomlValue value = TomlValue_new(TOML_STRING); - value.value.s = s; - return value; -} - -TomlValue toml_parse_multi_line_basic_string(TomlParser* self) -{ - StringBuilder sb = StringBuilder_alloc(0); - - if (*self->ptr == '\n') { - toml_move_next(self); - } - - while (self->ptr + 3 <= self->end && strncmp(self->ptr, "\"\"\"", 3) != 0) { - char ch1 = *self->ptr; - - if (ch1 == '\\') { - if (self->ptr + 3 > self->end || strncmp(self->ptr, "\"\"\"", 3) == 0) - break; - - toml_move_next(self); - char ch2 = *self->ptr; - - if (ch2 == '\"') { - StringBuilder_append_char(&sb, '\"'); - toml_move_next(self); - } else if (ch2 == 'b') { - StringBuilder_append_char(&sb, '\b'); - toml_move_next(self); - } else if (ch2 == 't') { - StringBuilder_append_char(&sb, '\t'); - toml_move_next(self); - } else if (ch2 == 'n') { - StringBuilder_append_char(&sb, '\n'); - toml_move_next(self); - } else if (ch2 == 'f') { - StringBuilder_append_char(&sb, '\f'); - toml_move_next(self); - } else if (ch2 == 'r') { - StringBuilder_append_char(&sb, '\r'); - toml_move_next(self); - } else if (ch2 == '\\') { - StringBuilder_append_char(&sb, '\\'); - toml_move_next(self); - } else if (ch2 == 'u') { - toml_move_next(self); - if (toml_encode_unicode_scalar(&sb, self, 4) != 0) { - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - } else if (ch2 == 'U') { - toml_move_next(self); - if (toml_encode_unicode_scalar(&sb, self, 8) != 0) { - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - } else if (ch2 == '\n') { - do { - toml_move_next(self); - } while (self->ptr + 3 <= self->end && isspace(*self->ptr)); - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid escape character", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - } else { - StringBuilder_append_char(&sb, ch1); - toml_move_next(self); - } - } - - if (self->ptr + 3 > self->end || strncmp(self->ptr, "\"\"\"", 3) != 0) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated multi-line basic string", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - - toml_next_n(self, 3); - - TomlValue value = TomlValue_new(TOML_STRING); - value.value.s = StringBuilder_getStr(&sb); - return value; -} - -TomlValue toml_parse_multi_line_literal_string(TomlParser* self) -{ - StringBuilder sb = StringBuilder_alloc(0); - - if (*self->ptr == '\n') { - toml_move_next(self); - } - - while (self->ptr + 3 <= self->end && strncmp(self->ptr, "\'\'\'", 3) != 0) { - StringBuilder_append_char(&sb,* self->ptr); - toml_move_next(self); - } - - if (self->ptr + 3 > self->end || strncmp(self->ptr, "\'\'\'", 3) != 0) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated multi-line literal string", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - - toml_next_n(self, 3); - - TomlValue value = TomlValue_new(TOML_STRING); - value.value.s = StringBuilder_getStr(&sb); - return value; -} - -TomlValue toml_parse_datetime(str s) -{ - (void)s; - //TODO: parse datetime - return TomlValue_new(TOML_DATETIME); -} - -TomlValue toml_parse_int_or_float_or_time(TomlParser* self) -{ - TomlValue value = {0}; - - char type = 'i'; - i32 base = 10; - - StringBuilder sb = StringBuilder_alloc(0); - - // Determine nan and inf type as float, as we cannot determine by dot. - // But do not strip it because we will append it to the string later - if (self->ptr + 3 <= self->end && - (strncmp(self->ptr, "nan", 3) == 0 || strncmp(self->ptr, "inf", 3) == 0)) { - type = 'f'; - } - - if (self->ptr + 4 <= self->end && - (strncmp(self->ptr, "+nan", 4) == 0 || - strncmp(self->ptr, "-nan", 4) == 0 || - strncmp(self->ptr, "+inf", 4) == 0 || - strncmp(self->ptr, "-inf", 4) == 0)) { - type = 'f'; - } - - // If there is a base prefix, set the base and strip the prefix, - // because strtoll() do not recognize 0o and 0b - if (self->ptr + 2 <= self->end) { - if (strncmp(self->ptr, "0x", 2) == 0) { - base = 16; - toml_next_n(self, 2); - } else if (strncmp(self->ptr, "0o", 2) == 0) { - base = 8; - toml_next_n(self, 2); - } else if (strncmp(self->ptr, "0b", 2) == 0) { - base = 2; - toml_next_n(self, 2); - } - } - - char last_char = 0; - i32 has_exp = false; - while (self->ptr < self->end) { - if (*self->ptr == '+' ||* self->ptr == '-') { - if (last_char == 0 || ((last_char == 'e' || last_char == 'E') && !has_exp)) { - if (last_char != 0) { - type = 'f'; - has_exp = true; - } - StringBuilder_append_char(&sb,* self->ptr); - } else { - break; - } - } else if (isalnum(*self->ptr)) { - if ((*self->ptr == 'e' ||* self->ptr == 'E') && base == 10) { - type = 'f'; - } - - StringBuilder_append_char(&sb,* self->ptr); - } else if (*self->ptr == '.') { - if (type == 'i') { - type = 'f'; - StringBuilder_append_char(&sb,* self->ptr); - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid float", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - } else if (*self->ptr == '_') { - if (type == 't') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid datetime", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - - if (!isalnum(last_char)) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid integer or float", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - } else if (*self->ptr == '-') { - type = 't'; - StringBuilder_append_char(&sb,* self->ptr); - } else { - break; - } - - last_char = *self->ptr; - toml_move_next(self); - } - - if (last_char == '_') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid integer or float or datetime", - self->filename, self->lineno, self->colno); - StringBuilder_destroy(&sb); - //TODO: error handling - assert(false && "TODO error handling"); - } - - if (type == 'i') { - char* end = NULL; - char* start = StringBuilder_getStr(&sb).data; - i64 n = strtoll(start, &end, base); - if (end < start + sb.buffer.size) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid integer", - self->filename, self->lineno, self->colno); - //TODO: error handling - printfe("%i < %u '%s'\n", (i32)(end-start), sb.buffer.size, start); - fflush(stderr); - StringBuilder_destroy(&sb); - assert(false && "TODO error handling"); - } - value = TomlValue_new_integer(n); - } else if (type == 'f') { - char* end = NULL; - char* start = StringBuilder_getStr(&sb).data; - f64 n = strtod(start, &end); - if (end < start + sb.buffer.size) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid float", - self->filename, self->lineno, self->colno); - goto cleanup; - } - value = TomlValue_new_float(n); - } else if (type == 't') { - value = toml_parse_datetime(StringBuilder_getStr(&sb)); - } - -cleanup: - StringBuilder_destroy(&sb); - return value; -} - -TomlValue toml_parse_bool(TomlParser* self) -{ - if (self->ptr + 4 <= self->end && strncmp(self->ptr, "true", 4) == 0 && - (self->ptr + 4 == self->end || isspace(*(self->ptr + 4)) || *(self->ptr + 4) == ',' || *(self->ptr + 4) == ']' || *(self->ptr + 4) == '}')) { - toml_next_n(self, 4); - return TomlValue_new_bool(true); - } - - if (self->ptr + 5 <= self->end && strncmp(self->ptr, "false", 5) == 0 && - (self->ptr + 5 == self->end || isspace(*(self->ptr + 5)) || *(self->ptr + 5) == ',' || *(self->ptr + 5) == ']' || *(self->ptr + 5) == '}')) { - toml_next_n(self, 5); - return TomlValue_new_bool(false); - } - - //TODO: error handling - assert(false && "TODO error handling"); - return TomlValue_new(TOML_INVALID_TYPE); -} - -TomlValue toml_parse_array(TomlParser* self); -TomlValue toml_parse_inline_table(TomlParser* self); - -TomlValue toml_parse_value(TomlParser* self) -{ - char ch = *self->ptr; - - TomlValue value; - if (strncmp(self->ptr, "\"\"\"", 3) == 0) { - toml_next_n(self, 3); - value = toml_parse_multi_line_basic_string(self); - } else if (strncmp(self->ptr, "\'\'\'", 3) == 0) { - toml_next_n(self, 3); - value = toml_parse_multi_line_literal_string(self); - } else if (ch == '\"') { - toml_move_next(self); - value = toml_parse_basic_string_value(self); - } else if (ch == '\'') { - toml_move_next(self); - value = toml_parse_literal_string_value(self); - } else if (isdigit(ch) || ch == '+' || ch == '-' || ch == '.' || ch == 'n' || ch == 'i') { - value = toml_parse_int_or_float_or_time(self); - } else if (ch == 't' || ch == 'f') { - value = toml_parse_bool(self); - } else if (ch == '[') { - toml_move_next(self); - value = toml_parse_array(self); - } else if (ch == '{') { - toml_move_next(self); - value = toml_parse_inline_table(self); - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", - self->filename, self->lineno, self->colno); - //TODO: error handling - assert(false && "TODO error handling"); - } - - return value; -} - -TomlErrCode toml_parse_key_value(TomlParser* self, TomlTable* table) -{ - while (self->ptr < self->end) { - char ch; - - ch = *self->ptr; - while (self->ptr < self->end && isspace(ch)) { - toml_move_next(self); - ch = *self->ptr; - } - - if (self->ptr == self->end) break; - - str key; - if (isalnum(ch) || ch == '_') { - key = toml_parse_bare_key(self); - if (key.data == NULL) - return toml_err()->code; - } else if (ch == '\"') { - toml_move_next(self); - key = toml_parse_basic_string(self); - if (key.data == NULL) - return toml_err()->code; - } else if (ch == '\'') { - toml_move_next(self); - key = toml_parse_literal_string(self); - if (key.data == NULL) - return toml_err()->code; - } else if (ch == '[') { - break; - } else if (ch == '#') { - do { - toml_move_next(self); - ch = *self->ptr; - } while (self->ptr < self->end && ch != '\n'); - toml_move_next(self); - continue; - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", - self->filename, self->lineno, self->colno); - return TOML_ERR_SYNTAX; - } - - ch = *self->ptr; - while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { - toml_move_next(self); - ch = *self->ptr; - } - - if (self->ptr == self->end) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", - self->filename, self->lineno, self->colno); - return TOML_ERR_SYNTAX; - } - - if (ch != '=') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", - self->filename, self->lineno, self->colno); - return TOML_ERR_SYNTAX; - } - - toml_move_next(self); - - ch = *self->ptr; - while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { - toml_move_next(self); - ch = *self->ptr; - } - - if (self->ptr == self->end) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", - self->filename, self->lineno, self->colno); - return TOML_ERR_SYNTAX; - } - - TomlValue value = toml_parse_value(self); - if (value.type == TOML_INVALID_TYPE) - return toml_err()->code; - - toml_table_set(table, key, value); - str_destroy(key); - - while (self->ptr < self->end && (*self->ptr == ' ' ||* self->ptr == '\t')) { - toml_move_next(self); - } - - if (self->ptr == self->end) - break; - - if (*self->ptr == '#') { - do { - toml_move_next(self); - } while (self->ptr < self->end &&* self->ptr != '\n'); - } - - if (*self->ptr == '\r') { - toml_move_next(self); - } - - if (*self->ptr == '\n') { - toml_move_next(self); - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: new line expected", - self->filename, self->lineno, self->colno); - return TOML_ERR_SYNTAX; - } - } - - return TOML_OK; -} - -TomlValue toml_parse_array(TomlParser* self) -{ - TomlValue array_value = TomlValue_new_array(); - - while (self->ptr < self->end) { - if (isspace(*self->ptr)) { - while (self->ptr < self->end && isspace(*self->ptr)) { - toml_move_next(self); - } - } else if (*self->ptr == '#') { - do { - toml_move_next(self); - } while (self->ptr < self->end &&* self->ptr != '\n'); - toml_move_next(self); - } else if (*self->ptr == '\n') { - toml_move_next(self); - } else if (*self->ptr == ']') { - toml_move_next(self); - break; - } else { - TomlValue value = toml_parse_value(self); - if (value.type == TOML_INVALID_TYPE) { - TomlValue_destroy(&array_value); - //TODO: error handling - assert(false && "TODO error handling"); - } - - toml_array_append(array_value.value.array, value); - - while (self->ptr < self->end) { - if (isspace(*self->ptr)) { - do { - toml_move_next(self); - } while (self->ptr < self->end && isspace(*self->ptr)); - } else if (*self->ptr == '#') { - do { - toml_move_next(self); - } while (self->ptr < self->end &&* self->ptr != '\n'); - } else { - break; - } - } - - if (self->ptr < self->end &&* self->ptr == ',') { - toml_move_next(self); - } - } - } - - return array_value; -} - -TomlValue toml_parse_inline_table(TomlParser* self) -{ - TomlValue table_value = TomlValue_new_table(); - - while (self->ptr < self->end) { - char ch = *self->ptr; - while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { - toml_move_next(self); - ch = *self->ptr; - } - - str key; - if (isalnum(ch) || ch == '_') { - key = toml_parse_bare_key(self); - if (key.data == NULL) - goto error; - } else if (ch == '\"') { - toml_move_next(self); - key = toml_parse_basic_string(self); - if (key.data == NULL) - goto error; - } else if (ch == '\'') { - toml_move_next(self); - key = toml_parse_literal_string(self); - if (key.data == NULL) - goto error; - } else if (ch == '}') { - break; - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", - self->filename, self->lineno, self->colno); - goto error; - } - - ch = *self->ptr; - while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { - toml_move_next(self); - ch = *self->ptr; - } - - if (self->ptr == self->end) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", - self->filename, self->lineno, self->colno); - goto error; - } - - if (ch != '=') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", - self->filename, self->lineno, self->colno); - goto error; - } - - toml_move_next(self); - - ch = *self->ptr; - while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { - toml_move_next(self); - ch = *self->ptr; - } - - if (self->ptr == self->end) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", - self->filename, self->lineno, self->colno); - goto error; - } - - TomlValue value = toml_parse_value(self); - if (value.type == TOML_INVALID_TYPE) - goto error; - - toml_table_set(table_value.value.table, key, value); - str_destroy(key); - - while (self->ptr < self->end && (*self->ptr == ' ' ||* self->ptr == '\t')) { - toml_move_next(self); - } - - if (*self->ptr == ',') { - toml_move_next(self); - } else if (*self->ptr == '}') { - toml_move_next(self); - break; - } - } - - goto end; - -error: - TomlValue_destroy(&table_value); - //TODO: error handling - assert(false && "TODO error handling"); -end: - return table_value; -} - -TomlTable* toml_walk_table_path(TomlParser* parser, TomlTable* table, - TomlArray *key_path, i32 is_array, - i32 create_if_not_exist) -{ - TomlTable* real_table = table; - - if (is_array) { - u64 i = 0; - for (; i < key_path->len - 1; i++) { - str part = key_path->elements[i].value.s; - TomlValue* t = toml_table_get(real_table, part); - if (t == NULL) { - if (create_if_not_exist) { - TomlValue new_table_value = TomlValue_new_table(); - toml_table_set(real_table, part, new_table_value); - real_table = new_table_value.value.table; - } else { - real_table = NULL; - break; - } - } else { - real_table = t->value.table; - } - } - - str part = key_path->elements[i].value.s; - TomlValue* t = toml_table_get(real_table, part); - if (t == NULL) { - if (create_if_not_exist) { - TomlValue array_value = TomlValue_new_array(); - TomlValue new_table_value = TomlValue_new_table(); - toml_array_append(array_value.value.array, new_table_value); - toml_table_set(real_table, part, array_value); - real_table = new_table_value.value.table; - } else { - real_table = NULL; - } - } else { - if (t->type != TOML_ARRAY) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: this key was not an array", - parser->filename, parser->lineno, parser->colno); - goto error; - } - - TomlValue new_table_value = TomlValue_new_table(); - toml_array_append(t->value.array, new_table_value); - real_table = new_table_value.value.table; - } - } else { - for (u64 i = 0; i < key_path->len; i++) { - str part = key_path->elements[i].value.s; - TomlValue* t = toml_table_get(real_table, part); - if (t == NULL) { - if (create_if_not_exist) { - TomlValue new_table_value = TomlValue_new_table(); - toml_table_set(real_table, part, new_table_value); - real_table = new_table_value.value.table; - } else { - real_table = NULL; - break; - } - } else { - if (t->type == TOML_ARRAY) { - real_table = t->value.array->elements[t->value.array->len - 1].value.table; - } else if (t->type == TOML_TABLE) { - real_table = t->value.table; - } - } - } - } - - goto end; - -error: - real_table = NULL; - //TODO: error handling - assert(false && "TODO error handling"); - -end: - return real_table; -} - -TomlErrCode toml_parse_table(TomlParser* self, TomlTable* table) -{ - TomlArray *key_path = NULL; - i32 is_array = false; - TomlTable* real_table = table; - TomlErrCode rc = 0; - - key_path = toml_array_new(); - - if (self->ptr < self->end &&* self->ptr == '[') { - is_array = true; - toml_move_next(self); - } - - while (1) { - if (*self->ptr == ' ' ||* self->ptr == '\t') { - do { - toml_move_next(self); - } while (*self->ptr <* self->end && (*self->ptr == ' ' ||* self->ptr == '\t')); - } else if (*self->ptr == ']') { - if (is_array) { - if (self->ptr + 2 <= self->end && strncmp(self->ptr, "]]", 2) == 0) { - toml_next_n(self, 2); - break; - } - } else { - toml_move_next(self); - break; - } - } else { - str key_part; - if (isalnum(*self->ptr) ||* self->ptr == '_') { - key_part = toml_parse_bare_key(self); - if (key_part.data == NULL) - goto cleanup; - } else if (*self->ptr == '\"') { - toml_move_next(self); - key_part = toml_parse_basic_string(self); - if (key_part.data == NULL) - goto cleanup; - } else if (*self->ptr == '\'') { - toml_move_next(self); - key_part = toml_parse_literal_string(self); - if (key_part.data == NULL) - goto cleanup; - } else { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", - self->filename, self->lineno, self->colno); - goto cleanup; - } - - TomlValue key_part_value = TomlValue_new(TOML_STRING); - key_part_value.value.s = key_part; - - toml_array_append(key_path, key_part_value); - - while (self->ptr < self->end && - (*self->ptr == ' ' ||* self->ptr == '\t')) { - toml_move_next(self); - } - - if (self->ptr < self->end &&* self->ptr == '.') { - toml_move_next(self); - } - } - } - - if (key_path->len == 0) { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: empty table name", - self->filename, self->lineno, self->colno); - goto cleanup; - } - - while (self->ptr < self->end && - (*self->ptr == ' ' ||* self->ptr == '\t' ||* self->ptr == '\r')) { - toml_move_next(self); - } - - if (self->ptr < self->end &&* self->ptr != '\n') { - toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: new line expected", - self->filename, self->lineno, self->colno); - goto cleanup; - } - - real_table = toml_walk_table_path(self, table, key_path, is_array, true); - if (real_table == NULL) - goto error; - - if(toml_parse_key_value(self, real_table) != TOML_OK) - goto error; - - goto cleanup; - -error: - rc = toml_err()->code; -cleanup: - toml_array_free(key_path); - return rc; -} - -TomlTable* toml_parse(TomlParser* self) -{ - TomlTable* table = toml_table_new(); - - while (self->ptr < self->end) { - char ch = *self->ptr; - - while (self->ptr < self->end && isspace(ch)) { - toml_move_next(self); - ch = *self->ptr; - } - - if (ch == '#') { - do { - toml_move_next(self); - ch = *self->ptr; - } while (self->ptr < self->end && ch != '\n'); - toml_move_next(self); - } else if (ch == '[') { - toml_move_next(self); - if (toml_parse_table(self, table) != 0) - return NULL; - } else if (isalnum(ch) || ch == '_' || ch == '-') { - if (toml_parse_key_value(self, table) != 0) - return NULL; - } else if (ch == ' ' || ch == '\t' || ch == '\r') { - do { - toml_move_next(self); - ch = *self->ptr; - } while (ch == ' ' || ch == '\t' || ch == '\r'); - } else if (ch == '\n') { - toml_move_next(self); - } - } - - return table; -} - -TomlTable* toml_load_str_filename(str s, cstr filename) -{ - TomlParser* parser = toml_parser_new(s, filename); - - TomlTable* table = toml_parse(parser); - - toml_parser_free(parser); - return table; -} - -TomlTable* toml_load_str(str s) -{ - return toml_load_str_filename(s, ""); -} - -TomlTable* toml_load_file_filename(FILE* file, cstr filename) -{ - TomlTable* table = NULL; - const u32 file_chunk_size = 8*1024; - StringBuilder sb = StringBuilder_alloc(file_chunk_size); - - u64 count = 0; - u64 remaining_capacity = 0; - do { - remaining_capacity = sb.buffer.allocated_size - sb.buffer.size; - count = fread((char*)sb.buffer.data + sb.buffer.size, 1, remaining_capacity, file); - if (ferror(file)) { - toml_err_set(TOML_ERR_OS, "Error when reading %s [errno %d: %s]", - filename, errno, strerror(errno)); - goto error; - } - - sb.buffer.size += count; - - if (sb.buffer.size + 1 >= sb.buffer.allocated_size) { - StringBuilder_increaseCapacity(&sb, file_chunk_size); - } - } while (count == remaining_capacity); - - table = toml_load_str_filename(StringBuilder_getStr(&sb), filename); - - goto cleanup; - -error: - toml_table_free(table); - table = NULL; - -cleanup: - StringBuilder_destroy(&sb); - return table; -} - -TomlTable* toml_load_file(FILE* file) -{ - return toml_load_file_filename(file, ""); -} - -TomlTable* toml_load_filename(cstr filename) -{ - TomlTable* table = NULL; - FILE* f = fopen(filename, "r"); - if (f == NULL) { - toml_err_set(TOML_ERR_OS, "Cannot open file %s [errno %d: %s]", - filename, errno, strerror(errno)); - goto error; - } - - table = toml_load_file_filename(f, filename); - - goto cleanup; - -error: - toml_table_free(table); - table = NULL; - -cleanup: - if (f != NULL) - fclose(f); - return table; -} diff --git a/src/toml_internal.h b/src/toml_internal.h new file mode 100644 index 0000000..a6344c2 --- /dev/null +++ b/src/toml_internal.h @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma onces + +#ifdef __cplusplus +extern "C" { +#endif + +#include "tlibtoml/toml.h" +#include "tlibc/string/StringBuilder.h" +#include "tlibc/collections/HashMap.h" +#include "tlibc/collections/List.h" +// TODO: get rid of libc assert +#include +#include + +typedef struct _TomlParser { + cstr begin; + cstr end; + cstr ptr; + i32 lineno; + i32 colno; + cstr filename; +} TomlParser; + +TomlParser* toml_parser_new(str s, cstr filename); +void toml_parser_free(TomlParser* self); + +void toml_move_next(TomlParser* self); +void toml_next_n(TomlParser* self, i32 n); +str toml_parse_bare_key(TomlParser* self); +char toml_hex_char_to_int(char ch); +Result(void) toml_encode_unicode_scalar(StringBuilder* sb_ptr, TomlParser* parser, i32 n); + +Result(str*) toml_parse_basic_string(TomlParser* self); +Result(str*) toml_parse_literal_string(TomlParser* self); +TomlValue toml_parse_basic_string_value(TomlParser* self); +TomlValue toml_parse_literal_string_value(TomlParser* self); +TomlValue toml_parse_multi_line_basic_string(TomlParser* self); +TomlValue toml_parse_multi_line_literal_string(TomlParser* self); +TomlValue toml_parse_datetime(str s); +TomlValue toml_parse_int_or_float_or_time(TomlParser* self); +TomlValue toml_parse_bool(TomlParser* self); +TomlValue toml_parse_value(TomlParser* self); +TomlValue toml_parse_array(TomlParser* self); +TomlValue toml_parse_inline_table(TomlParser* self); +Result(void) toml_parse_table(TomlParser* self, TomlTable* table); +Result(void) toml_parse_key_value(TomlParser* self, TomlTable* table); +Result(TomlTable*) toml_walk_table_path(TomlParser* parser, TomlTable* table, + TomlArray* key_path, i32 is_array, i32 create_if_not_exist); + + +#ifdef __cplusplus +} +#endif diff --git a/src/toml_load.c b/src/toml_load.c new file mode 100644 index 0000000..a9ce1ae --- /dev/null +++ b/src/toml_load.c @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" +#include "tlibc/filesystem.h" + +Result(TomlTable*) toml_load_str_filename(str s, cstr filename) +{ + Deferral(1); + + TomlParser* parser = toml_parser_new(s, filename); + Defer(toml_parser_free(parser)); + try(TomlTable* table, p, = toml_parse(parser)); + + Return RESULT_VALUE(p, table); +} + +Result(TomlTable*) toml_load_str(str s) +{ + return toml_load_str_filename(s, ""); +} + +Result(TomlTable*) toml_load_file(FILE* file) +{ + return toml_load_file_filename(file, ""); +} + +Result(TomlTable*) toml_load_filename(cstr filename) +{ + Deferral(4); + + try(FILE* f, p, file_open(filename, FO_ReadExisting)); + try(TomlTable* table, p, toml_load_file_filename(f, filename)); + + Return RESULT_VALUE(p, table); +} + +Result(TomlTable*) toml_load_file_filename(FILE* file, cstr filename) +{ + Deferral(8); + + const u32 file_chunk_size = 8*1024; + StringBuilder sb = StringBuilder_alloc(file_chunk_size); + Defer(StringBuilder_destroy(&sb)); + u64 count = 0; + u64 remaining_capacity = 0; + do { + remaining_capacity = sb.buffer.allocated_size - sb.buffer.size; + count = fread((char*)sb.buffer.data + sb.buffer.size, 1, remaining_capacity, file); + if (ferror(file)) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_OS, + "Error when reading %s [errno %d: %s]", + filename, errno, strerror(errno)); + } + + sb.buffer.size += count; + + if (sb.buffer.size + 1 >= sb.buffer.allocated_size) { + StringBuilder_increaseCapacity(&sb, file_chunk_size); + } + } while (count == remaining_capacity); + + try(TomlTable* table, p, toml_load_str_filename(StringBuilder_getStr(&sb), filename)); + + Return RESULT_VALUE(p, table); +} diff --git a/src/toml_parse/toml_parse.c b/src/toml_parse/toml_parse.c new file mode 100644 index 0000000..09e56ea --- /dev/null +++ b/src/toml_parse/toml_parse.c @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +Result(TomlTable*) toml_parse(TomlParser* self) +{ + Deferral(8); + TomlTable* table = TomlTable_new(); + Defer(TomlTable_free(table)); + + while (self->ptr < self->end) { + char ch = *self->ptr; + + while (self->ptr < self->end && isspace(ch)) { + toml_move_next(self); + ch = *self->ptr; + } + + if (ch == '#') { + do { + toml_move_next(self); + ch = *self->ptr; + } while (self->ptr < self->end && ch != '\n'); + toml_move_next(self); + } else if (ch == '[') { + toml_move_next(self); + if (toml_parse_table(self, table) != 0) + return NULL; + } else if (isalnum(ch) || ch == '_' || ch == '-') { + if (toml_parse_key_value(self, table) != 0) + return NULL; + } else if (ch == ' ' || ch == '\t' || ch == '\r') { + do { + toml_move_next(self); + ch = *self->ptr; + } while (ch == ' ' || ch == '\t' || ch == '\r'); + } else if (ch == '\n') { + toml_move_next(self); + } + } + + return table; +} diff --git a/src/toml_parse/toml_parse_array.c b/src/toml_parse/toml_parse_array.c new file mode 100644 index 0000000..08558bd --- /dev/null +++ b/src/toml_parse/toml_parse_array.c @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_array(TomlParser* self) +{ + TomlValue array_value = TomlValue_new_array(); + + while (self->ptr < self->end) { + if (isspace(*self->ptr)) { + while (self->ptr < self->end && isspace(*self->ptr)) { + toml_move_next(self); + } + } else if (*self->ptr == '#') { + do { + toml_move_next(self); + } while (self->ptr < self->end &&* self->ptr != '\n'); + toml_move_next(self); + } else if (*self->ptr == '\n') { + toml_move_next(self); + } else if (*self->ptr == ']') { + toml_move_next(self); + break; + } else { + TomlValue value = toml_parse_value(self); + if (value.type == TLIBTOML_INVALID_TYPE) { + TomlValue_destroy(&array_value); + //TODO: error handling + assert(false && "TODO error handling"); + } + + TomlArray_append(array_value.value.array, value); + + while (self->ptr < self->end) { + if (isspace(*self->ptr)) { + do { + toml_move_next(self); + } while (self->ptr < self->end && isspace(*self->ptr)); + } else if (*self->ptr == '#') { + do { + toml_move_next(self); + } while (self->ptr < self->end &&* self->ptr != '\n'); + } else { + break; + } + } + + if (self->ptr < self->end &&* self->ptr == ',') { + toml_move_next(self); + } + } + } + + return array_value; +} diff --git a/src/toml_parse/toml_parse_basic_string.c b/src/toml_parse/toml_parse_basic_string.c new file mode 100644 index 0000000..0b7d06b --- /dev/null +++ b/src/toml_parse/toml_parse_basic_string.c @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +Result(str*) toml_parse_basic_string(TomlParser* self) +{ + Deferral(16); + StringBuilder sb = StringBuilder_alloc(32); + + while (self->ptr < self->end &&* self->ptr != '\"' &&* self->ptr != '\n') { + char ch1 = *self->ptr; + if (ch1 == '\\') { + if (self->ptr >= self->end) break; + + toml_move_next(self); + char ch2 = *self->ptr; + + if (ch2 == '\"') { + StringBuilder_append_char(&sb, '\"'); + toml_move_next(self); + } else if (ch2 == 'b') { + StringBuilder_append_char(&sb, '\b'); + toml_move_next(self); + } else if (ch2 == 't') { + StringBuilder_append_char(&sb, '\t'); + toml_move_next(self); + } else if (ch2 == 'n') { + StringBuilder_append_char(&sb, '\n'); + toml_move_next(self); + } else if (ch2 == 'f') { + StringBuilder_append_char(&sb, '\f'); + toml_move_next(self); + } else if (ch2 == 'r') { + StringBuilder_append_char(&sb, '\r'); + toml_move_next(self); + } else if (ch2 == '\\') { + StringBuilder_append_char(&sb, '\\'); + toml_move_next(self); + } else if (ch2 == 'u') { + toml_move_next(self); + try_void(toml_encode_unicode_scalar(&sb, self, 4)); + } else if (ch2 == 'U') { + toml_move_next(self); + try_void(toml_encode_unicode_scalar(&sb, self, 8)); + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid escape character", + self->filename, self->lineno, self->colno); + } + } else { + StringBuilder_append_char(&sb, ch1); + toml_move_next(self); + } + } + + if (self->ptr >= self->end ||* self->ptr != '\"' ||* self->ptr == '\n') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated basic string", + self->filename, self->lineno, self->colno); + } + + toml_move_next(self); +} diff --git a/src/toml_parse/toml_parse_basic_string_value.c b/src/toml_parse/toml_parse_basic_string_value.c new file mode 100644 index 0000000..0499b74 --- /dev/null +++ b/src/toml_parse/toml_parse_basic_string_value.c @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_basic_string_value(TomlParser* self) +{ + str s = toml_parse_basic_string(self); + //TODO: error handling + assert(s.data != NULL); + + TomlValue value = TomlValue_new(TLIBTOML_STRING); + value.value.s = s; + return value; +} diff --git a/src/toml_parse/toml_parse_bool.c b/src/toml_parse/toml_parse_bool.c new file mode 100644 index 0000000..0fe486e --- /dev/null +++ b/src/toml_parse/toml_parse_bool.c @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_bool(TomlParser* self) +{ + if (self->ptr + 4 <= self->end && strncmp(self->ptr, "true", 4) == 0 && + (self->ptr + 4 == self->end || isspace(*(self->ptr + 4)) || *(self->ptr + 4) == ',' || *(self->ptr + 4) == ']' || *(self->ptr + 4) == '}')) { + toml_next_n(self, 4); + return TomlValue_new_bool(true); + } + + if (self->ptr + 5 <= self->end && strncmp(self->ptr, "false", 5) == 0 && + (self->ptr + 5 == self->end || isspace(*(self->ptr + 5)) || *(self->ptr + 5) == ',' || *(self->ptr + 5) == ']' || *(self->ptr + 5) == '}')) { + toml_next_n(self, 5); + return TomlValue_new_bool(false); + } + + //TODO: error handling + assert(false && "TODO error handling"); + return TomlValue_new(TLIBTOML_INVALID_TYPE); +} diff --git a/src/toml_parse/toml_parse_datetime.c b/src/toml_parse/toml_parse_datetime.c new file mode 100644 index 0000000..da28771 --- /dev/null +++ b/src/toml_parse/toml_parse_datetime.c @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_datetime(str s) +{ + (void)s; + //TODO: parse datetime + return TomlValue_new(TLIBTOML_DATETIME); +} diff --git a/src/toml_parse/toml_parse_inline_table.c b/src/toml_parse/toml_parse_inline_table.c new file mode 100644 index 0000000..2295573 --- /dev/null +++ b/src/toml_parse/toml_parse_inline_table.c @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_inline_table(TomlParser* self) +{ + TomlValue table_value = TomlValue_new_table(); + + while (self->ptr < self->end) { + char ch = *self->ptr; + while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { + toml_move_next(self); + ch = *self->ptr; + } + + str key; + if (isalnum(ch) || ch == '_') { + key = toml_parse_bare_key(self); + if (key.data == NULL) + goto error; + } else if (ch == '\"') { + toml_move_next(self); + key = toml_parse_basic_string(self); + if (key.data == NULL) + goto error; + } else if (ch == '\'') { + toml_move_next(self); + key = toml_parse_literal_string(self); + if (key.data == NULL) + goto error; + } else if (ch == '}') { + break; + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", + self->filename, self->lineno, self->colno); + goto error; + } + + ch = *self->ptr; + while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { + toml_move_next(self); + ch = *self->ptr; + } + + if (self->ptr == self->end) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", + self->filename, self->lineno, self->colno); + goto error; + } + + if (ch != '=') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", + self->filename, self->lineno, self->colno); + goto error; + } + + toml_move_next(self); + + ch = *self->ptr; + while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { + toml_move_next(self); + ch = *self->ptr; + } + + if (self->ptr == self->end) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", + self->filename, self->lineno, self->colno); + goto error; + } + + TomlValue value = toml_parse_value(self); + if (value.type == TLIBTOML_INVALID_TYPE) + goto error; + + TomlTable_set(table_value.value.table, key, value); + str_destroy(key); + + while (self->ptr < self->end && (*self->ptr == ' ' ||* self->ptr == '\t')) { + toml_move_next(self); + } + + if (*self->ptr == ',') { + toml_move_next(self); + } else if (*self->ptr == '}') { + toml_move_next(self); + break; + } + } + + goto end; + +error: + TomlValue_destroy(&table_value); + //TODO: error handling + assert(false && "TODO error handling"); +end: + return table_value; +} diff --git a/src/toml_parse/toml_parse_int_or_float_or_time.c b/src/toml_parse/toml_parse_int_or_float_or_time.c new file mode 100644 index 0000000..c23fb1c --- /dev/null +++ b/src/toml_parse/toml_parse_int_or_float_or_time.c @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_int_or_float_or_time(TomlParser* self) +{ + TomlValue value = {0}; + + char type = 'i'; + i32 base = 10; + + StringBuilder sb = StringBuilder_alloc(32); + + // Determine nan and inf type as float, as we cannot determine by dot. + // But do not strip it because we will append it to the string later + if (self->ptr + 3 <= self->end && + (strncmp(self->ptr, "nan", 3) == 0 || strncmp(self->ptr, "inf", 3) == 0)) { + type = 'f'; + } + + if (self->ptr + 4 <= self->end && + (strncmp(self->ptr, "+nan", 4) == 0 || + strncmp(self->ptr, "-nan", 4) == 0 || + strncmp(self->ptr, "+inf", 4) == 0 || + strncmp(self->ptr, "-inf", 4) == 0)) { + type = 'f'; + } + + // If there is a base prefix, set the base and strip the prefix, + // because strtoll() do not recognize 0o and 0b + if (self->ptr + 2 <= self->end) { + if (strncmp(self->ptr, "0x", 2) == 0) { + base = 16; + toml_next_n(self, 2); + } else if (strncmp(self->ptr, "0o", 2) == 0) { + base = 8; + toml_next_n(self, 2); + } else if (strncmp(self->ptr, "0b", 2) == 0) { + base = 2; + toml_next_n(self, 2); + } + } + + char last_char = 0; + i32 has_exp = false; + while (self->ptr < self->end) { + if (*self->ptr == '+' ||* self->ptr == '-') { + if (last_char == 0 || ((last_char == 'e' || last_char == 'E') && !has_exp)) { + if (last_char != 0) { + type = 'f'; + has_exp = true; + } + StringBuilder_append_char(&sb,* self->ptr); + } else { + break; + } + } else if (isalnum(*self->ptr)) { + if ((*self->ptr == 'e' ||* self->ptr == 'E') && base == 10) { + type = 'f'; + } + + StringBuilder_append_char(&sb,* self->ptr); + } else if (*self->ptr == '.') { + if (type == 'i') { + type = 'f'; + StringBuilder_append_char(&sb,* self->ptr); + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid float", + self->filename, self->lineno, self->colno); + StringBuilder_destroy(&sb); + //TODO: error handling + assert(false && "TODO error handling"); + } + } else if (*self->ptr == '_') { + if (type == 't') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid datetime", + self->filename, self->lineno, self->colno); + StringBuilder_destroy(&sb); + //TODO: error handling + assert(false && "TODO error handling"); + } + + if (!isalnum(last_char)) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid integer or float", + self->filename, self->lineno, self->colno); + StringBuilder_destroy(&sb); + //TODO: error handling + assert(false && "TODO error handling"); + } + } else if (*self->ptr == '-') { + type = 't'; + StringBuilder_append_char(&sb,* self->ptr); + } else { + break; + } + + last_char = *self->ptr; + toml_move_next(self); + } + + if (last_char == '_') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid integer or float or datetime", + self->filename, self->lineno, self->colno); + StringBuilder_destroy(&sb); + //TODO: error handling + assert(false && "TODO error handling"); + } + + if (type == 'i') { + char* end = NULL; + char* start = StringBuilder_getStr(&sb).data; + i64 n = strtoll(start, &end, base); + if (end < start + sb.buffer.size) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid integer", + self->filename, self->lineno, self->colno); + StringBuilder_destroy(&sb); + //TODO: error handling + assert(false && "TODO error handling"); + } + value = TomlValue_new_integer(n); + } else if (type == 'f') { + char* end = NULL; + char* start = StringBuilder_getStr(&sb).data; + f64 n = strtod(start, &end); + if (end < start + sb.buffer.size) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: invalid float", + self->filename, self->lineno, self->colno); + goto cleanup; + } + value = TomlValue_new_float(n); + } else if (type == 't') { + value = toml_parse_datetime(StringBuilder_getStr(&sb)); + } + +cleanup: + StringBuilder_destroy(&sb); + return value; +} diff --git a/src/toml_parse/toml_parse_key_value.c b/src/toml_parse/toml_parse_key_value.c new file mode 100644 index 0000000..b6bec7a --- /dev/null +++ b/src/toml_parse/toml_parse_key_value.c @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +Result(void) toml_parse_key_value(TomlParser* self, TomlTable* table) +{ + while (self->ptr < self->end) { + char ch; + + ch = *self->ptr; + while (self->ptr < self->end && isspace(ch)) { + toml_move_next(self); + ch = *self->ptr; + } + + if (self->ptr == self->end) break; + + str key; + if (isalnum(ch) || ch == '_') { + key = toml_parse_bare_key(self); + if (key.data == NULL) + return toml_err()->code; + } else if (ch == '\"') { + toml_move_next(self); + key = toml_parse_basic_string(self); + if (key.data == NULL) + return toml_err()->code; + } else if (ch == '\'') { + toml_move_next(self); + key = toml_parse_literal_string(self); + if (key.data == NULL) + return toml_err()->code; + } else if (ch == '[') { + break; + } else if (ch == '#') { + do { + toml_move_next(self); + ch = *self->ptr; + } while (self->ptr < self->end && ch != '\n'); + toml_move_next(self); + continue; + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", + self->filename, self->lineno, self->colno); + } + + ch = *self->ptr; + while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { + toml_move_next(self); + ch = *self->ptr; + } + + if (self->ptr == self->end) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", + self->filename, self->lineno, self->colno); + } + + if (ch != '=') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", + self->filename, self->lineno, self->colno); + } + + toml_move_next(self); + + ch = *self->ptr; + while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { + toml_move_next(self); + ch = *self->ptr; + } + + if (self->ptr == self->end) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair", + self->filename, self->lineno, self->colno); + } + + TomlValue value = toml_parse_value(self); + if (value.type == TLIBTOML_INVALID_TYPE) + return toml_err()->code; + + TomlTable_set(table, key, value); + str_destroy(key); + + while (self->ptr < self->end && (*self->ptr == ' ' ||* self->ptr == '\t')) { + toml_move_next(self); + } + + if (self->ptr == self->end) + break; + + if (*self->ptr == '#') { + do { + toml_move_next(self); + } while (self->ptr < self->end &&* self->ptr != '\n'); + } + + if (*self->ptr == '\r') { + toml_move_next(self); + } + + if (*self->ptr == '\n') { + toml_move_next(self); + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: new line expected", + self->filename, self->lineno, self->colno); + } + } + + return TLIBTOML_OK; +} diff --git a/src/toml_parse/toml_parse_literal_string.c b/src/toml_parse/toml_parse_literal_string.c new file mode 100644 index 0000000..de7cd0b --- /dev/null +++ b/src/toml_parse/toml_parse_literal_string.c @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +Result(str*) toml_parse_literal_string(TomlParser* self) +{ + Deferral(4); + StringBuilder sb = StringBuilder_alloc(32); + bool success = false; + Defer(if(!success) StringBuilder_destroy(&sb)); + + while (self->ptr < self->end &&* self->ptr != '\'' &&* self->ptr != '\n') { + StringBuilder_append_char(&sb,* self->ptr); + toml_move_next(self); + } + + if (self->ptr >= self->end ||* self->ptr != '\'' ||* self->ptr == '\n') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated literal string", + self->filename, self->lineno, self->colno); + } + + toml_move_next(self); + + success = true; + return StringBuilder_getStr(&sb); +} diff --git a/src/toml_parse/toml_parse_literal_string_value.c b/src/toml_parse/toml_parse_literal_string_value.c new file mode 100644 index 0000000..5ba9d77 --- /dev/null +++ b/src/toml_parse/toml_parse_literal_string_value.c @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_literal_string_value(TomlParser* self) +{ + str s = toml_parse_literal_string(self); + //TODO: error handling + assert(s.data != NULL); + + TomlValue value = TomlValue_new(TLIBTOML_STRING); + value.value.s = s; + return value; +} diff --git a/src/toml_parse/toml_parse_multi_line_basic_string.c b/src/toml_parse/toml_parse_multi_line_basic_string.c new file mode 100644 index 0000000..ca1b972 --- /dev/null +++ b/src/toml_parse/toml_parse_multi_line_basic_string.c @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_multi_line_basic_string(TomlParser* self) +{ + Deferral(4); + StringBuilder sb = StringBuilder_alloc(32); + bool success = false; + Defer(if(!success) StringBuilder_destroy(&sb)); + + if (*self->ptr == '\n') { + toml_move_next(self); + } + + while (self->ptr + 3 <= self->end && strncmp(self->ptr, "\"\"\"", 3) != 0) { + char ch1 = *self->ptr; + + if (ch1 == '\\') { + if (self->ptr + 3 > self->end || strncmp(self->ptr, "\"\"\"", 3) == 0) + break; + + toml_move_next(self); + char ch2 = *self->ptr; + + if (ch2 == '\"') { + StringBuilder_append_char(&sb, '\"'); + toml_move_next(self); + } else if (ch2 == 'b') { + StringBuilder_append_char(&sb, '\b'); + toml_move_next(self); + } else if (ch2 == 't') { + StringBuilder_append_char(&sb, '\t'); + toml_move_next(self); + } else if (ch2 == 'n') { + StringBuilder_append_char(&sb, '\n'); + toml_move_next(self); + } else if (ch2 == 'f') { + StringBuilder_append_char(&sb, '\f'); + toml_move_next(self); + } else if (ch2 == 'r') { + StringBuilder_append_char(&sb, '\r'); + toml_move_next(self); + } else if (ch2 == '\\') { + StringBuilder_append_char(&sb, '\\'); + toml_move_next(self); + } else if (ch2 == 'u') { + toml_move_next(self); + try_void(toml_encode_unicode_scalar(&sb, self, 4)); + } else if (ch2 == 'U') { + toml_move_next(self); + try_void(toml_encode_unicode_scalar(&sb, self, 8)); + } else if (ch2 == '\n') { + do { + toml_move_next(self); + } while (self->ptr + 3 <= self->end && isspace(*self->ptr)); + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, + "%s:%d:%d: invalid escape character", + self->filename, self->lineno, self->colno); + } + } else { + StringBuilder_append_char(&sb, ch1); + toml_move_next(self); + } + } + + if (self->ptr + 3 > self->end || strncmp(self->ptr, "\"\"\"", 3) != 0) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, + "%s:%d:%d: unterminated multi-line basic string", + self->filename, self->lineno, self->colno); + } + + toml_next_n(self, 3); + + TomlValue value = TomlValue_new(TLIBTOML_STRING); + value.value.s = StringBuilder_getStr(&sb); + return value; +} diff --git a/src/toml_parse/toml_parse_multi_line_literal_string.c b/src/toml_parse/toml_parse_multi_line_literal_string.c new file mode 100644 index 0000000..aef4be0 --- /dev/null +++ b/src/toml_parse/toml_parse_multi_line_literal_string.c @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_multi_line_literal_string(TomlParser* self) +{ + StringBuilder sb = StringBuilder_alloc(32); + + if (*self->ptr == '\n') { + toml_move_next(self); + } + + while (self->ptr + 3 <= self->end && strncmp(self->ptr, "\'\'\'", 3) != 0) { + StringBuilder_append_char(&sb,* self->ptr); + toml_move_next(self); + } + + if (self->ptr + 3 > self->end || strncmp(self->ptr, "\'\'\'", 3) != 0) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unterminated multi-line literal string", + self->filename, self->lineno, self->colno); + StringBuilder_destroy(&sb); + //TODO: error handling + assert(false && "TODO error handling"); + } + + toml_next_n(self, 3); + + TomlValue value = TomlValue_new(TLIBTOML_STRING); + value.value.s = StringBuilder_getStr(&sb); + return value; +} diff --git a/src/toml_parse/toml_parse_table.c b/src/toml_parse/toml_parse_table.c new file mode 100644 index 0000000..5ba33d5 --- /dev/null +++ b/src/toml_parse/toml_parse_table.c @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +Result(void) toml_parse_table(TomlParser* self, TomlTable* table) +{ + Deferral(16); + + i32 is_array = false; + TomlTable* real_table = table; + TomlArray* key_path = TomlArray_new(); + Defer(TomlArray_free(key_path)); + + if (self->ptr < self->end &&* self->ptr == '[') { + is_array = true; + toml_move_next(self); + } + + while (1) { + if (*self->ptr == ' ' ||* self->ptr == '\t') { + do { + toml_move_next(self); + } while (*self->ptr <* self->end && (*self->ptr == ' ' ||* self->ptr == '\t')); + } else if (*self->ptr == ']') { + if (is_array) { + if (self->ptr + 2 <= self->end && strncmp(self->ptr, "]]", 2) == 0) { + toml_next_n(self, 2); + break; + } + } else { + toml_move_next(self); + break; + } + } else { + str key_part; + if (isalnum(*self->ptr) ||* self->ptr == '_') { + key_part = toml_parse_bare_key(self); + if (key_part.data == NULL) + goto cleanup; + } else if (*self->ptr == '\"') { + toml_move_next(self); + key_part = toml_parse_basic_string(self); + if (key_part.data == NULL) + goto cleanup; + } else if (*self->ptr == '\'') { + toml_move_next(self); + key_part = toml_parse_literal_string(self); + if (key_part.data == NULL) + goto cleanup; + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", + self->filename, self->lineno, self->colno); + goto cleanup; + } + + TomlValue key_part_value = TomlValue_new(TLIBTOML_STRING); + key_part_value.value.s = key_part; + + TomlArray_append(key_path, key_part_value); + + while (self->ptr < self->end && + (*self->ptr == ' ' ||* self->ptr == '\t')) { + toml_move_next(self); + } + + if (self->ptr < self->end &&* self->ptr == '.') { + toml_move_next(self); + } + } + } + + if (key_path->len == 0) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: empty table name", + self->filename, self->lineno, self->colno); + goto cleanup; + } + + while (self->ptr < self->end && + (*self->ptr == ' ' ||* self->ptr == '\t' ||* self->ptr == '\r')) { + toml_move_next(self); + } + + if (self->ptr < self->end &&* self->ptr != '\n') { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: new line expected", + self->filename, self->lineno, self->colno); + goto cleanup; + } + + real_table = toml_walk_table_path(self, table, key_path, is_array, true); + if (real_table == NULL) + goto error; + + if(toml_parse_key_value(self, real_table) != TLIBTOML_OK) + goto error; + + Return RESULT_VOID; +} diff --git a/src/toml_parse/toml_parse_value.c b/src/toml_parse/toml_parse_value.c new file mode 100644 index 0000000..96df99a --- /dev/null +++ b/src/toml_parse/toml_parse_value.c @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlValue toml_parse_value(TomlParser* self) +{ + char ch = *self->ptr; + + TomlValue value; + if (strncmp(self->ptr, "\"\"\"", 3) == 0) { + toml_next_n(self, 3); + value = toml_parse_multi_line_basic_string(self); + } else if (strncmp(self->ptr, "\'\'\'", 3) == 0) { + toml_next_n(self, 3); + value = toml_parse_multi_line_literal_string(self); + } else if (ch == '\"') { + toml_move_next(self); + value = toml_parse_basic_string_value(self); + } else if (ch == '\'') { + toml_move_next(self); + value = toml_parse_literal_string_value(self); + } else if (isdigit(ch) || ch == '+' || ch == '-' || ch == '.' || ch == 'n' || ch == 'i') { + value = toml_parse_int_or_float_or_time(self); + } else if (ch == 't' || ch == 'f') { + value = toml_parse_bool(self); + } else if (ch == '[') { + toml_move_next(self); + value = toml_parse_array(self); + } else if (ch == '{') { + toml_move_next(self); + value = toml_parse_inline_table(self); + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", + self->filename, self->lineno, self->colno); + //TODO: error handling + assert(false && "TODO error handling"); + } + + return value; +} diff --git a/src/toml_parse/toml_walk_table_path.c b/src/toml_parse/toml_walk_table_path.c new file mode 100644 index 0000000..525dadb --- /dev/null +++ b/src/toml_parse/toml_walk_table_path.c @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + + +TomlTable* toml_walk_table_path(TomlParser* parser, TomlTable* table, + TomlArray* key_path, i32 is_array, i32 create_if_not_exist) +{ + TomlTable* real_table = table; + + if (is_array) { + u64 i = 0; + for (; i < key_path->len - 1; i++) { + str part = key_path->elements[i].value.s; + TomlValue* t = TomlTable_get(real_table, part); + if (t == NULL) { + if (create_if_not_exist) { + TomlValue new_table_value = TomlValue_new_table(); + TomlTable_set(real_table, part, new_table_value); + real_table = new_table_value.value.table; + } else { + real_table = NULL; + break; + } + } else { + real_table = t->value.table; + } + } + + str part = key_path->elements[i].value.s; + TomlValue* t = TomlTable_get(real_table, part); + if (t == NULL) { + if (create_if_not_exist) { + TomlValue array_value = TomlValue_new_array(); + TomlValue new_table_value = TomlValue_new_table(); + TomlArray_append(array_value.value.array, new_table_value); + TomlTable_set(real_table, part, array_value); + real_table = new_table_value.value.table; + } else { + real_table = NULL; + } + } else { + if (t->type != TLIBTOML_ARRAY) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_SYNTAX, "%s:%d:%d: this key was not an array", + parser->filename, parser->lineno, parser->colno); + goto error; + } + + TomlValue new_table_value = TomlValue_new_table(); + TomlArray_append(t->value.array, new_table_value); + real_table = new_table_value.value.table; + } + } else { + for (u64 i = 0; i < key_path->len; i++) { + str part = key_path->elements[i].value.s; + TomlValue* t = TomlTable_get(real_table, part); + if (t == NULL) { + if (create_if_not_exist) { + TomlValue new_table_value = TomlValue_new_table(); + TomlTable_set(real_table, part, new_table_value); + real_table = new_table_value.value.table; + } else { + real_table = NULL; + break; + } + } else { + if (t->type == TLIBTOML_ARRAY) { + real_table = t->value.array->elements[t->value.array->len - 1].value.table; + } else if (t->type == TLIBTOML_TABLE) { + real_table = t->value.table; + } + } + } + } + + goto end; + +error: + real_table = NULL; + //TODO: error handling + assert(false && "TODO error handling"); + +end: + return real_table; +} diff --git a/src/toml_parser.c b/src/toml_parser.c new file mode 100644 index 0000000..cd44d18 --- /dev/null +++ b/src/toml_parser.c @@ -0,0 +1,155 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "toml_internal.h" + +TomlParser* toml_parser_new(str s, cstr filename) +{ + TomlParser* parser = malloc(sizeof(TomlParser)); + parser->begin = s.data; + parser->end = s.data + s.size; + parser->ptr = s.data; + parser->lineno = 1; + parser->colno = 1; + parser->filename = filename; + return parser; +} + +void toml_parser_free(TomlParser* self) +{ + if (self == NULL) + return; + + free(self); +} + + +void toml_move_next(TomlParser* self) +{ + if (self->ptr < self->end) { + if (*self->ptr == '\n') { + self->lineno++; + self->colno = 1; + } else { + self->colno++; + } + self->ptr++; + } +} + +void toml_next_n(TomlParser* self, i32 n) +{ + for (i32 i = 0; i < n; i++) { + toml_move_next(self); + } +} + +str toml_parse_bare_key(TomlParser* self) +{ + cstr s = self->ptr; + u64 len = 0; + + while (self->ptr < self->end) { + char ch = *self->ptr; + + if (!(isalnum(ch) || ch == '_' || ch == '-')) + break; + + len++; + toml_move_next(self); + } + + return str_copy(str_construct((void*)s, len, false)); +} + +char toml_hex_char_to_int(char ch) +{ + assert(isxdigit(ch)); + if (isdigit(ch)) { + return ch - '0'; + } else if (islower(ch)) { + return ch - 'a' + 10; + } else if (isupper(ch)) { + return ch - 'A' + 10; + } + return 0; +} + +Result(void) toml_encode_unicode_scalar(StringBuilder* sb_ptr, TomlParser* parser, i32 n) +{ + Deferral(16); + u32 scalar = 0; + + if (parser->ptr + n > parser->end) { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_UNICODE, + "%s:%d:%d: invalid unicode scalar", + parser->filename, parser->lineno, parser->colno); + } + + for (i32 i = 0; i < n; i++) { + char ch = *parser->ptr; + if (isxdigit(ch)) { + scalar = scalar * 16 + (u32)toml_hex_char_to_int(ch); + toml_move_next(parser); + } else { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", + parser->filename, parser->lineno, parser->colno); + } + } + + if ((scalar >= 0xd800 && scalar <= 0xdfff) || + (scalar >= 0xfffe && scalar <= 0xffff)) + { + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", + parser->filename, parser->lineno, parser->colno); + } + + if (scalar <= 0x7f) { + StringBuilder_append_char(sb_ptr, (char)scalar); + Return RESULT_VOID; + } + + if (scalar <= 0x7ff) { + StringBuilder_append_char(sb_ptr, (char)(0xc0 | (scalar >> 6))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); + Return RESULT_VOID; + } + + if (scalar <= 0xffff) { + StringBuilder_append_char(sb_ptr, (char)(0xe0 | (scalar >> 12))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); + Return RESULT_VOID; + } + + if (scalar <= 0x1fffff) { + StringBuilder_append_char(sb_ptr, (char)(0xf0 | (scalar >> 18))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 12) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); + Return RESULT_VOID; + } + + if (scalar <= 0x3ffffff) { + StringBuilder_append_char(sb_ptr, (char)(0xf8 | (scalar >> 24))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 18) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 12) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); + Return RESULT_VOID; + } + + if (scalar <= 0x7fffffff) { + StringBuilder_append_char(sb_ptr, (char)(0xfc | (scalar >> 30))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 24) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 18) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 12) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | ((scalar >> 6) & 0x3f))); + StringBuilder_append_char(sb_ptr, (char)(0x80 | (scalar & 0x3f))); + Return RESULT_VOID; + } + + Return RESULT_ERROR_CODE_FMT(TLIBTOML, TLIBTOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", + parser->filename, parser->lineno, parser->colno); +} diff --git a/tests/main.c b/tests/main.c index 420aeab..ade4c94 100644 --- a/tests/main.c +++ b/tests/main.c @@ -31,25 +31,25 @@ void print_value(const TomlValue* value) default: assert(false && "invalid type"); break; - case TOML_TABLE: + case TLIBTOML_TABLE: print_table(value->value.table); break; - case TOML_ARRAY: + case TLIBTOML_ARRAY: print_array(value->value.array); break; - case TOML_STRING: + case TLIBTOML_STRING: printf("\"%s\"", value->value.s.data); break; - case TOML_INTEGER: + case TLIBTOML_INTEGER: printf("%" PRId64, value->value.i); break; - case TOML_FLOAT: + case TLIBTOML_FLOAT: printf("%f", value->value.f); break; - case TOML_DATETIME: + case TLIBTOML_DATETIME: printf("(datetime)"); break; - case TOML_BOOLEAN: + case TLIBTOML_BOOLEAN: printf("%s", value->value.b ? "true" : "false"); break; } @@ -78,31 +78,24 @@ void print_table(const TomlTable* table) printf("}"); } -i32 test_run(cstr filename) +Result(void) test_run(cstr filename) { - TomlTable* table = NULL; - i32 rc = 0; - - table = toml_load_filename(filename); - if (table == NULL) - goto cleanup; - + Deferral(4); + try(TomlTable* table, p, toml_load_filename(filename)); + Defer(TomlTable_free(table)); print_table(table); printf("\n"); cleanup: - toml_table_free(table); - - if (toml_err()->code != TOML_OK) { - fprintf(stderr, "%s\n", toml_err()->message); - rc = (i32)toml_err()->code; - } - toml_err_clear(); - return rc; + ; } i32 main(void) { + Deferral(32); + try_fatal_void(tlibc_init()); + Defer(tlibc_deinit()); + static cstr const filenames[] = { /* should parse */ PROJECT_SOURCE_DIR "/tests/key-values.toml", @@ -123,12 +116,14 @@ i32 main(void) i32 num_failed = 0; for (i32 i = 0; i < total_tests; i++) { - i32 rc = test_run(filenames[i]); - if (rc == 0) { + ResultVar(void) r = test_run(filenames[i]); + if (!r.error) { printf("test %d success\n", i); num_passed++; } else { - printf("test %d returned %d\n", i, rc); + str err_str = Error_toStr(r.error); + printf("%s\ntest %d failed\n", err_str.data, i); + str_destroy(err_str); num_failed++; } }