diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index 3184e02..a25bfdd 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -325,7 +325,7 @@ char *string_to_debug_str__(CTX_DECLARATION, const char *name, const char *strin // Check if the pointer is readable. if (!is_readable(string)) - return arena_strdup_fmt__(CTX(arena), "%s = unreadable", name); + return arena_strdup_fmt__(CTX(arena), "%s = ", name); return arena_strdup_fmt__(CTX(arena), "%s = %s%p%s \"%s\"", name, DEBUG_COLOR(COLOR_CYAN), string, @@ -385,14 +385,14 @@ char *union_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char * if (!variant_exists) { return arena_strdup_fmt__(file, func, line, arena, - "%sunion%s %s %s = {invalid variant %d} %s%p%s", + "%sunion%s %s %s = %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), type, name, active_variant, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); } if (!value) { return arena_strdup_fmt__(file, func, line, arena, - "%sunion%s %s %s = {unknown variant} %s%p%s", + "%sunion%s %s %s = %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); } @@ -1473,12 +1473,254 @@ char* json_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, Json json) { return result; } +JsonResult debug_str_to_json__(POSITION_INFO_DECLARATION, Arena *arena, const char **s) { + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "DEBUG STR TO JSON: debug_str: %s", *s); + + // Remove the unused 'start' variable + Json *json = arena_alloc__(POSITION_INFO, arena, sizeof(Json)); + memset(json, 0, sizeof(Json)); + + // Extract the name/key + const char *equal_sign = strstr(*s, "="); + if (!equal_sign) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no equal sign found"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_EQUAL_SIGN_ERROR, "No equal sign found"); + } + + Slice full_name = slice_create__(POSITION_INFO, 1, *s, strlen(*s), 0, equal_sign - *s); + if (full_name.len == 0) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no name found"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR, "No left operand found"); + } + + // Move past the equal sign + *s = skip_whitespace(equal_sign + 1); + + // Check for struct, union, enum, or other types + const char *name_str = full_name.data; + name_str = skip_whitespace(name_str); + + if (strncmp(name_str, "struct ", 7) == 0) { + // Handle struct + json->type = JSON_OBJECT; + + // Extract struct type and name + name_str += 7; // Skip "struct " + const char *space = strchr(name_str, ' '); + + if (!space) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: missing struct name"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR, "Struct without name"); + } + + // Extract type (between "struct " and space) + + // Extract name (after space, before any other character) + name_str = skip_whitespace(space + 1); + const char *name_end = name_str; + while (*name_end && !isspace(*name_end) && *name_end != '{') name_end++; + + if (name_end == name_str) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: missing struct variable name"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR, "Struct without variable name"); + } + + size_t name_len = name_end - name_str; + json->key = arena_strncpy__(POSITION_INFO, arena, name_str, name_len); + + // Find struct body + const char *body_start = strchr(name_end, '{'); + if (!body_start) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no start found for struct"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_START_ERROR, "Struct without start"); + } + + const char *body_end = strrchr(body_start, '}'); + if (!body_end) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no end found for struct"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_END_ERROR, "Struct without end"); + } + + // Move pointer past the struct + *s = body_end + 1; + + // TODO: Parse struct fields + // For now, we're just creating an empty object + } + else if (strncmp(name_str, "union ", 6) == 0) { + // Handle union + json->type = JSON_OBJECT; + + // Extract union name + name_str += 6; // Skip "union " + const char *space = strchr(name_str, ' '); + + if (!space) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: missing union name"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR, "Union without name"); + } + + // Extract type (between "union " and space) + + // Extract name (after space, before any other character) + name_str = skip_whitespace(space + 1); + const char *name_end = name_str; + while (*name_end && !isspace(*name_end) && *name_end != '{') name_end++; + + size_t name_len = name_end - name_str; + json->key = arena_strncpy__(POSITION_INFO, arena, name_str, name_len); + + // Find body + const char *body_start = strchr(name_end, '{'); + if (!body_start) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no start found for union"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_START_ERROR, "Union without start"); + } + + const char *body_end = strrchr(body_start, '}'); + if (!body_end) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no end found for union"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_END_ERROR, "Union without end"); + } + + // Move pointer past the union + *s = body_end + 1; + + // TODO: Parse union variant + // For now, we're just creating an empty object + } + else if (strncmp(name_str, "enum ", 5) == 0) { + // Handle enum + json->type = JSON_STRING; + + // Find enum value (typically at the end) + const char *value_start = strrchr(*s, ' '); + if (!value_start) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: missing enum value"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR, "Invalid enum format"); + } + + // Extract name + name_str += 5; // Skip "enum " + const char *space = strchr(name_str, ' '); + + if (space) { + size_t name_len = space - name_str; + json->key = arena_strncpy__(POSITION_INFO, arena, name_str, name_len); + } + + // Extract value as string + value_start = skip_whitespace(value_start + 1); + json->JsonValue.string = arena_strdup__(POSITION_INFO, arena, value_start); + + // Move pointer to the end + *s += strlen(*s); + } + else if (strchr(name_str, '[') && strchr(name_str, ']')) { + // Handle array + json->type = JSON_ARRAY; + + // Extract array name + const char *bracket = strchr(name_str, '['); + if (bracket > name_str) { + const char *name_end = bracket; + while (name_end > name_str && isspace(*(name_end-1))) name_end--; + + size_t name_len = name_end - name_str; + json->key = arena_strncpy__(POSITION_INFO, arena, name_str, name_len); + } + + // Find array body + const char *body_start = strchr(*s, '['); + if (!body_start) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no start found for array"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_START_ERROR, "Array without start"); + } + + const char *body_end = strrchr(body_start, ']'); + if (!body_end) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: no end found for array"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_NO_END_ERROR, "Array without end"); + } + + // Move pointer past the array + *s = body_end + 1; + + // TODO: Parse array elements + // For now, we're just creating an empty array + } + else { + // Try to determine value type (string, number, bool, null) + const char *value = *s; + + if (strncmp(value, "NULL", 4) == 0 || strncmp(value, "null", 4) == 0) { + json->type = JSON_NULL; + *s += 4; + } + else if (strncmp(value, "true", 4) == 0) { + json->type = JSON_BOOL; + json->JsonValue.boolean = true; + *s += 4; + } + else if (strncmp(value, "false", 5) == 0) { + json->type = JSON_BOOL; + json->JsonValue.boolean = false; + *s += 5; + } + else if (*value == '"') { + // String value + json->type = JSON_STRING; + value++; // Skip opening quote + + const char *end_quote = strchr(value, '"'); + if (!end_quote) { + raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "DEBUG STR TO JSON: unterminated string"); + return RESULT_ERROR(JsonResult, DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR, "Unterminated string"); + } + + size_t str_len = end_quote - value; + json->JsonValue.string = arena_strncpy__(POSITION_INFO, arena, value, str_len); + *s = end_quote + 1; + } + else if (isdigit(*value) || *value == '-' || *value == '+') { + // Numeric value + json->type = JSON_NUMBER; + + // Use strtod to parse the number + char *end; + json->JsonValue.number = strtod(value, &end); + *s = end; + } + else { + // Default to string for unknown types + json->type = JSON_STRING; + + // Find the end of the value (space, comma, etc.) + const char *end = value; + while (*end && !isspace(*end) && *end != ',' && *end != '}' && *end != ']') end++; + + size_t str_len = end - value; + json->JsonValue.string = arena_strncpy__(POSITION_INFO, arena, value, str_len); + *s = end; + } + + // Extract name for simple types + const char *name_end = name_str; + while (*name_end && !isspace(*name_end) && *name_end != '=') name_end++; + + size_t name_len = name_end - name_str; + json->key = arena_strncpy__(POSITION_INFO, arena, name_str, name_len); + } + + return RESULT_SOME(JsonResult, *json); +} + // ----------- // -- slice -- // ----------- // Create a slice from an array with boundary check. -Slice slice_create__(POSITION_INFO_DECLARATION, size_t isize, void *array, size_t array_len, size_t start, size_t len) { +Slice slice_create__(POSITION_INFO_DECLARATION, size_t isize, const void *array, size_t array_len, size_t start, size_t len) { // Function entry logging raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "SLICE: Creating slice (source: %p, array_length: %zu, start: %zu, length: %zu, item_size: %zu)", diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index 5cb4e8e..35cc0a8 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -85,6 +85,11 @@ typedef enum { TEMPLATE_ERROR_OUT_OF_MEMORY = 985575, LOGGER_ERROR_INVALID_RULES_STRING = 985576, LOGGER_ERROR_OUT_OF_MEMORY = 985577, + DEBUG_TO_JSON_PARSE_NO_EQUAL_SIGN_ERROR = 985578, + DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR = 985579, + DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR = 985580, + DEBUG_TO_JSON_PARSE_NO_START_ERROR = 985581, + DEBUG_TO_JSON_PARSE_NO_END_ERROR = 985582, } HecticErrorCode; // Define color macros based on output type @@ -464,7 +469,7 @@ bool debug_ptrset_contains(PtrSet *set, void *ptr); name = "$1"; \ \ if (debug_ptrset_contains__(visited, ptr, #type, name)) \ - return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sunion%s %s %s = {cycle detected} %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); \ + return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sunion%s %s %s = %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); \ \ if (!ptr) \ return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sunion%s %s %s = NULL", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name); \ @@ -504,7 +509,7 @@ bool debug_ptrset_contains(PtrSet *set, void *ptr); name = "$1"; \ \ if (debug_ptrset_contains__(visited, ptr, #type, name)) \ - return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sstruct%s %s %s = {cycle detected} %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); \ + return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sstruct%s %s %s = %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); \ \ if (!ptr) \ return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sstruct%s %s %s = NULL", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name); \ @@ -588,6 +593,8 @@ typedef struct Json { } JsonValue; } Json; +RESULT(Json, Json); + #define json_parse(arena, s) json_parse__(__FILE__, __func__, __LINE__, arena, s) Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s); @@ -608,8 +615,12 @@ char* json_to_debug_str__(const char* file, const char* func, int line, Arena *a char *json_to_pretty_str__(const char* file, const char* func, int line, Arena *arena, const Json * const item, int indent_level); +JsonResult debug_str_to_json__(const char* file, const char* func, int line, Arena *arena, const char **s); + #define json_to_pretty_str(arena, json) json_to_pretty_str__(__FILE__, __func__, __LINE__, arena, json, 0) +#define DEBUG_STR_TO_JSON(arena, debug_ptr) debug_str_to_json__(__FILE__, __func__, __LINE__, arena, debug_ptr) + // ----------- // -- Slice -- // ----------- @@ -625,7 +636,7 @@ typedef struct { // printf("Content: %d\n", SLICE_ARGS(slice, int)); #define SLICE_ARGS(slice, type) ((int)((slice).len / sizeof(type))), ((type*)((slice).data)) -Slice slice_create__(const char *file, const char *func, int line, size_t isize, void *array, size_t array_len, size_t start, size_t len); +Slice slice_create__(const char *file, const char *func, int line, size_t isize, const void *array, size_t array_len, size_t start, size_t len); Slice slice_subslice__(const char *file, const char *func, int line, Slice s, size_t start, size_t len); diff --git a/package/c/hectic/test/04-json.c b/package/c/hectic/test/04-json.c index a502f2c..c76fc87 100644 --- a/package/c/hectic/test/04-json.c +++ b/package/c/hectic/test/04-json.c @@ -8,91 +8,76 @@ #define ARENA_SIZE 1024 * 1024 // Test 1: Parse JSON object with a string value. -static void test_parse_json_object(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_parse_json_object(Arena *arena) { const char *json = "{\"key\":\"value\"}"; - Json *root = json_parse(&arena, &json); + Json *root = json_parse(arena, &json); assert(root->type == JSON_OBJECT); Json *child = root->child; assert(child && strcmp(child->key, "key") == 0); assert(child->type == JSON_STRING); assert(strcmp(child->JsonValue.string, "value") == 0); - arena_free(&arena); } // Test 2: Parse JSON number. -static void test_parse_json_number(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_parse_json_number(Arena *arena) { const char *json = "42"; - Json *root = json_parse(&arena, &json); + Json *root = json_parse(arena, &json); assert(root->type == JSON_NUMBER); assert(root->JsonValue.number == 42); - arena_free(&arena); } // Test 3: Parse JSON string. -static void test_parse_json_string(void) { - Arena arena = {0}; +static void test_parse_json_string(Arena *arena) { const char *json = "\"hello\""; - Json *root = json_parse(&arena, &json); + Json *root = json_parse(arena, &json); assert(root->type == JSON_STRING); assert(strcmp(root->JsonValue.string, "hello") == 0); - arena_free(&arena); } // Test 4: Get object items by key. -static void test_get_object_items(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_get_object_items(Arena *arena) { const char *json = "{\"a\":\"1\", \"b\":2}"; - Json *root = json_parse(&arena, &json); + Json *root = json_parse(arena, &json); Json *item_a = json_get_object_item(root, "a"); assert(item_a && item_a->type == JSON_STRING); assert(strcmp(item_a->JsonValue.string, "1") == 0); Json *item_b = json_get_object_item(root, "b"); assert(item_b && item_b->type == JSON_NUMBER); assert(item_b->JsonValue.number == 2); - arena_free(&arena); } // Test 5: Print JSON object. -static void test_print_json_object(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_print_json_object(Arena *arena) { const char *json = "{\"key\":\"value\", \"num\":3.14}"; - Json *root = json_parse(&arena, &json); - char *printed = json_to_string(&arena, root); + Json *root = json_parse(arena, &json); + char *printed = json_to_string(arena, root); assert(strstr(printed, "\"key\":") != NULL); assert(strstr(printed, "\"value\"") != NULL); assert(strstr(printed, "\"num\":") != NULL); assert(strstr(printed, "3.14") != NULL); - arena_free(&arena); } // Test 6: Print JSON number. -static void test_print_json_number(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_print_json_number(Arena *arena) { const char *json = "123.456"; - Json *root = json_parse(&arena, &json); - char *printed = json_to_string(&arena, root); + Json *root = json_parse(arena, &json); + char *printed = json_to_string(arena, root); double val = atof(printed); assert(val == 123.456); - arena_free(&arena); } // Test 7: Print JSON string. -static void test_print_json_string(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_print_json_string(Arena *arena) { const char *json = "\"test string\""; - Json *root = json_parse(&arena, &json); - char *printed = json_to_string(&arena, root); + Json *root = json_parse(arena, &json); + char *printed = json_to_string(arena, root); assert(strcmp(printed, "\"test string\"") == 0); - arena_free(&arena); } // Test 8: Nested JSON object. -static void test_nested_json_object(void) { - Arena arena = arena_init(1024 * 1024); +static void test_nested_json_object(Arena *arena) { const char *json = "{\"outer\":{\"inner\":100}}"; - Json *root = json_parse(&arena, &json); + Json *root = json_parse(arena, &json); assert(root != NULL); assert(root->type == JSON_OBJECT); @@ -105,38 +90,59 @@ static void test_nested_json_object(void) { assert(inner->type == JSON_NUMBER); assert(inner->JsonValue.number == 100); - arena_free(&arena); } // Test 9: Arena reset and reuse. -static void test_arena_reset_reuse(void) { - Arena arena = arena_init(ARENA_SIZE); +static void test_arena_reset_reuse(Arena *arena) { const char *json1 = "{\"key\":\"value\"}"; - Json *root1 = json_parse(&arena, &json1); - char *printed1 = json_to_string(&arena, root1); + Json *root1 = json_parse(arena, &json1); + char *printed1 = json_to_string(arena, root1); assert(strcmp(printed1, "{\"key\":\"value\"}") == 0); - arena_reset(&arena); + arena_reset(arena); const char *json2 = "\"another test\""; - Json *root2 = json_parse(&arena, &json2); - char *printed2 = json_to_string(&arena, root2); + Json *root2 = json_parse(arena, &json2); + char *printed2 = json_to_string(arena, root2); assert(strcmp(printed2, "\"another test\"") == 0); - arena_free(&arena); +} + +static void test_debug_str_to_json(Arena *arena) { + const char *debug_str = "struct SomeStruct struct_name = { name = \"value\", next = NULL, value = 123 }"; + JsonResult result = DEBUG_STR_TO_JSON(arena, &debug_str); + if (IS_RESULT_ERROR(result)) { + raise_exception("DEBUG_STR_TO_JSON: %s", &RESULT_ERROR_MESSAGE(result)); + return; + } + raise_notice("result: %s", json_to_string(arena, &RESULT_SOME_VALUE(result))); + assert(RESULT_SOME_VALUE(result).type == JSON_OBJECT); } int main(void) { printf("%sRunning %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); logger_init(); - test_parse_json_object(); - test_parse_json_number(); - test_parse_json_string(); - test_get_object_items(); - test_print_json_object(); - test_print_json_number(); - test_print_json_string(); - test_nested_json_object(); - test_arena_reset_reuse(); + Arena arena = arena_init(ARENA_SIZE); + test_parse_json_object(&arena); + arena_reset(&arena); + test_parse_json_number(&arena); + arena_reset(&arena); + test_parse_json_string(&arena); + arena_reset(&arena); + test_get_object_items(&arena); + arena_reset(&arena); + test_print_json_object(&arena); + arena_reset(&arena); + test_print_json_number(&arena); + arena_reset(&arena); + test_print_json_string(&arena); + arena_reset(&arena); + test_nested_json_object(&arena); + arena_reset(&arena); + test_arena_reset_reuse(&arena); + arena_reset(&arena); + test_debug_str_to_json(&arena); + + arena_free(&arena); logger_free(); printf("%sall tests passed.%s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); return 0; diff --git a/package/c/hectic/test/06-templater.c b/package/c/hectic/test/06-templater.c index 63c5326..9a02386 100755 --- a/package/c/hectic/test/06-templater.c +++ b/package/c/hectic/test/06-templater.c @@ -89,6 +89,6 @@ int main(void) { logger_free(); arena_free(&arena); - printf("%sall tests passed.%s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); + printf("%sall tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); return 0; } \ No newline at end of file