feat: hectic C: union in debug string constructor

This commit is contained in:
2025-04-14 16:28:16 +00:00
parent 807ea83892
commit abf1e706b7
4 changed files with 119 additions and 30 deletions

View File

@@ -1,16 +1,7 @@
// ------------
// -- Colors --
// ------------
// Color mode enumeration
typedef enum {
COLOR_MODE_AUTO,
COLOR_MODE_FORCE,
COLOR_MODE_DISABLE
} ColorMode;
// External color mode variable declaration
extern ColorMode color_mode;
extern ColorMode debug_color_mode;
const char* color_mode_to_string(ColorMode mode);
@@ -30,7 +21,7 @@ void set_output_color_mode(ColorMode mode);
* DEBUG_COLOR_MODE is the color mode for debug output after USE_COLOR() check.
* used for debug colorized output
*/
#define USE_COLOR_IN_DEBUG() (USE_COLOR() ? DEBUG_COLOR_MODE : COLOR_MODE_DISABLE)
#define USE_COLOR_IN_DEBUG() (color_mode == COLOR_MODE_AUTO ? ((debug_color_mode == COLOR_MODE_FORCE) || (debug_color_mode == COLOR_MODE_AUTO && IS_TERMINAL())) : USE_COLOR())
#define COLOR_RED "\033[1;31m"
#define COLOR_GREEN "\033[1;32m"
@@ -44,6 +35,5 @@ void set_output_color_mode(ColorMode mode);
#define OPTIONAL_COLOR(color) (USE_COLOR() ? color : "")
#define DEBUG_COLOR(color) (USE_COLOR_IN_DEBUG() ? color : "")
OPTIONAL_COLOR(COLOR_RED) "Hello" OPTIONAL_COLOR(COLOR_RESET)
">>>>"
DEBUG_COLOR(COLOR_RED) "Hello" DEBUG_COLOR(COLOR_RESET)

View File

@@ -288,7 +288,7 @@ void debug_ptrset_add__(CTX_DECLARATION, PtrSet *set, const void *ptr, const cha
}
char *enum_to_debug_str__(CTX_DECLARATION, const char *name, size_t enum_value, const char *enum_str) {
return arena_strdup_fmt__(CTX(arena), "%s = %s%s%s %zu ", name, DEBUG_COLOR(COLOR_CYAN), enum_str, DEBUG_COLOR(COLOR_RESET), enum_value);
return arena_strdup_fmt__(CTX(arena), "%senum%s %s = %s%s%s %zu ", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), name, DEBUG_COLOR(COLOR_CYAN), enum_str, DEBUG_COLOR(COLOR_RESET), enum_value);
}
char *string_to_debug_str__(CTX_DECLARATION, const char *name, const char *string) {
@@ -321,6 +321,53 @@ char *char_to_debug_str__(CTX_DECLARATION, const char *name, char c) {
return arena_strdup_fmt__(CTX(arena), "%s = %c", name, c);
}
char *union_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *type, const char *name, const void *ptr, size_t active_variant, size_t count, ...) {
if (count % 2 == 0) {
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "HECTICLIB ERROR: Union to debug str: count is even");
assert(0);
}
va_list args;
va_start(args, count);
char *value = NULL;
bool variant_exists = false;
// Find the matching value for the active variant
while (count--) {
size_t variant = va_arg(args, size_t);
if (variant == (size_t)-1) break; // End marker
if (variant == active_variant) {
variant_exists = true;
value = va_arg(args, char*);
break;
}
// Skip the string value for non-matching variants
va_arg(args, char*);
}
va_end(args);
if (!variant_exists) {
return arena_strdup_fmt__(file, func, line, arena,
"%sunion%s %s %s = {invalid variant %d} %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",
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 %s%p%s",
DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET),
type, name, value, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
}
/* Private function */
char *debug_join_debug_strings_v(CTX_DECLARATION, int count, va_list args) {
@@ -363,7 +410,7 @@ char *struct_to_debug_str__(CTX_DECLARATION, const char *type, const char *name,
char *joined = debug_join_debug_strings_v(CTX(arena), count, args);
va_end(args);
return arena_strdup_fmt__(CTX(arena), "%s %s = {%s} %s%p%s", type, name, joined, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
return arena_strdup_fmt__(CTX(arena), "%sstruct%s %s %s = {%s} %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), type, name, joined, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
}
// ------------
@@ -2099,15 +2146,15 @@ char *template_text_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena
return result;
}
char *template_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateValue *self, PtrSet *visited) {
char *template_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateValue *self, TemplateNodeType type, PtrSet *visited) {
char *result = arena_alloc(arena, MEM_KiB);
STRUCT_TO_DEBUG_STR(arena, result, TemplateValue, name, self, visited, 5,
template_section_value_to_debug_str__(POSITION_INFO, arena, "section", &self->section, visited),
template_interpolate_value_to_debug_str__(POSITION_INFO, arena, "interpolate", &self->interpolate, visited),
template_execute_value_to_debug_str__(POSITION_INFO, arena, "execute", &self->execute, visited),
template_include_value_to_debug_str__(POSITION_INFO, arena, "include", &self->include, visited),
template_text_value_to_debug_str__(POSITION_INFO, arena, "text", &self->text, visited)
UNION_TO_DEBUG_STR(arena, result, TemplateValue, name, self, visited, type, 5,
TEMPLATE_NODE_SECTION, template_section_value_to_debug_str__(POSITION_INFO, arena, "section", &self->section, visited),
TEMPLATE_NODE_INTERPOLATE, template_interpolate_value_to_debug_str__(POSITION_INFO, arena, "interpolate", &self->interpolate, visited),
TEMPLATE_NODE_EXECUTE, template_execute_value_to_debug_str__(POSITION_INFO, arena, "execute", &self->execute, visited),
TEMPLATE_NODE_INCLUDE, template_include_value_to_debug_str__(POSITION_INFO, arena, "include", &self->include, visited),
TEMPLATE_NODE_TEXT, template_text_value_to_debug_str__(POSITION_INFO, arena, "text", &self->text, visited)
);
return result;
}
@@ -2116,7 +2163,7 @@ char *template_node_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, cons
char *result = arena_alloc(arena, MEM_KiB);
STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 4,
string_to_debug_str__(POSITION_INFO, arena, "type", template_node_type_to_string(self->type)),
template_value_to_debug_str__(POSITION_INFO, arena, "value", &self->value, visited),
template_value_to_debug_str__(POSITION_INFO, arena, "value", &self->value, self->type, visited),
template_node_to_debug_str__(POSITION_INFO, arena, "children", self->children, visited),
template_node_to_debug_str__(POSITION_INFO, arena, "next", self->next, visited)
);

View File

@@ -438,6 +438,8 @@ char *ptr_to_debug_str__(const char *file, const char *func, int line, Arena *ar
char *char_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *name, char c);
char *union_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *type, const char *name, const void *ptr, size_t active_variant, size_t count, ...);
char *struct_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *type, const char *name, const void *ptr, int count, ...);
bool debug_ptrset_contains(PtrSet *set, void *ptr);
@@ -457,6 +459,21 @@ bool debug_ptrset_contains(PtrSet *set, void *ptr);
#define CHAR_TO_DEBUG_STR(arena, name, c) \
char_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, c)
#define UNION_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, active_variant, count, ...) do { \
if (!name) \
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)); \
\
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); \
\
debug_ptrset_add__(__FILE__, __func__, __LINE__, arena, visited, ptr, #type, name); \
\
buffer = union_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, ptr, active_variant, count, ##__VA_ARGS__); \
} while (0)
/*
* STRUCT_TO_DEBUG_STR - Converts a structure into a debug string.
*
@@ -487,19 +504,16 @@ 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, "%s %s = {cycle detected} %p", #type, name, ptr); \
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)); \
\
if (!ptr) \
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = NULL", #type, name); \
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sstruct%s %s %s = NULL", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name); \
\
debug_ptrset_add__(__FILE__, __func__, __LINE__, arena, visited, ptr, #type, name); \
\
buffer = struct_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, ptr, count, ##__VA_ARGS__); \
} while (0)
#define UNION_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ...) \
STRUCT_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ##__VA_ARGS__)
// ------------------
// -- Logger Rules --
// ------------------

View File

@@ -20,6 +20,18 @@ struct Struct2 {
Struct *other;
};
typedef enum TestUnionVariant {
TEST_UNION_VARIANT_A,
TEST_UNION_VARIANT_B,
TEST_UNION_VARIANT_C
} TestUnionVariant;
typedef union TestUnion {
int a;
char *b;
float c;
} TestUnion;
char *struct_to_debug_str(Arena *arena, char *name, Struct *self, PtrSet *visited) {
raise_trace("struct_to_debug_str: name: %s, self: %p, visited: %p", name, self, visited);
@@ -48,6 +60,18 @@ char *struct2_to_debug_str(Arena *arena, char *name, Struct2 *self, PtrSet *visi
return result;
}
char *test_union_to_debug_str(Arena *arena, char *name, TestUnion *self, PtrSet *visited, TestUnionVariant active_variant) {
raise_trace("test_union_to_debug_str: name: %s, self: %p, visited: %p, active_variant: %zu", name, self, visited, active_variant);
char *result = arena_alloc(arena, MEM_KiB);
UNION_TO_DEBUG_STR(arena, result, TestUnion, name, self, visited, active_variant, 3,
TEST_UNION_VARIANT_A, INT_TO_DEBUG_STR(arena, "a", self->a),
TEST_UNION_VARIANT_B, STRING_TO_DEBUG_STR(arena, "b", self->b),
TEST_UNION_VARIANT_C, FLOAT_TO_DEBUG_STR(arena, "c", self->c)
);
return result;
}
void test_struct_to_debug_str(Arena *arena) {
// Mock a struct with a cycle
Struct test_struct = {.a = 1, .b = 2, .next = NULL};
@@ -58,7 +82,7 @@ void test_struct_to_debug_str(Arena *arena) {
raise_notice("result: %s", result);
char *check = arena_alloc(arena, MEM_KiB);
sprintf(check, "Struct struct = {a = 1, b = 2, Struct next = {a = 1, b = 2, Struct next = {cycle detected} %p} %p} %p", (void*)&test_struct, (void*)&test_struct, (void*)&test_struct);
sprintf(check, "struct Struct struct = {a = 1, b = 2, struct Struct next = {a = 1, b = 2, struct Struct next = {cycle detected} %p} %p} %p", (void*)&test_struct, (void*)&test_struct, (void*)&test_struct);
raise_notice("check: %s", check);
assert(strcmp(result, check) == 0);
}
@@ -74,7 +98,18 @@ void test_struct2_to_debug_str(Arena *arena) {
char *result = struct2_to_debug_str(arena, "struct2", &test_struct2, visited);
raise_notice("result: %s", result);
char *check = arena_alloc(arena, MEM_KiB);
sprintf(check, "Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", Struct other = {a = 1, b = 2, Struct next = {a = 1, b = 2, Struct next = {cycle detected} %p} %p} %p, Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2);
sprintf(check, "struct Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", struct Struct other = {a = 1, b = 2, struct Struct next = {a = 1, b = 2, struct Struct next = {cycle detected} %p} %p} %p, struct Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2);
raise_notice("check: %s", check);
assert(strcmp(result, check) == 0);
}
void test_test_union_to_debug_str(Arena *arena) {
TestUnion test_union = {.a = 1};
PtrSet *visited = ptrset_init(arena);
char *result = test_union_to_debug_str(arena, "test_union", &test_union, visited, TEST_UNION_VARIANT_A);
raise_notice("result: %s", result);
char *check = arena_alloc(arena, MEM_KiB);
sprintf(check, "union TestUnion test_union = {a = 1} %p", (void*)&test_union);
raise_notice("check: %s", check);
assert(strcmp(result, check) == 0);
}
@@ -92,6 +127,9 @@ int main(void) {
printf("%sTesting struct_to_debug_str2%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
test_struct2_to_debug_str(&arena);
printf("%sTesting test_union_to_debug_str%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
test_test_union_to_debug_str(&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));