diff --git a/README.md b/README.md index 7af2b3e..766d2c1 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ Join our community on: | nut | signed | ✅ | | maxxing | sizeof | ✅ | | salty | static | ✅ | -| gang | struct | ❌ | +| gang | struct | ✅ | | ohio | switch | ✅ | | chungus | union | ❌ | | nonut | unsigned | ✅ | @@ -255,7 +255,6 @@ Current limitations include: - Limited support for complex expressions - Basic error reporting - No support for arrays in user-defined functions -- No support for pointers ## 🔌 VSCode Extension diff --git a/ast.c b/ast.c index 1bce924..71a1869 100644 --- a/ast.c +++ b/ast.c @@ -17,6 +17,8 @@ JumpBuffer *jump_buffer = {0}; HashMap *function_map = NULL; static HashMap *static_variable_map = NULL; +static HashMap *struct_registry = NULL; +static StructDef *struct_registry_list = NULL; ReturnValue current_return_value; Arena arena; @@ -28,16 +30,20 @@ Scope *current_scope; /* Include the symbol table functions */ extern void yyerror(const char *s); extern void cleanup(void); -extern TypeModifiers get_variable_modifiers(const char *name); +extern TypeModifiers get_variable_modifiers(const String name); extern int yylineno; -static int get_function_return_pointer_level(const char *name); -char *evaluate_expression_string(ASTNode *node); +static int get_function_return_pointer_level(const String name); +String evaluate_expression_string(ASTNode *node); /* Helper to build a namespaced static key */ -static void make_static_key(char *out, size_t out_size, - const char *func_name, const char *var_name) +static String make_static_key(const String func_name, const String var_name) { - snprintf(out, out_size, "%s::%s", func_name ? func_name : "__global", var_name); + static char buf[MAX_BUFFER_LEN]; + size_t len = (size_t)snprintf(buf, sizeof(buf), + "%s::%s", + func_name.data ? func_name.data : "__global", + var_name.data); + return (String){ .data = buf, .len = len }; } size_t get_type_size_for_descriptor(VarType type, int pointer_level, TypeModifiers mods) @@ -67,7 +73,7 @@ size_t get_type_size_for_descriptor(VarType type, int pointer_level, TypeModifie return sizeof(unsigned int); return sizeof(int); case VAR_STRING: - return sizeof(char *); + return sizeof(String ); case NONE: default: return 0; @@ -78,7 +84,7 @@ static void write_value_to_address(void *address, VarType type, int pointer_leve static void initialize_variable_from_expr(Variable *var, ASTNode *expr); // Symbol table functions -bool set_variable(const char *name, void *value, VarType type, TypeModifiers mods) +bool set_variable(const String name, void *value, VarType type, TypeModifiers mods) { Variable *var = get_variable(name); if (var != NULL) @@ -113,7 +119,10 @@ bool set_variable(const char *name, void *value, VarType type, TypeModifiers mod var->value.ivalue = *(char *)value; break; case VAR_STRING: - var->value.strvalue = ARENA_STRDUP((char *)value); + var->value.strvalue = ARENA_STRDUP(*(String *)value); + break; + case VAR_STRUCT: + /* struct blob is managed separately via array_data; nothing to copy here */ break; case NONE: break; @@ -123,7 +132,7 @@ bool set_variable(const char *name, void *value, VarType type, TypeModifiers mod return false; // Symbol table is full } -bool set_multi_array_variable(const char *name, int dimensions[], int num_dimensions, TypeModifiers mods, VarType type) +bool set_multi_array_variable(const String name, int dimensions[], int num_dimensions, TypeModifiers mods, VarType type) { Variable *var = get_variable(name); if(var == NULL) @@ -160,7 +169,39 @@ bool set_multi_array_variable(const char *name, int dimensions[], int num_dimens return true; } -ASTNode *create_multi_array_declaration_node(char *name, int dimensions[], int num_dimensions, VarType type) { +ASTNode *create_struct_def_node(String name, StructField *fields) { + ASTNode *node = ARENA_ALLOC_ASTNODE(); + node->type = NODE_STRUCT_DEF; + node->data.struct_def.name = ARENA_STRDUP(name); + node->data.struct_def.fields = fields; /* pointer only — registry owns memory */ + return node; +} + +ASTNode *create_struct_access_node(ASTNode *object, String member) { + ASTNode *node = ARENA_ALLOC_ASTNODE(); + node->type = NODE_STRUCT_ACCESS; + node->data.struct_access.object = object; + node->data.struct_access.member_name = ARENA_STRDUP(member); + /* Propagate struct_name so callers can infer the type */ + if (object && object->type == NODE_IDENTIFIER) { + Variable *var = get_variable(object->data.name); + if (var && var->var_type == VAR_STRUCT && var->struct_name.data) { + node->data.struct_access.struct_name = ARENA_STRDUP(var->struct_name); + /* Set var_type/pointer_level based on the field */ + StructDef *def = get_struct_def(var->struct_name); + if (def) { + StructField *fld = find_struct_field(def, member); + if (fld) { + node->var_type = fld->type; + node->pointer_level = fld->pointer_level; + } + } + } + } + return node; +} + +ASTNode *create_multi_array_declaration_node(String name, int dimensions[], int num_dimensions, VarType type) { ASTNode *node = ARENA_ALLOC_ASTNODE(); if (!node) { yyerror("Memory allocation failed"); @@ -198,7 +239,7 @@ ASTNode *create_multi_array_declaration_node(char *name, int dimensions[], int n return node; } -ASTNode *create_multi_array_access_node(char *name, ASTNode *indices[], int num_indices) { +ASTNode *create_multi_array_access_node(String name, ASTNode *indices[], int num_indices) { ASTNode *node = ARENA_ALLOC_ASTNODE(); if (!node) { yyerror("Memory allocation failed"); @@ -228,7 +269,7 @@ ASTNode *create_multi_array_access_node(char *name, ASTNode *indices[], int num_ } // Function to rename the old create_array_access_node to maintain compatibility -ASTNode *create_array_access_node_single(char *name, ASTNode *index) { +ASTNode *create_array_access_node_single(String name, ASTNode *index) { // Create a wrapper that calls the multi-dimensional version with a single index ASTNode *indices[1] = {index}; return create_multi_array_access_node(name, indices, 1); @@ -269,7 +310,7 @@ size_t calculate_array_offset(Variable *var, int indices[], int num_indices) { for (int i = 0; i < num_indices; i++) { // Check if the index is within bounds if (indices[i] < 0 || indices[i] >= var->array_dimensions.dimensions[i]) { - char error_msg[100]; + char error_msg[MAX_BUFFER_LEN]; sprintf(error_msg, "Array index out of bounds: dimension %d (index=%d, size=%d)", i + 1, indices[i], var->array_dimensions.dimensions[i]); yyerror(error_msg); @@ -289,6 +330,48 @@ size_t calculate_array_offset(Variable *var, int indices[], int num_indices) { return offset; } +void *evaluate_struct_member_address(ASTNode *node) { + if (!node || node->type != NODE_STRUCT_ACCESS) { + yyerror("Invalid struct member access node"); + return NULL; + } + + ASTNode *obj = node->data.struct_access.object; + const String member = node->data.struct_access.member_name; + + Variable *var = NULL; + if (obj->type == NODE_IDENTIFIER) { + var = get_variable(obj->data.name); + } else { + yyerror("Nested struct access not yet supported"); + return NULL; + } + + if (!var) { yyerror("Undefined struct variable"); return NULL; } + if (var->var_type != VAR_STRUCT) { yyerror("Variable is not a struct"); return NULL; } + + StructDef *def = get_struct_def(var->struct_name); + if (!def) { yyerror("Unknown struct type"); return NULL; } + + /* Lazily allocate blob if missing — handles cases where parse-time + pointer was invalidated by hashmap resize during semantic analysis */ + if (!var->value.array_data) { + var->value.array_data = calloc(1, def->total_size); + if (!var->value.array_data) { yyerror("Out of memory for struct blob"); return NULL; } + } + + StructField *fld = find_struct_field(def, member); + if (!fld) { + char msg[MAX_BUFFER_LEN]; + snprintf(msg, sizeof(msg), "Struct '%s' has no member '%s'", + var->struct_name.data, member.data); + yyerror(msg); + return NULL; + } + + return (char *)var->value.array_data + fld->offset; +} + // Evaluate a multi-dimensional array access node void *evaluate_multi_array_access(ASTNode *node) { // Validate the node structure @@ -304,21 +387,28 @@ void *evaluate_multi_array_access(ASTNode *node) { // CRITICAL: Store the array name in a local copy IMMEDIATELY // The array name might be corrupted if we access node->data.array.name after // evaluating indices, due to union memory layout issues - char array_name_buffer[256]; - const char *original_array_name = node->data.array.name; - if (!original_array_name) { + char array_name_buffer[MAX_BUFFER_LEN]; + + const String original_array_name = node->data.array.name; + if (!original_array_name.data) { yyerror("Invalid array access node: missing array name"); exit(EXIT_FAILURE); } - int name_len = (int)strlen(original_array_name); - if (name_len == 0 || name_len >= (int)sizeof(array_name_buffer)) { + + size_t name_len = original_array_name.len; + + if (name_len == 0 || name_len >= sizeof(array_name_buffer)) { yyerror("Invalid array name in array access"); exit(EXIT_FAILURE); } - strncpy(array_name_buffer, original_array_name, sizeof(array_name_buffer) - 1); - array_name_buffer[sizeof(array_name_buffer) - 1] = '\0'; - const char *array_name = array_name_buffer; - + + memcpy(array_name_buffer, original_array_name.data, name_len); + array_name_buffer[name_len] = '\0'; + const String array_name = { + .data = array_name_buffer, + .len = original_array_name.len // ← use the actual name length + }; + // Also store num_dimensions locally before evaluation int num_indices = node->data.array.num_dimensions; if (num_indices <= 0) { @@ -329,14 +419,14 @@ void *evaluate_multi_array_access(ASTNode *node) { // Get the variable using the preserved array name Variable *var = get_variable(array_name); if (var == NULL) { - char error_msg[200]; - snprintf(error_msg, sizeof(error_msg), "Variable '%.100s' is not defined", array_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Variable '%.100s' is not defined", array_name.data); yyerror(error_msg); exit(EXIT_FAILURE); } if (!var->is_array) { - char error_msg[200]; - snprintf(error_msg, sizeof(error_msg), "Variable '%.100s' is not an array", array_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Variable '%.100s' is not an array", array_name.data); yyerror(error_msg); exit(EXIT_FAILURE); } @@ -348,8 +438,8 @@ void *evaluate_multi_array_access(ASTNode *node) { for (int i = 0; i < num_indices; i++) { ASTNode *index_node = node->data.array.indices[i]; if (!index_node) { - char error_msg[200]; - snprintf(error_msg, sizeof(error_msg), "Missing index %d for array '%.100s'", i, array_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Missing index %d for array '%.100s'", i, array_name.data); yyerror(error_msg); exit(EXIT_FAILURE); } @@ -358,10 +448,10 @@ void *evaluate_multi_array_access(ASTNode *node) { indices[i] = evaluate_expression_int(index_node); // After evaluating each index, verify the array name hasn't been corrupted - if (node->data.array.name != original_array_name) { + if (node->data.array.name.data != original_array_name.data) { // Restore the original array name if it was modified // Note: We need to cast away const because the field is not const - node->data.array.name = (char*)original_array_name; + node->data.array.name.data = (char*)original_array_name.data; } } @@ -388,17 +478,17 @@ void *evaluate_multi_array_access(ASTNode *node) { } } -bool set_int_variable(const char *name, int value, TypeModifiers mods) +bool set_int_variable(const String name, int value, TypeModifiers mods) { return set_variable(name, &value, VAR_INT, mods); } -bool set_char_variable(const char *name, int value, TypeModifiers mods) +bool set_char_variable(const String name, int value, TypeModifiers mods) { return set_variable(name, &value, VAR_CHAR, mods); } -bool set_array_variable(char *name, int length, TypeModifiers mods, VarType type) +bool set_array_variable(String name, int length, TypeModifiers mods, VarType type) { // search for an existing variable Variable *var = get_variable(name); @@ -454,27 +544,27 @@ bool set_array_variable(char *name, int length, TypeModifiers mods, VarType type return false; // no space } -bool set_short_variable(const char *name, short value, TypeModifiers mods) +bool set_short_variable(const String name, short value, TypeModifiers mods) { return set_variable(name, &value, VAR_SHORT, mods); } -bool set_string_variable(const char *name, const char *value, TypeModifiers mods) +bool set_string_variable(const String name, String value, TypeModifiers mods) { - return set_variable(name, (void *)value, VAR_STRING, mods); + return set_variable(name, &value, VAR_STRING, mods); } -bool set_float_variable(const char *name, float value, TypeModifiers mods) +bool set_float_variable(const String name, float value, TypeModifiers mods) { return set_variable(name, &value, VAR_FLOAT, mods); } -bool set_double_variable(const char *name, double value, TypeModifiers mods) +bool set_double_variable(const String name, double value, TypeModifiers mods) { return set_variable(name, &value, VAR_DOUBLE, mods); } -bool set_bool_variable(const char *name, bool value, TypeModifiers mods) +bool set_bool_variable(const String name, bool value, TypeModifiers mods) { return set_variable(name, &value, VAR_BOOL, mods); } @@ -500,7 +590,7 @@ TypeModifiers get_current_modifiers(void) /* Function implementations */ -bool check_and_mark_identifier(ASTNode *node, const char *contextErrorMessage) +bool check_and_mark_identifier(ASTNode *node, const String contextErrorMessage) { if (!node->already_checked) { @@ -515,7 +605,7 @@ bool check_and_mark_identifier(ASTNode *node, const char *contextErrorMessage) if (!node->is_valid_symbol) { yylineno = yylineno - 2; - yyerror(contextErrorMessage); + yyerror(contextErrorMessage.data); } } @@ -595,7 +685,7 @@ ASTNode *create_int_node(int value) return node; } -ASTNode *create_array_declaration_node(char *name, int length, VarType var_type) +ASTNode *create_array_declaration_node(String name, int length, VarType var_type) { ASTNode *node = ARENA_ALLOC_ASTNODE(); if (!node) @@ -609,7 +699,7 @@ ASTNode *create_array_declaration_node(char *name, int length, VarType var_type) return node; } -ASTNode *create_array_access_node(char *name, ASTNode *index) +ASTNode *create_array_access_node(String name, ASTNode *index) { ASTNode *node = ARENA_ALLOC_ASTNODE(); if (!node) @@ -666,12 +756,12 @@ ASTNode *create_boolean_node(bool value) return node; } -ASTNode *create_identifier_node(char *name) +ASTNode *create_identifier_node(String name) { return create_identifier_node_ex(name, 0); } -ASTNode *create_identifier_node_ex(char *name, int pointer_level) +ASTNode *create_identifier_node_ex(String name, int pointer_level) { ASTNode *node = create_node(NODE_IDENTIFIER, current_var_type, current_modifiers); node->pointer_level = pointer_level; @@ -679,7 +769,7 @@ ASTNode *create_identifier_node_ex(char *name, int pointer_level) return node; } -ASTNode *create_assignment_node(char *name, ASTNode *expr) +ASTNode *create_assignment_node(String name, ASTNode *expr) { return create_assignment_target_node(create_identifier_node(name), expr); } @@ -692,12 +782,12 @@ ASTNode *create_assignment_target_node(ASTNode *target, ASTNode *expr) return node; } -ASTNode *create_declaration_node(char *name, ASTNode *expr) +ASTNode *create_declaration_node(String name, ASTNode *expr) { return create_declaration_node_ex(name, expr, 0); } -ASTNode *create_declaration_node_ex(char *name, ASTNode *expr, int pointer_level) +ASTNode *create_declaration_node_ex(String name, ASTNode *expr, int pointer_level) { ASTNode *node = create_node(NODE_DECLARATION, current_var_type, get_current_modifiers()); node->pointer_level = pointer_level; @@ -740,7 +830,7 @@ ASTNode *create_do_while_statement_node(ASTNode *cond, ASTNode *body) return node; } -ASTNode *create_function_call_node(char *func_name, ArgumentList *args) +ASTNode *create_function_call_node(String func_name, ArgumentList *args) { ASTNode *node = create_node(NODE_FUNC_CALL, NONE, current_modifiers); SET_DATA_FUNC_CALL(node, func_name, args); @@ -762,12 +852,12 @@ ASTNode *create_sizeof_node(ASTNode *expr) } // @param promotion: 0 for no promotion, 1 for promotion to double 2 for promotion to float -void *handle_identifier(ASTNode *node, const char *contextErrorMessage, int promote) +void *handle_identifier(ASTNode *node, const String contextErrorMessage, int promote) { if (!check_and_mark_identifier(node, contextErrorMessage)) ragequit(1); - char *name = node->data.name; + String name = node->data.name; Variable *var = get_variable(name); if (var != NULL) { @@ -917,6 +1007,15 @@ int get_expression_pointer_level(ASTNode *node) default: return 0; } + case NODE_STRUCT_ACCESS: { + ASTNode *obj = node->data.struct_access.object; + Variable *var = (obj->type == NODE_IDENTIFIER) + ? get_variable(obj->data.name) : NULL; + if (!var || var->var_type != VAR_STRUCT) return 0; + StructDef *def = get_struct_def(var->struct_name); + StructField *fld = def ? find_struct_field(def, node->data.struct_access.member_name) : NULL; + return fld ? fld->pointer_level : 0; + } default: return node->pointer_level; } @@ -948,8 +1047,8 @@ VarType get_expression_type(ASTNode *node) { // First, get the array's base type from symbol table // Store the array name locally to prevent modification - const char *array_name = node->data.array.name; - if (!array_name) { + const String array_name = node->data.array.name; + if (!array_name.data) { yyerror("Invalid array access: missing array name"); return NONE; } @@ -967,7 +1066,7 @@ VarType get_expression_type(ASTNode *node) case NODE_IDENTIFIER: { // Look up the variable type in the symbol table - const char *array_name = node->data.name; + const String array_name = node->data.name; Variable *var = get_variable(array_name); if (var != NULL) { @@ -1016,7 +1115,7 @@ VarType get_expression_type(ASTNode *node) case NODE_FUNC_CALL: { // Look up the function in the symbol table - const char *func_name = node->data.func_call.function_name; + const String func_name = node->data.func_call.function_name; Function *func = get_function(func_name); if (func != NULL) { @@ -1025,6 +1124,16 @@ VarType get_expression_type(ASTNode *node) yyerror("Undefined function in get_expression_type"); return NONE; } + case NODE_STRUCT_ACCESS: { + ASTNode *obj = node->data.struct_access.object; + Variable *var = (obj->type == NODE_IDENTIFIER) + ? get_variable(obj->data.name) : NULL; + if (!var || var->var_type != VAR_STRUCT) return NONE; + StructDef *def = get_struct_def(var->struct_name); + if (!def) return NONE; + StructField *fld = find_struct_field(def, node->data.struct_access.member_name); + return fld ? fld->type : NONE; + } default: yyerror("Unknown node type in get_expression_type"); return NONE; @@ -1363,6 +1472,8 @@ void *evaluate_lvalue_address(ASTNode *node) if (node->data.unary.op == OP_DEREFERENCE) return (void *)evaluate_expression_pointer(node->data.unary.operand); break; + case NODE_STRUCT_ACCESS: + return evaluate_struct_member_address(node); default: break; } @@ -1481,7 +1592,7 @@ static void write_value_to_address(void *address, VarType type, int pointer_leve *(int *)address = evaluate_expression_int(expr); break; case VAR_STRING: - *(char **)address = evaluate_expression_string(expr); + *(String *)address = evaluate_expression_string(expr); break; case NONE: default: @@ -1741,7 +1852,11 @@ float evaluate_expression_float(ASTNode *node) yyerror("Cannot use pointer in float context"); return 0.0f; } - return *(float *)handle_identifier(node, "Undefined variable", 2); + String error = { + .data = "Undefined variable", + .len = sizeof("Undefined variable") - 1 + }; + return *(float *)handle_identifier(node, error, 2); } case NODE_OPERATION: { @@ -1796,6 +1911,26 @@ float evaluate_expression_float(ASTNode *node) } return 0.0f; } + case NODE_STRUCT_ACCESS: { + void *addr = evaluate_struct_member_address(node); + if (!addr) return 0; + /* Read based on the field's type */ + ASTNode *obj = node->data.struct_access.object; + Variable *var = get_variable(obj->data.name); + StructDef *def = var ? get_struct_def(var->struct_name) : NULL; + StructField *fld = def ? find_struct_field(def, node->data.struct_access.member_name) : NULL; + if (!fld) return 0; + if (fld->pointer_level > 0) return (float)*(uintptr_t *)addr; + switch (fld->type) { + case VAR_INT: return (float)*(int *)addr; + case VAR_SHORT: return (float)*(short *)addr; + case VAR_BOOL: return (float)*(bool *)addr; + case VAR_CHAR: return (float)*(char *)addr; + case VAR_FLOAT: return *(float *)addr; + case VAR_DOUBLE: return (float)*(double *)addr; + default: return 0; + } + } default: yyerror("Invalid float expression"); return 0.0f; @@ -1829,7 +1964,11 @@ double evaluate_expression_double(ASTNode *node) yyerror("Cannot use pointer in double context"); return 0.0; } - return *(double *)handle_identifier(node, "Undefined variable", 1); + String error = { + .data = "Undefined variable", + .len = sizeof("Undefined variable") - 1 + }; + return *(double *)handle_identifier(node, error, 1); } case NODE_OPERATION: { @@ -1884,12 +2023,32 @@ double evaluate_expression_double(ASTNode *node) } return 0.0L; } + case NODE_STRUCT_ACCESS: { + void *addr = evaluate_struct_member_address(node); + if (!addr) return 0; + /* Read based on the field's type */ + ASTNode *obj = node->data.struct_access.object; + Variable *var = get_variable(obj->data.name); + StructDef *def = var ? get_struct_def(var->struct_name) : NULL; + StructField *fld = def ? find_struct_field(def, node->data.struct_access.member_name) : NULL; + if (!fld) return 0; + if (fld->pointer_level > 0) return (double)*(uintptr_t *)addr; + switch (fld->type) { + case VAR_INT: return (double)*(int *)addr; + case VAR_SHORT: return (double)*(short *)addr; + case VAR_BOOL: return (double)*(bool *)addr; + case VAR_CHAR: return (double)*(char *)addr; + case VAR_FLOAT: return (double)*(float *)addr; + case VAR_DOUBLE: return *(double *)addr; + default: return 0; + } + } default: yyerror("Invalid double expression"); return 0.0L; } } -size_t get_type_size(char *name) +size_t get_type_size(String name) { Variable *var = get_variable(name); if (var != NULL) @@ -1940,34 +2099,38 @@ size_t handle_sizeof(ASTNode *node) } } -char *evaluate_expression_string(ASTNode *node) +String evaluate_expression_string(ASTNode *node) { if (!node) - return NULL; + return (String){ .data = NULL, .len = 0 }; switch (node->type) { case NODE_STRING_LITERAL: case NODE_STRING: - return safe_strdup(node->data.strvalue); + return safe_strdup(&node->data.strvalue); case NODE_IDENTIFIER: { - return safe_strdup((char *)handle_identifier(node, "Undefined variable", 3)); + String error = { + .data = "Undefined variable", + .len = sizeof("Undefined variable") - 1 + }; + return safe_strdup((String *)handle_identifier(node, error, 3)); } case NODE_FUNC_CALL: { - char *res = (char *)handle_function_call(node); + String *res = (String *)handle_function_call(node); if (res != NULL) { - char *result = safe_strdup(res); + String result = safe_strdup(res); SAFE_FREE(res); return result; } - return NULL; + return (String){ .data = NULL, .len = 0 }; } default: yyerror("Invalid string expression"); - return NULL; + return (String){ .data = NULL, .len = 0 }; } } @@ -2002,7 +2165,11 @@ short evaluate_expression_short(ASTNode *node) yyerror("Cannot use pointer in integer context"); return 0; } - return *(short *)handle_identifier(node, "Undefined variable", 0); + String error = { + .data = "Undefined variable", + .len = sizeof("Undefined variable") - 1 + }; + return *(short *)handle_identifier(node, error, 0); } case NODE_OPERATION: { @@ -2081,6 +2248,26 @@ short evaluate_expression_short(ASTNode *node) } return 0; } + case NODE_STRUCT_ACCESS: { + void *addr = evaluate_struct_member_address(node); + if (!addr) return 0; + /* Read based on the field's type */ + ASTNode *obj = node->data.struct_access.object; + Variable *var = get_variable(obj->data.name); + StructDef *def = var ? get_struct_def(var->struct_name) : NULL; + StructField *fld = def ? find_struct_field(def, node->data.struct_access.member_name) : NULL; + if (!fld) return 0; + if (fld->pointer_level > 0) return (short)*(uintptr_t *)addr; + switch (fld->type) { + case VAR_INT: return (short)*(int *)addr; + case VAR_SHORT: return *(short *)addr; + case VAR_BOOL: return (short)*(bool *)addr; + case VAR_CHAR: return (short)*(char *)addr; + case VAR_FLOAT: return (short)*(float *)addr; + case VAR_DOUBLE: return (short)*(double *)addr; + default: return 0; + } + } default: yyerror("Invalid short expression"); return 0; @@ -2118,7 +2305,11 @@ int evaluate_expression_int(ASTNode *node) yyerror("Cannot use pointer in integer context"); return 0; } - return *(int *)handle_identifier(node, "Undefined variable", 0); + String error = { + .data = "Undefined variable", + .len = sizeof("Undefined variable") - 1 + }; + return *(int *)handle_identifier(node, error, 0); } case NODE_OPERATION: { @@ -2194,6 +2385,26 @@ int evaluate_expression_int(ASTNode *node) } return 0; } + case NODE_STRUCT_ACCESS: { + void *addr = evaluate_struct_member_address(node); + if (!addr) return 0; + /* Read based on the field's type */ + ASTNode *obj = node->data.struct_access.object; + Variable *var = get_variable(obj->data.name); + StructDef *def = var ? get_struct_def(var->struct_name) : NULL; + StructField *fld = def ? find_struct_field(def, node->data.struct_access.member_name) : NULL; + if (!fld) return 0; + if (fld->pointer_level > 0) return (int)*(uintptr_t *)addr; + switch (fld->type) { + case VAR_INT: return *(int *)addr; + case VAR_SHORT: return (int)*(short *)addr; + case VAR_BOOL: return (int)*(bool *)addr; + case VAR_CHAR: return (int)*(char *)addr; + case VAR_FLOAT: return (int)*(float *)addr; + case VAR_DOUBLE: return (int)*(double *)addr; + default: return 0; + } + } default: yyerror("Invalid integer expression"); return 0; @@ -2241,8 +2452,11 @@ void *handle_function_call(ASTNode *node) *(short *)return_value = current_return_value.value.svalue; break; case VAR_STRING: - return_value = SAFE_MALLOC(char *); - *(char **)return_value = safe_strdup(current_return_value.value.strvalue); + return_value = SAFE_MALLOC(String *); + *(String *)return_value = safe_strdup(¤t_return_value.value.strvalue); + break; + case VAR_STRUCT: + /* struct return not yet supported; fall through to NULL */ break; case NONE: return NULL; @@ -2275,7 +2489,11 @@ bool evaluate_expression_bool(ASTNode *node) if (get_expression_pointer_level(node) > 0) { return evaluate_expression_pointer(node) != (uintptr_t)0; } - return *(bool *)handle_identifier(node, "Undefined variable", 0); + String error = { + .data = "Undefined variable", + .len = sizeof("Undefined variable") - 1 + }; + return *(bool *)handle_identifier(node, error, 0); } case NODE_OPERATION: { @@ -2360,6 +2578,26 @@ bool evaluate_expression_bool(ASTNode *node) } return 0; } + case NODE_STRUCT_ACCESS: { + void *addr = evaluate_struct_member_address(node); + if (!addr) return 0; + /* Read based on the field's type */ + ASTNode *obj = node->data.struct_access.object; + Variable *var = get_variable(obj->data.name); + StructDef *def = var ? get_struct_def(var->struct_name) : NULL; + StructField *fld = def ? find_struct_field(def, node->data.struct_access.member_name) : NULL; + if (!fld) return 0; + if (fld->pointer_level > 0) return (bool)*(uintptr_t *)addr; + switch (fld->type) { + case VAR_INT: return (bool)*(int *)addr; + case VAR_SHORT: return (bool)*(short *)addr; + case VAR_BOOL: return *(bool *)addr; + case VAR_CHAR: return (bool)*(char *)addr; + case VAR_FLOAT: return (bool)*(float *)addr; + case VAR_DOUBLE: return (bool)*(double *)addr; + default: return 0; + } + } default: yyerror("Invalid boolean expression"); return 0; @@ -2450,7 +2688,7 @@ ASTNode *create_statement_list(ASTNode *statement, ASTNode *existing_list) } } -bool is_const_variable(const char *name) +bool is_const_variable(const String name) { Variable *var = get_variable(name); if (var != NULL) @@ -2460,7 +2698,7 @@ bool is_const_variable(const char *name) return false; } -void check_const_assignment(const char *name) +void check_const_assignment(const String name) { if (is_const_variable(name)) { @@ -2490,7 +2728,11 @@ bool is_expression(ASTNode *node, VarType type) } case NODE_IDENTIFIER: { - if (!check_and_mark_identifier(node, "Undefined variable in type check")) + String error = { + .data = "Undefined variable in type check", + .len = sizeof("Undefined variable in type check") - 1 + }; + if (!check_and_mark_identifier(node, error)) ragequit(1); Variable *var = get_variable(node->data.name); if (var != NULL) @@ -2509,26 +2751,36 @@ bool is_expression(ASTNode *node, VarType type) { return get_function_return_type(node->data.func_call.function_name) == type; } + case NODE_STRUCT_ACCESS: { + ASTNode *obj = node->data.struct_access.object; + Variable *var = (obj && obj->type == NODE_IDENTIFIER) + ? get_variable(obj->data.name) : NULL; + StructDef *def = var ? get_struct_def(var->struct_name) : NULL; + StructField *fld = def + ? find_struct_field(def, node->data.struct_access.member_name) + : NULL; + return fld ? (fld->type == type) : false; + } default: return node->type == VART_TO_NODET(type); } } -Function *get_function(const char *name) +Function *get_function(const String name) { - if (!function_map || !name) { + if (!function_map || !name.data) { return NULL; } - size_t name_len = strlen(name); - Function **func_ptr = (Function **)hm_get(function_map, name, name_len); + size_t name_len = name.len; + Function **func_ptr = (Function **)hm_get(function_map, name.data, name_len); if (func_ptr) { return *func_ptr; } return NULL; } -VarType get_function_return_type(const char *name) +VarType get_function_return_type(const String name) { Function *func = get_function(name); if (func != NULL) @@ -2539,7 +2791,7 @@ VarType get_function_return_type(const char *name) return NONE; } -static int get_function_return_pointer_level(const char *name) +static int get_function_return_pointer_level(const String name) { Function *func = get_function(name); if (func != NULL) @@ -2584,7 +2836,7 @@ void execute_assignment(ASTNode *node) if (target->type == NODE_IDENTIFIER) { - char *name = target->data.name; + String name = target->data.name; check_const_assignment(name); Variable *var = get_variable(name); if (!var) @@ -2596,6 +2848,22 @@ void execute_assignment(ASTNode *node) target_pointer_level = var->pointer_level; mods = var->modifiers; } + else if (target->type == NODE_STRUCT_ACCESS) + { + /* Resolve type from the actual field at runtime */ + ASTNode *obj = target->data.struct_access.object; + if (obj && obj->type == NODE_IDENTIFIER) { + Variable *var = get_variable(obj->data.name); + if (var && var->var_type == VAR_STRUCT) { + StructDef *def = get_struct_def(var->struct_name); + StructField *fld = def ? find_struct_field(def, target->data.struct_access.member_name) : NULL; + if (fld) { + target_type = fld->type; + target_pointer_level = fld->pointer_level; + } + } + } + } void *address = evaluate_lvalue_address(target); write_value_to_address(address, target_type, target_pointer_level, value_node, mods); @@ -2609,7 +2877,7 @@ void execute_statement(ASTNode *node) { case NODE_DECLARATION: { - char *name = node->data.op.left->data.name; + String name = node->data.op.left->data.name; Variable *var = variable_new(name); var->var_type = node->var_type; var->pointer_level = node->pointer_level; @@ -2617,15 +2885,14 @@ void execute_statement(ASTNode *node) /* Check if it's static and already initialized */ if (node->modifiers.is_static) { - const char *func_name = NULL; + String func_name = {NULL, 0}; Scope *s = current_scope; while (s) { - if (s->is_function_scope) { func_name = s->function_name; break; } + if (s->is_function_scope) { func_name= s->function_name; break; } s = s->parent; } - char static_key[512]; - make_static_key(static_key, sizeof(static_key), func_name, name); - Variable *existing = hm_get(static_variable_map, static_key, strlen(static_key)); + String static_key = make_static_key(func_name, name); + Variable *existing = hm_get(static_variable_map, static_key.data, MAX_BUFFER_LEN); if (existing) { SAFE_FREE(var); break; /* Already initialized — skip assignment entirely */ @@ -2649,9 +2916,9 @@ void execute_statement(ASTNode *node) break; } case NODE_ARRAY_ACCESS: - if (node->data.array.name && node->data.array.index) + if (node->data.array.name.data && node->data.array.index) { - if (!(node->data.array.name)) + if (!(node->data.array.name.data)) { yyerror("Failed to create array"); } @@ -2700,12 +2967,20 @@ void execute_statement(ASTNode *node) ASTNode *expr = node->data.op.left; if (expr->type == NODE_STRING_LITERAL) { - yapping("%s\n", expr->data.name); + String s = { + .data = "%s\n", + .len = sizeof("%s\n") - 1 + }; + yapping(s, expr->data.name); } else { + String s = { + .data = "%d\n", + .len = sizeof("%d\n") - 1 + }; int value = evaluate_expression(expr); - yapping("%d\n", value); + yapping(s, value); } break; } @@ -2714,12 +2989,20 @@ void execute_statement(ASTNode *node) ASTNode *expr = node->data.op.left; if (expr->type == NODE_STRING_LITERAL) { - baka("%s\n", expr->data.name); + String s = { + .data = "%s\n", + .len = sizeof("%s\n") - 1 + }; + baka(s, expr->data.name); } else { + String s = { + .data = "%d\n", + .len = sizeof("%d\n") - 1 + }; int value = evaluate_expression(expr); - baka("%d\n", value); + baka(s, value); } break; } @@ -2868,7 +3151,7 @@ ASTNode *create_if_statement_node(ASTNode *condition, ASTNode *then_branch, ASTN return node; } -ASTNode *create_string_literal_node(char *string) +ASTNode *create_string_literal_node(String string) { ASTNode *node = ARENA_ALLOC_ASTNODE(); node->type = NODE_STRING_LITERAL; @@ -2939,8 +3222,13 @@ ASTNode *create_default_node(VarType var_type) return create_char_node('\0'); case VAR_BOOL: return create_boolean_node(0); - case VAR_STRING: - return create_string_literal_node("\0"); + case VAR_STRING: { + String s = { + .data = "\0", + .len = sizeof("\0") - 1 + }; + return create_string_literal_node(s); + } default: yyerror("Unsupported type for default node"); exit(1); @@ -3014,7 +3302,35 @@ void free_expression_list(ExpressionList *list) SAFE_FREE(list); } -void populate_multi_array_variable(char *name, ExpressionList *list, int dimensions[], int num_dimensions) { +void populate_struct_variable(const String name, ExpressionList *list) { + Variable *var = get_variable(name); + if (!var || var->var_type != VAR_STRUCT) return; + StructDef *def = get_struct_def(var->struct_name); + if (!def) return; + + StructField *fld = def->fields; + ExpressionList *cur = list; + while (fld && cur) { + void *addr = (char *)var->value.array_data + fld->offset; + if (fld->pointer_level > 0) { + *(uintptr_t *)addr = evaluate_expression_pointer(cur->expr); + } else { + switch (fld->type) { + case VAR_INT: *(int *)addr = evaluate_expression_int(cur->expr); break; + case VAR_SHORT: *(short *)addr = evaluate_expression_short(cur->expr); break; + case VAR_FLOAT: *(float *)addr = evaluate_expression_float(cur->expr); break; + case VAR_DOUBLE: *(double *)addr = evaluate_expression_double(cur->expr); break; + case VAR_BOOL: *(bool *)addr = evaluate_expression_bool(cur->expr); break; + case VAR_CHAR: *(char *)addr = (char)evaluate_expression_int(cur->expr); break; + default: break; + } + } + fld = fld->next; + cur = cur->next; + } +} + +void populate_multi_array_variable(String name, ExpressionList *list, int dimensions[], int num_dimensions) { Variable *var = get_variable(name); if (var == NULL || !var->is_array) { yyerror("Cannot initialize: not an array"); @@ -3109,26 +3425,25 @@ Scope *create_scope(Scope *parent) return scope; } -Variable *get_variable(const char *name) +Variable *get_variable(const String name) { /* Check static store first */ if (static_variable_map) { - const char *func_name = NULL; + String func_name = {NULL, 0}; Scope *s = current_scope; while (s) { if (s->is_function_scope) { func_name = s->function_name; break; } s = s->parent; } - char static_key[512]; - make_static_key(static_key, sizeof(static_key), func_name, name); - Variable *var = hm_get(static_variable_map, static_key, strlen(static_key)); + String static_key = make_static_key(func_name, name); + Variable *var = hm_get(static_variable_map, static_key.data, static_key.len); if (var) { return var; } } Scope *scope = current_scope; while (scope) { - Variable *var = hm_get(scope->variables, name, strlen(name)); + Variable *var = hm_get(scope->variables, name.data, name.len); if (var) { return var; @@ -3167,7 +3482,7 @@ void enter_scope() { current_scope = create_scope(current_scope); } -Variable *variable_new(char *name) +Variable *variable_new(String name) { Variable *var = SAFE_MALLOC(Variable); if (!var) @@ -3181,7 +3496,7 @@ Variable *variable_new(char *name) return var; } -void add_variable_to_scope(const char *name, Variable *var) +void add_variable_to_scope(const String name, Variable *var) { if (!current_scope) { yyerror("No scope to add variable to"); @@ -3194,33 +3509,31 @@ void add_variable_to_scope(const char *name, Variable *var) static_variable_map = hm_new(); /* Find nearest function scope to namespace the key */ - const char *func_name = NULL; + String func_name = {NULL, 0}; Scope *s = current_scope; while (s) { if (s->is_function_scope) { func_name = s->function_name; break; } s = s->parent; } - char static_key[512]; - make_static_key(static_key, sizeof(static_key), func_name, name); - size_t key_len = strlen(static_key); - - Variable *existing = hm_get(static_variable_map, static_key, key_len); + String static_key = make_static_key(func_name, name); + Variable *existing = hm_get(static_variable_map, static_key.data, static_key.len); if (!existing) - hm_put(static_variable_map, static_key, key_len, var, sizeof(Variable)); + hm_put(static_variable_map, static_key.data, static_key.len, var, sizeof(Variable)); + return; /* <-- always return here, never fall through to normal scope */ } /* Normal (non-static) path — unchanged from your original */ - size_t name_len = strlen(name); - Variable *existing = hm_get(current_scope->variables, name, name_len); + size_t name_len = name.len; + Variable *existing = hm_get(current_scope->variables, name.data, name_len); if (existing) { yyerror("Variable already exists in current scope"); SAFE_FREE(var); exit(1); } - hm_put(current_scope->variables, name, name_len, var, sizeof(Variable)); + hm_put(current_scope->variables, name.data, name_len, var, sizeof(Variable)); } ASTNode *create_return_node(ASTNode *expr) @@ -3236,7 +3549,7 @@ ASTNode *create_return_node(ASTNode *expr) return node; } -Function *create_function_ex(char *name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body) +Function *create_function_ex(String name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body) { /* Check if function already exists - if so, just return it (parse + execute causes double creation) */ Function *existing = get_function(name); @@ -3251,7 +3564,7 @@ Function *create_function_ex(char *name, VarType return_type, int return_pointer return NULL; } - func->name = safe_strdup(name); + func->name = safe_strdup(&name); func->return_type = return_type; func->return_pointer_level = return_pointer_level; func->parameters = params; @@ -3261,18 +3574,18 @@ Function *create_function_ex(char *name, VarType return_type, int return_pointer if (!function_map) { function_map = hm_new(); } - size_t name_len = strlen(name); - hm_put(function_map, name, name_len, &func, sizeof(Function *)); + size_t name_len = name.len; + hm_put(function_map, name.data, name_len, &func, sizeof(Function *)); return func; } -Function *create_function(char *name, VarType return_type, Parameter *params, ASTNode *body) +Function *create_function(String name, VarType return_type, Parameter *params, ASTNode *body) { return create_function_ex(name, return_type, 0, params, body); } -void execute_function_call(const char *name, ArgumentList *args) +void execute_function_call(const String name, ArgumentList *args) { /* Use optimized O(1) hash map lookup instead of O(n) linked list search */ Function *func = get_function(name); @@ -3358,7 +3671,7 @@ void handle_return_statement(ASTNode *expr) } } -Parameter *create_parameter_ex(char *name, VarType type, int pointer_level, Parameter *next, TypeModifiers mods) +Parameter *create_parameter_ex(String name, VarType type, int pointer_level, Parameter *next, TypeModifiers mods) { Parameter *param = ARENA_ALLOC(Parameter); if (!param) @@ -3376,12 +3689,12 @@ Parameter *create_parameter_ex(char *name, VarType type, int pointer_level, Para return param; } -Parameter *create_parameter(char *name, VarType type, Parameter *next, TypeModifiers mods) +Parameter *create_parameter(String name, VarType type, Parameter *next, TypeModifiers mods) { return create_parameter_ex(name, type, 0, next, mods); } -ASTNode *create_function_def_node_ex(char *name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body) +ASTNode *create_function_def_node_ex(String name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body) { ASTNode *node = ARENA_ALLOC_ASTNODE(); if (!node) @@ -3403,7 +3716,7 @@ ASTNode *create_function_def_node_ex(char *name, VarType return_type, int return return node; } -ASTNode *create_function_def_node(char *name, VarType return_type, Parameter *params, ASTNode *body) +ASTNode *create_function_def_node(String name, VarType return_type, Parameter *params, ASTNode *body) { return create_function_def_node_ex(name, return_type, 0, params, body); } @@ -3505,6 +3818,9 @@ void enter_function_scope(Function *func, ArgumentList *args) case VAR_STRING: yyerror("String parameters are not supported"); return; + case VAR_STRUCT: + yyerror("Struct parameters are not yet supported"); + return; case NONE: break; } @@ -3570,6 +3886,9 @@ void enter_function_scope(Function *func, ArgumentList *args) case VAR_STRING: yyerror("String parameters are not supported"); return; + case VAR_STRUCT: + yyerror("Struct parameters are not yet supported"); + return; case NONE: break; } @@ -3577,3 +3896,70 @@ void enter_function_scope(Function *func, ArgumentList *args) } reverse_parameter_list(&func->parameters); } + +void register_struct_def(StructDef *def) { + if (!struct_registry) struct_registry = hm_new(); + size_t len = def->name.len; + hm_put(struct_registry, def->name.data, len, def, sizeof(StructDef)); + def->next_def = struct_registry_list; + struct_registry_list = def; +} + +StructDef *get_struct_def(const String name) { + if (!struct_registry || !name.data) return NULL; + return (StructDef *)hm_get(struct_registry, name.data, name.len); +} + +void free_struct_registry(void) { + if (struct_registry) { + hm_free_shallow(struct_registry); + struct_registry = NULL; + } + StructDef *def = struct_registry_list; + while (def) { + StructField *f = def->fields; + while (f) { + StructField *nxt = f->next; + SAFE_FREE(f->name); + SAFE_FREE(f); + f = nxt; + } + SAFE_FREE(def->name); + StructDef *nxt = def->next_def; + SAFE_FREE(def); + def = nxt; + } + struct_registry_list = NULL; +} + +StructField *find_struct_field(StructDef *def, const String name) { + if (!def || !name.data) return NULL; + StructField *f = def->fields; + while (f) { + if (strcmp(f->name.data, name.data) == 0) return f; + f = f->next; + } + return NULL; +} + +/* Walk the field list, assign natural-alignment offsets, return total size. + We use simple sequential layout (no padding) to keep it straightforward; + add alignment rounding here if needed later. */ +size_t compute_struct_layout(StructField *fields) { + size_t off = 0; + StructField *f = fields; + while (f) { + f->offset = off; + size_t fsz; + if (f->pointer_level > 0) { + fsz = sizeof(uintptr_t); + } else { + TypeModifiers m = {0}; + fsz = get_type_size_for_descriptor(f->type, 0, m); + if (fsz == 0) fsz = sizeof(int); + } + off += fsz; + f = f->next; + } + return off; /* total bytes */ +} diff --git a/ast.h b/ast.h index c4b2d3d..2b14570 100644 --- a/ast.h +++ b/ast.h @@ -6,6 +6,7 @@ #include "lib/hm.h" #include "lib/arena.h" #include "lib/mem.h" +#include "lib/string_value.h" #include #include #include @@ -25,7 +26,7 @@ typedef struct CaseNode CaseNode; typedef struct { - char *name; + String name; int pointer_level; } Declarator; @@ -73,15 +74,33 @@ typedef enum VAR_BOOL, VAR_CHAR, VAR_STRING, + VAR_STRUCT, NONE, } VarType; +/* A single field inside a struct definition */ +typedef struct StructField { + String name; + VarType type; + int pointer_level; + size_t offset; /* byte offset within the struct blob */ + struct StructField *next; +} StructField; + +/* A struct definition (the "template") */ +typedef struct StructDef { + String name; + StructField *fields; + size_t total_size; /* total byte size of one instance */ + struct StructDef *next_def; +} StructDef; + /* AST helper functions */ ASTNode* arena_alloc_astnode(void); typedef struct Parameter { - char *name; + String name; VarType type; int pointer_level; TypeModifiers modifiers; @@ -90,7 +109,7 @@ typedef struct Parameter typedef struct Function { - char *name; + String name; VarType return_type; int return_pointer_level; Parameter *parameters; @@ -107,7 +126,7 @@ typedef struct double dvalue; bool bvalue; short svalue; - char *strvalue; + String strvalue; uintptr_t pvalue; } value; VarType type; @@ -117,7 +136,7 @@ typedef struct /* Symbol table structure */ typedef struct { - char *name; + String name; union { int ivalue; @@ -126,7 +145,7 @@ typedef struct float fvalue; double dvalue; void *array_data; - char *strvalue; + String strvalue; uintptr_t pvalue; } value; TypeModifiers modifiers; @@ -135,6 +154,7 @@ typedef struct bool is_array; int array_length; // lets keep it for now for backword compatibility ArrayDimensions array_dimensions; + String struct_name; /* non-NULL when var_type == VAR_STRUCT */ } Variable; typedef union @@ -147,7 +167,7 @@ typedef union bool bvalue; float fvalue; double dvalue; - char *strvalue; + String strvalue; uintptr_t pvalue; }; int pointer_level; @@ -211,11 +231,13 @@ typedef enum NODE_FUNC_CALL, NODE_FUNCTION_DEF, NODE_RETURN, + NODE_STRUCT_DEF, + NODE_STRUCT_ACCESS, } NodeType; typedef struct { - char *name; + String name; ASTNode *index; ASTNode *indices[MAX_DIMENSIONS]; int num_dimensions; @@ -267,9 +289,19 @@ struct ASTNode int ivalue; float fvalue; double dvalue; - char *strvalue; - char *name; + String strvalue; + String name; Array array; + struct { + String struct_name; /* name of the struct type */ + String member_name; /* field being accessed */ + ASTNode *object; /* the struct-valued expr */ + } struct_access; + + struct { + String name; /* struct tag */ + StructField *fields; /* linked list of fields */ + } struct_def; struct { ASTNode *left; @@ -295,7 +327,7 @@ struct ASTNode } while_stmt; struct { - char *function_name; + String function_name; ArgumentList *arguments; } func_call; StatementList *statements; @@ -311,7 +343,7 @@ struct ASTNode } sizeof_stmt; struct { - char *name; + String name; VarType return_type; Parameter *parameters; ASTNode *body; @@ -325,7 +357,7 @@ typedef struct Scope HashMap *variables; struct Scope *parent; bool is_function_scope; - char *function_name; + String function_name; } Scope; /* Global variable declarations */ @@ -335,53 +367,53 @@ extern HashMap *function_map; extern ReturnValue current_return_value; extern JumpBuffer *jump_buffer; /* Function prototypes */ -bool set_int_variable(const char *name, int value, TypeModifiers mods); -bool set_array_variable(char *name, int length, TypeModifiers mods, VarType type); -bool set_short_variable(const char *name, short value, TypeModifiers mods); -bool set_float_variable(const char *name, float value, TypeModifiers mods); -bool set_double_variable(const char *name, double value, TypeModifiers mods); -TypeModifiers get_variable_modifiers(const char *name); +bool set_int_variable(const String name, int value, TypeModifiers mods); +bool set_array_variable(String name, int length, TypeModifiers mods, VarType type); +bool set_short_variable(const String name, short value, TypeModifiers mods); +bool set_float_variable(const String name, float value, TypeModifiers mods); +bool set_double_variable(const String name, double value, TypeModifiers mods); +TypeModifiers get_variable_modifiers(const String name); void reset_modifiers(void); TypeModifiers get_current_modifiers(void); -Variable *get_variable(const char *name); +Variable *get_variable(const String name); Scope *create_scope(Scope *parent); void enter_function_scope(Function *func, ArgumentList *args); void exit_scope(); void enter_scope(); void free_scope(Scope *scope); -void add_variable_to_scope(const char *name, Variable *var); -Variable *variable_new(char *name); -Function *get_function(const char *name); -VarType get_function_return_type(const char *name); +void add_variable_to_scope(const String name, Variable *var); +Variable *variable_new(String name); +Function *get_function(const String name); +VarType get_function_return_type(const String name); /* Node creation functions */ ASTNode *create_int_node(int value); -ASTNode *create_array_declaration_node(char *name, int length, VarType type); -ASTNode *create_array_access_node(char *name, ASTNode *index); +ASTNode *create_array_declaration_node(String name, int length, VarType type); +ASTNode *create_array_access_node(String name, ASTNode *index); ASTNode *create_short_node(short value); ASTNode *create_float_node(float value); ASTNode *create_double_node(double value); ASTNode *create_char_node(char value); ASTNode *create_boolean_node(bool value); -ASTNode *create_identifier_node(char *name); -ASTNode *create_identifier_node_ex(char *name, int pointer_level); -ASTNode *create_assignment_node(char *name, ASTNode *expr); +ASTNode *create_identifier_node(String name); +ASTNode *create_identifier_node_ex(String name, int pointer_level); +ASTNode *create_assignment_node(String name, ASTNode *expr); ASTNode *create_assignment_target_node(ASTNode *target, ASTNode *expr); -ASTNode *create_declaration_node(char *name, ASTNode *expr); -ASTNode *create_declaration_node_ex(char *name, ASTNode *expr, int pointer_level); +ASTNode *create_declaration_node(String name, ASTNode *expr); +ASTNode *create_declaration_node_ex(String name, ASTNode *expr, int pointer_level); ASTNode *create_operation_node(OperatorType op, ASTNode *left, ASTNode *right); ASTNode *create_unary_operation_node(OperatorType op, ASTNode *operand); ASTNode *create_for_statement_node(ASTNode *init, ASTNode *cond, ASTNode *incr, ASTNode *body); ASTNode *create_while_statement_node(ASTNode *cond, ASTNode *body); ASTNode *create_do_while_statement_node(ASTNode *cond, ASTNode *body); -ASTNode *create_function_call_node(char *func_name, ArgumentList *args); +ASTNode *create_function_call_node(String func_name, ArgumentList *args); ArgumentList *create_argument_list(ASTNode *expr, ArgumentList *existing_list); ASTNode *create_print_statement_node(ASTNode *expr); ASTNode *create_sizeof_node(ASTNode *node); ASTNode *create_error_statement_node(ASTNode *expr); ASTNode *create_statement_list(ASTNode *statement, ASTNode *next_statement); ASTNode *create_if_statement_node(ASTNode *condition, ASTNode *then_branch, ASTNode *else_branch); -ASTNode *create_string_literal_node(char *string); +ASTNode *create_string_literal_node(String string); ASTNode *create_switch_statement_node(ASTNode *expression, CaseNode *cases); CaseNode *create_case_node(ASTNode *value, ASTNode *statements); CaseNode *create_default_case_node(ASTNode *statements); @@ -392,7 +424,7 @@ ASTNode *create_return_node(ASTNode *expr); ExpressionList *create_expression_list(ASTNode *expr); ExpressionList *append_expression_list(ExpressionList *list, ASTNode *expr); void free_expression_list(ExpressionList *list); -void populate_multi_array_variable(char *name, ExpressionList *list, int dimensions[], int num_dimensions); +void populate_multi_array_variable(String name, ExpressionList *list, int dimensions[], int num_dimensions); void free_ast(void); /* Evaluation and execution functions */ @@ -403,8 +435,8 @@ int evaluate_expression_int(ASTNode *node); short evaluate_expression_short(ASTNode *node); bool evaluate_expression_bool(ASTNode *node); int evaluate_expression(ASTNode *node); -bool is_const_variable(const char *name); -void check_const_assignment(const char *name); +bool is_const_variable(const String name); +void check_const_assignment(const String name); void execute_statement(ASTNode *node); void execute_statements(ASTNode *node); void execute_assignment(ASTNode *node); @@ -413,7 +445,7 @@ void execute_while_statement(ASTNode *node); void execute_do_while_statement(ASTNode *node); void execute_if_statement(ASTNode *node); void reset_modifiers(void); -bool check_and_mark_identifier(ASTNode *node, const char *contextErrorMessage); +bool check_and_mark_identifier(ASTNode *node, const String contextErrorMessage); bool is_expression(ASTNode *node, VarType type); int get_expression_pointer_level(ASTNode *node); uintptr_t evaluate_expression_pointer(ASTNode *node); @@ -421,35 +453,38 @@ void *evaluate_lvalue_address(ASTNode *node); void bruh(); size_t count_expression_list(ExpressionList *list); size_t handle_sizeof(ASTNode *node); -size_t get_type_size(char *name); +size_t get_type_size(String name); size_t get_type_size_for_descriptor(VarType type, int pointer_level, TypeModifiers mods); void *handle_function_call(ASTNode *node); -ASTNode *create_multi_array_declaration_node(char *name, int dimensions[], int num_dimensions, VarType type); -bool set_multi_array_variable(const char *name, int dimensions[], int num_dimensions, TypeModifiers mods, VarType type); -ASTNode *create_array_access_node_single(char *name, ASTNode *index); -ASTNode *create_multi_array_access_node(char *name, ASTNode *indices[], int num_indices); - -/* Built-In functions */ -void execute_yapping_call(ArgumentList *args); -void execute_yappin_call(ArgumentList *args); -void execute_baka_call(ArgumentList *args); -void execute_ragequit_call(ArgumentList *args); -void execute_chill_call(ArgumentList *args); -void execute_slorp_call(ArgumentList *args); +ASTNode *create_multi_array_declaration_node(String name, int dimensions[], int num_dimensions, VarType type); +bool set_multi_array_variable(const String name, int dimensions[], int num_dimensions, TypeModifiers mods, VarType type); +ASTNode *create_array_access_node_single(String name, ASTNode *index); +ASTNode *create_multi_array_access_node(String name, ASTNode *indices[], int num_indices); /* User-defined functions */ -Function *create_function(char *name, VarType return_type, Parameter *params, ASTNode *body); -Function *create_function_ex(char *name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body); -Parameter *create_parameter(char *name, VarType type, Parameter *next, TypeModifiers mods); -Parameter *create_parameter_ex(char *name, VarType type, int pointer_level, Parameter *next, TypeModifiers mods); -void execute_function_call(const char *name, ArgumentList *args); -ASTNode *create_function_def_node(char *name, VarType return_type, Parameter *params, ASTNode *body); -ASTNode *create_function_def_node_ex(char *name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body); +Function *create_function(String name, VarType return_type, Parameter *params, ASTNode *body); +Function *create_function_ex(String name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body); +Parameter *create_parameter(String name, VarType type, Parameter *next, TypeModifiers mods); +Parameter *create_parameter_ex(String name, VarType type, int pointer_level, Parameter *next, TypeModifiers mods); +void execute_function_call(const String name, ArgumentList *args); +ASTNode *create_function_def_node(String name, VarType return_type, Parameter *params, ASTNode *body); +ASTNode *create_function_def_node_ex(String name, VarType return_type, int return_pointer_level, Parameter *params, ASTNode *body); void handle_return_statement(ASTNode *expr); void *handle_binary_operation(ASTNode *node); void free_function_table(void); void free_static_variable_map(void); +/* Struct types */ +void register_struct_def(StructDef *def); +StructDef *get_struct_def(const String name); +void free_struct_registry(void); +StructField *find_struct_field(StructDef *def, const String name); +size_t compute_struct_layout(StructField *fields); /* fills offsets, returns total */ +ASTNode *create_struct_def_node(String name, StructField *fields); +ASTNode *create_struct_access_node(ASTNode *object, String member); +void *evaluate_struct_member_address(ASTNode *node); +void populate_struct_variable(const String name, ExpressionList *list); + extern TypeModifiers current_modifiers; extern Arena arena; diff --git a/docs/brainrot-user-guide.md b/docs/brainrot-user-guide.md index 3e16901..3ef5f33 100644 --- a/docs/brainrot-user-guide.md +++ b/docs/brainrot-user-guide.md @@ -2,9 +2,9 @@ # 1. Introduction -This language (informally called **Brainrot**) allows you to write a “main” function using the keyword `skibidi main`, declare integer variables with `rizz`, and use specialized keywords for loops (`goon` for while, `flex` for for-loops), conditionals (`edgy` for if, `amogus` for else), and more. It also includes three built-in print/error functions—`yapping`, `yappin`, and `baka`—to handle common output scenarios. +This language (informally called **Brainrot**) allows you to write a "main" function using the keyword `skibidi main`, declare integer variables with `rizz`, and use specialized keywords for loops (`goon` for while, `flex` for for-loops), conditionals (`edgy` for if, `amogus` for else), and more. It also includes three built-in print/error functions—`yapping`, `yappin`, and `baka`—to handle common output scenarios. -Below, you’ll find a reference for each core feature, along with short code snippets illustrating proper usage. +Below, you'll find a reference for each core feature, along with short code snippets illustrating proper usage. --- @@ -141,8 +141,8 @@ amogus { } ``` -- **`edgy`**: The “if” keyword. -- **`amogus`**: The “else” keyword. +- **`edgy`**: The "if" keyword. +- **`amogus`**: The "else" keyword. You can nest these if you want multiple branches. @@ -212,11 +212,101 @@ ohio (expr) { - **`based`**: The `default` keyword. - **`bruh`**: The `break` statement, optionally used to exit the switch after a case. -_(Your actual grammar might vary, but these are the typical synonyms used.)_ +--- + +# 8. Structs (`gang`) + +Use **`gang`** to define a struct type and declare struct variables. + +### Struct Definition + +Define a struct outside of `skibidi main` (at the top level): + +```c +gang Point { + rizz x; + rizz y; + chad magnitude; +}; +``` + +- **`gang TypeName { ... };`**: Defines a new struct type. +- Fields are declared using any supported type keyword (`rizz`, `chad`, `gigachad`, `smol`, `cap`, `yap`). +- The definition must end with `};`. + +### Struct Declaration + +Declare a struct variable inside a function: + +```c +gang Point p; +``` + +This allocates storage for all fields, zero-initialized. + +### Initializer Syntax + +You can initialize a struct at declaration time with a brace-enclosed list: + +```c +gang Point q = {10, 20, 0.0}; +``` + +Values are assigned to fields in order of declaration. + +### Member Access + +Use `.` to read or write individual fields: + +```c +p.x = 3; +p.y = 4; +p.magnitude = 5.0; +yapping("Point: %d %d %f", p.x, p.y, p.magnitude); +``` + +### Full Example + +```c +gang Point { + rizz x; + rizz y; + chad magnitude; +}; + +skibidi main { + gang Point p; + p.x = 3; + p.y = 4; + p.magnitude = 5.0; + yapping("Point: %d %d %f", p.x, p.y, p.magnitude); + + gang Point q = {10, 20, 0.0}; + yapping("Q: %d %d %f", q.x, q.y, q.magnitude); +} +``` + +Output: +``` +Point: 3 4 5.0 +Q: 10 20 0.0 +``` + +### Keyword Reference + +| Brainrot | C equivalent | +|----------|-------------| +| `gang` | `struct` | +| `.` | `.` | + +### Current Limitations + +- Nested struct access (`p.inner.x`) is not yet supported. +- Structs cannot be passed as function parameters or returned from functions. --- -# 8. Return Statements (`bussin`) +# 9. Return Statements (`bussin`) To return from **`skibidi main`** (or any function, if you support them), use **`bussin expression`**: @@ -226,11 +316,9 @@ bussin 0; - This signals that your program (or function) finishes execution and returns the given value. -_(If your grammar doesn’t define actual multi-function usage beyond `main`, `bussin 0` is a typical “exit code.”)_ - --- -# 8.1 Call by Reference (via pointers) +# 9.1 Call by Reference (via pointers) Brainrot uses pointer-based call by reference, just like C. @@ -252,7 +340,7 @@ For multi-level reference passing, use `**`, `***`, etc. --- -# 9. Built-In Functions +# 10. Built-In Functions Brainrot includes some built-in functions for convenience: @@ -266,7 +354,7 @@ Brainrot includes some built-in functions for convenience: | **slorp** | `stdin` | - | Reads user input. | | **bet** | `stderr` | No | Tests conditions and terminates with error message if false. | -## 9.1. yapping +## 10.1. yapping **Prototype** @@ -286,7 +374,7 @@ yapping("Hello %s", "User"); 🚽 Prints => "Hello User" + newline ``` -## 9.2. yappin +## 10.2. yappin **Prototype** @@ -307,7 +395,7 @@ yappin("Hello "); yappin("World!\n"); 🚽 One newline here ``` -## 9.3. baka +## 10.3. baka **Prototype** @@ -326,9 +414,7 @@ void baka(const char* format, ...); baka("Error: something went wrong at %s\n", location); ``` -_(This prints to stderr, not stdout.)_ - -## 9.4. ragequit +## 10.4. ragequit **Prototype** @@ -339,8 +425,7 @@ void ragequit(int exit_code); **Key Points** - Terminates program execution immediately with the provided exit code. -- Behaves like exit(exit_code);, but uses the custom ragequit keyword for dramatic exits. -- No additional output is printed unless explicitly added before the ragequit call. +- Behaves like `exit(exit_code)`, but with more drama. ### Example @@ -356,12 +441,7 @@ amogus { } ``` -In the example above: - -- If i == 1, the program prints the message and exits with code 1. -- If the condition fails, the program exits with code 0. - -## 9.5. chill +## 10.5. chill **Prototype** @@ -371,20 +451,20 @@ void chill(unsigned int seconds); **Key Points** -- Sleeps for a specified number of seconds (must be an unsigned integer) +- Sleeps for a specified number of seconds (must be an unsigned integer). ### Example ```c skibidi main { - yapping("I'll chill for a 2 seconds ..."); + yapping("I'll chill for 2 seconds ..."); chill(2); 🚽 sleep for 2 seconds yapping("Ok imma head out"); bussin 0; } ``` -## 9.6. slorp +## 10.6. slorp **Prototype** @@ -394,7 +474,7 @@ void slorp(var_type var_name); **Key Points** -- Reads user input (similar to C's `scanf` but safer) +- Reads user input (similar to C's `scanf` but safer). ### Example @@ -402,13 +482,13 @@ void slorp(var_type var_name); skibidi main { rizz num; yapping("Enter a number:"); - slorp(num); + slorp(num); yapping("You typed: %d", num); - bussin 0; + bussin 0; } ``` -## 9.7. bet +## 10.7. bet **Prototype** @@ -418,10 +498,9 @@ void bet(int condition, const char* message); **Key Points** -- Tests a condition and terminates the program if it's false -- Similar to C's `assert()` macro -- When the condition fails, prints an error message with the line number and optional custom message -- Useful for catching bugs and verifying assumptions during development +- Tests a condition and terminates the program if it's false. +- Similar to C's `assert()` macro. +- When the condition fails, prints an error message with the line number and optional custom message. ### Example @@ -449,11 +528,9 @@ Output: Error: bet: assertion failed at line 2: this assertion must fail ``` -The program terminates immediately when a `bet` fails. - --- -# 10. Example Program +# 11. Example Program Below is a short **full** example showing variable declarations, loops, conditionals, and printing: @@ -488,22 +565,12 @@ skibidi main { } ``` -### Explanation - -1. **`rizz i = 1;`** declares an integer `i` with initial value 1. -2. **`goon (i < 5) { ... }`** loops while `i` is less than 5. -3. Inside the loop, two prints: - - `yapping(...)` => includes an automatic newline. - - `yappin(...)` => user must add `\n` if needed. -4. We increment `i` each iteration until `i >= 5`. -5. **`edgy (i == 5) { ... } amogus { ... }`** checks if `i` is exactly 5. If not, logs an error using `baka(...)`. -6. **`bussin 0;`** exits the program with code 0. - --- -## 11. Additional Notes +## 12. Additional Notes -- **Keywords** like `skibidi`, `rizz`, `goon`, `flex`, `edgy`, `amogus`, etc., are specialized synonyms for standard concepts (`main`, `int`, `while`, `for`, `if`, `else`, etc.). +- **Keywords** like `skibidi`, `rizz`, `goon`, `flex`, `edgy`, `amogus`, `gang`, etc., are specialized synonyms for standard concepts (`main`, `int`, `while`, `for`, `if`, `else`, `struct`, etc.). - **Syntax** is otherwise quite C-like: `;` to end statements, braces `{ }` to define blocks, parentheses `( )` around conditions. - **Expressions** accept typical operators (`+`,`++`, `-`,`--`, `*`, `/`, `%`, relational, logical) plus the assignment operator `=`, matching standard precedence rules. -- **Escapes in strings** (`"\n"`, `"\t"`, etc.) may require an unescape function in your lexer, so check that it’s converting them into real newlines or tabs at runtime. +- **Escapes in strings** (`"\n"`, `"\t"`, etc.) may require an unescape function in your lexer, so check that it's converting them into real newlines or tabs at runtime. +```` diff --git a/docs/the-brainrot-programming-language.md b/docs/the-brainrot-programming-language.md index cd77853..5ab9d54 100644 --- a/docs/the-brainrot-programming-language.md +++ b/docs/the-brainrot-programming-language.md @@ -19,6 +19,7 @@ A Meme-Fueled Journey into Compiler Design, Internet Slang, and Skibidi Toilets - 7.6. Built-In Functions - 7.7. User Defined Functions - 7.8. Pointers and Call by Reference + - 7.9. Structs (`gang`) 8. **Extended User Documentation** - 8.1. `yapping` - 8.2. `yappin` @@ -156,7 +157,6 @@ Brainrot replaces familiar C keywords with meme-inspired slang: | skibidi | void | | rizz | int | | cap | bool | -| cooked | auto | | flex | for | | bussin | return | | edgy | if | @@ -352,7 +352,91 @@ skibidi main { } ``` -This is the Brainrot equivalent of call by reference. +### 7.9. Structs (`gang`) + +Use **`gang`** to define a struct type and declare struct variables. Structs group multiple fields of different types under a single name. + +#### Struct Definition + +Define a struct at the top level, outside of any function: + +```c +gang Point { + rizz x; + rizz y; + chad magnitude; +}; +``` + +- **`gang TypeName { ... };`**: Defines a new struct type. +- Fields are declared using any supported type keyword (`rizz`, `chad`, `gigachad`, `smol`, `cap`, `yap`). +- The definition must end with `};`. + +#### Struct Declaration + +Declare a struct variable inside a function body: + +```c +gang Point p; +``` + +All fields are zero-initialized by default. + +#### Initializer Syntax + +Initialize a struct at declaration time with a brace-enclosed list: + +```c +gang Point q = {10, 20, 0.0}; +``` + +Values are assigned to fields in order of declaration. + +#### Member Access + +Use `.` to read or write individual fields: + +```c +p.x = 3; +p.y = 4; +p.magnitude = 5.0; +yapping("Point: %d %d %f", p.x, p.y, p.magnitude); +``` + +#### Full Example + +```c +gang Point { + rizz x; + rizz y; + chad magnitude; +}; + +skibidi main { + gang Point p; + p.x = 3; + p.y = 4; + p.magnitude = 5.0; + yapping("Point: %d %d %f", p.x, p.y, p.magnitude); + + gang Point q = {10, 20, 0.0}; + yapping("Q: %d %d %f", q.x, q.y, q.magnitude); + + bussin 0; +} +``` + +Output: + +``` +Point: 3 4 5.0 +Q: 10 20 0.0 +``` + +#### Current Limitations + +- Nested struct access (`p.inner.x`) is not yet supported. +- Structs cannot be passed as function parameters or returned from functions. ## 8. Extended User Documentation @@ -500,7 +584,8 @@ The program terminates immediately when a `bet` fails, preventing further execut - No built-in support for increment/decrement (`++`, `--`). - Functions other than `skibidi main` not fully supported (unless you add them). -- Arrays, complex data structures, and advanced memory management are absent. +- Complex data structures beyond basic structs, and advanced memory management are not fully supported. +- Struct function parameters and return values are not yet implemented. - Error reporting is minimal, typically halting on the first serious parse error. --- diff --git a/interpreter.c b/interpreter.c index f9b2564..554a249 100644 --- a/interpreter.c +++ b/interpreter.c @@ -7,23 +7,23 @@ #include extern void yyerror(const char *s); -extern char *safe_strdup(const char *str); -extern void execute_func_call(const char *func_name, ArgumentList *args); +extern String safe_strdup(const String *str); +extern void execute_func_call(const String func_name, ArgumentList *args); /* External functions we need from the original implementation */ -extern Variable *variable_new(char *name); -extern void add_variable_to_scope(const char *name, Variable *var); -extern Variable *get_variable(const char *name); -extern Function *get_function(const char *name); -extern bool is_builtin_function(const char *name); -extern void execute_builtin_function(const char *name, ArgumentList *args); +extern Variable *variable_new(String name); +extern void add_variable_to_scope(const String name, Variable *var); +extern Variable *get_variable(const String name); +extern Function *get_function(const String name); +extern bool is_builtin_function(const String name); +extern void execute_builtin_function(const String name, ArgumentList *args); extern void execute_assignment(ASTNode *node); extern void execute_for_statement(ASTNode *node); extern void execute_while_statement(ASTNode *node); extern void execute_do_while_statement(ASTNode *node); extern void execute_switch_statement(ASTNode *node); extern void handle_return_statement(ASTNode *expr); -extern Function *create_function(char *name, VarType return_type, Parameter *params, ASTNode *body); +extern Function *create_function(String name, VarType return_type, Parameter *params, ASTNode *body); extern void bruh(void); /* For now, use the original evaluation system with visitor wrappers */ @@ -32,7 +32,7 @@ extern float evaluate_expression_float(ASTNode *node); extern double evaluate_expression_double(ASTNode *node); extern short evaluate_expression_short(ASTNode *node); extern bool evaluate_expression_bool(ASTNode *node); -extern char *evaluate_expression_string(ASTNode *node); +extern String evaluate_expression_string(ASTNode *node); extern void *evaluate_multi_array_access(ASTNode *node); extern void *handle_function_call(ASTNode *node); extern size_t handle_sizeof(ASTNode *node); @@ -170,7 +170,7 @@ void* interpreter_visit_string_literal(Visitor *self, ASTNode *node) { void* interpreter_visit_identifier(Visitor *self, ASTNode *node) { (void)self; - if (!node || !node->data.name) return NULL; + if (!node || !node->data.name.data) return NULL; Variable *var = get_variable(node->data.name); if (!var) { @@ -240,7 +240,7 @@ void* interpreter_visit_function_call(Visitor *self, ASTNode *node) { (void)self; if (!node) return NULL; - const char *func_name = node->data.func_call.function_name; + const String func_name = node->data.func_call.function_name; ArgumentList *args = node->data.func_call.arguments; extern Scope* current_scope; @@ -270,9 +270,23 @@ void* interpreter_visit_sizeof(Visitor *self, ASTNode *node) { void interpreter_visit_declaration(Visitor *self, ASTNode *node) { (void)self; - if (!node || !node->data.op.left || !node->data.op.left->data.name) return; - - char *name = node->data.op.left->data.name; + if (!node || !node->data.op.left || !node->data.op.left->data.name.data) return; + + String name = node->data.op.left->data.name; + /* Struct declarations: set up blob now (at runtime, after hashmap is stable) */ + if (node->var_type == VAR_STRUCT || + (node->data.op.right && node->data.op.right->type == NODE_STRUCT_DEF)) { + const String struct_type = node->data.op.right + ? node->data.op.right->data.struct_def.name + : (String){ .data = NULL, .len = 0 }; + if (!struct_type.data) return; + Variable *sv = get_variable(name); + if (sv && sv->var_type == VAR_STRUCT && !sv->value.array_data) { + StructDef *def = get_struct_def(sv->struct_name.data ? sv->struct_name : struct_type); + if (def) sv->value.array_data = calloc(1, def->total_size); + } + return; + } Variable *var = variable_new(name); var->modifiers = node->modifiers; var->var_type = node->var_type; @@ -287,12 +301,29 @@ void interpreter_visit_declaration(Visitor *self, ASTNode *node) { } } + /* Detect struct declaration: right node is a NODE_STRUCT_DEF */ + if (node->data.op.right && node->data.op.right->type == NODE_STRUCT_DEF) { + var->var_type = VAR_STRUCT; + var->struct_name = safe_strdup(&node->data.op.right->data.struct_def.name); + } + add_variable_to_scope(name, var); SAFE_FREE(var); /* Handle initialization */ if (node->data.op.right) { Variable *scope_var = get_variable(name); + if (scope_var && scope_var->var_type == VAR_STRUCT) { + if (!scope_var->value.array_data) { + StructDef *def = get_struct_def(scope_var->struct_name); + if (def) { + scope_var->value.array_data = calloc(1, def->total_size); + hm_put(current_scope->variables, name.data, name.len, + scope_var, sizeof(Variable)); + } + } + return; + } if (scope_var) { if (scope_var->pointer_level > 0) { scope_var->value.pvalue = evaluate_expression_pointer(node->data.op.right); @@ -330,7 +361,7 @@ void interpreter_visit_declaration(Visitor *self, ASTNode *node) { break; } case VAR_STRING: { - char *string_value = evaluate_expression_string(node->data.op.right); + String string_value = evaluate_expression_string(node->data.op.right); scope_var->value.strvalue = string_value; break; } @@ -514,7 +545,7 @@ void interpreter_visit_print_statement(Visitor *self, ASTNode *node) { ASTNode *expr = node->data.op.left; ArgumentList args = {expr, NULL}; - execute_func_call("yapping", &args); + execute_func_call((String){ .data = "yapping", .len = sizeof("yapping") }, &args); } void interpreter_visit_error_statement(Visitor *self, ASTNode *node) { @@ -523,5 +554,5 @@ void interpreter_visit_error_statement(Visitor *self, ASTNode *node) { ASTNode *expr = node->data.op.left; ArgumentList args = {expr, NULL}; - execute_func_call("baka", &args); + execute_func_call((String){ .data = "baka", .len = sizeof("baka") }, &args); } diff --git a/lang.l b/lang.l index e8fd668..8be6fd8 100644 --- a/lang.l +++ b/lang.l @@ -5,41 +5,56 @@ #include "ast.h" #include "lib/mem.h" #include "lib/arena.h" +#include "lib/string_value.h" #include "lang.tab.h" VarType current_var_type = NONE; -char *unescape_string(const char *src) { - // Allocate a buffer big enough for the worst case - // (same length as src, since we only shrink on escapes) - char *dest = malloc(strlen(src) + 1); +static String copy_bytes(const char *src, size_t len) { + String s; + s.data = safe_malloc(len + 1); + if (!s.data) { fprintf(stderr, "out of memory\n"); exit(1); } + memcpy(s.data, src, len); + s.data[len] = '\0'; + s.len = len; + return s; +} + +static String unescape_string(const char *src, size_t len) { + char *dest = safe_malloc(len + 1); + if (!dest) { fprintf(stderr, "out of memory\n"); exit(1); } + char *d = dest; const char *s = src; + const char *end = src + len; - while (*s) { + while (s < end) { if (*s == '\\') { s++; + if (s >= end) break; switch (*s) { case 'n': *d++ = '\n'; break; case 't': *d++ = '\t'; break; case '\\': *d++ = '\\'; break; - case '"': *d++ = '"'; break; - // ... handle other escapes you care about ... - default: - // If it's an unknown escape like \q, - // you might just copy it literally or skip it. - *d++ = *s; - break; + case '"': *d++ = '"'; break; + case '\'': *d++ = '\''; break; + case 'r': *d++ = '\r'; break; + case '0': *d++ = '\0'; break; + default: *d++ = *s; break; } s++; } else { *d++ = *s++; } } - *d = '\0'; - return dest; -} + String out; + out.len = (size_t)(d - dest); + // removed realloc — incompatible with safe_malloc blocks + out.data = dest; + out.data[out.len] = '\0'; + return out; +} extern int yylineno; %} @@ -74,6 +89,7 @@ extern int yylineno; "maxxing" { return SIZEOF; } "salty" { return STATIC; } "gang" { return STRUCT; } +"." { return DOT; } "ohio" { return SWITCH; } "chungus" { return UNION; } "nonut" { return UNSIGNED; } @@ -154,16 +170,13 @@ extern int yylineno; } '.' { yylval.ival = yytext[1]; return CHAR; } -[a-zA-Z_][a-zA-Z0-9_]* { yylval.strval = safe_strdup(yytext); return IDENTIFIER; } +[a-zA-Z_][a-zA-Z0-9_]* { + yylval.strval = copy_bytes(yytext, (size_t)yyleng); + return IDENTIFIER; +} \"([^\\\"]|\\.)*\" { - // Strip the leading and trailing quotes: - char *raw = (yytext + 1); - raw[strlen(raw) - 1] = '\0'; - - // Convert backslash escapes to real characters: - char *unescaped = unescape_string(raw); - - yylval.strval = unescaped; // Now it has real newlines, etc. + size_t raw_len = (size_t)yyleng - 2; // strip the surrounding quotes + yylval.strval = unescape_string(yytext + 1, raw_len); return STRING_LITERAL; } \'([^\\\']|\\.)\' { diff --git a/lang.y b/lang.y index 774a7fc..254a5fc 100644 --- a/lang.y +++ b/lang.y @@ -6,6 +6,7 @@ #include "interpreter.h" #include "stdrot.h" #include "lib/mem.h" +#include "lib/string_value.h" #include #include #include @@ -16,7 +17,7 @@ int yylex(void); int yylex_destroy(void); void yyerror(const char *s); void cleanup(); -TypeModifiers get_variable_modifiers(const char* name); +TypeModifiers get_variable_modifiers(const String name); extern TypeModifiers current_modifiers; extern VarType current_var_type; @@ -37,7 +38,7 @@ static Interpreter *global_interpreter = NULL; float fval; double dval; char cval; - char *strval; + String strval; ASTNode *node; CaseNode *case_node; ArgumentList *args; @@ -67,6 +68,9 @@ static Interpreter *global_interpreter = NULL; %token FLOAT_LITERAL %token DOUBLE_LITERAL %token SLORP +%token DOT +%type struct_def struct_access +%type struct_field_list struct_field /* reuse Parameter as field carrier */ /* Declare types for non-terminals */ %type type @@ -107,6 +111,7 @@ static Interpreter *global_interpreter = NULL; %nonassoc LOWER_THAN_ELSE %nonassoc ELSE +%left DOT %right EQUALS /* Assignment operator */ %left OR /* Logical OR */ %left AND /* Logical AND */ @@ -128,6 +133,58 @@ function_def_list { $$ = NULL; } | function_def_list function_def { $$ = create_statement_list($2, $1); } + | function_def_list struct_def + { $$ = $1; (void)$2; } + ; + +struct_def + : STRUCT IDENTIFIER LBRACE struct_field_list RBRACE SEMICOLON + { + /* Build StructField list from Parameter list */ + StructField *fields = NULL, *tail = NULL; + Parameter *p = $4; + while (p) { + StructField *f = SAFE_MALLOC(StructField); + f->name = safe_strdup(&p->name); + f->type = p->type; + f->pointer_level = p->pointer_level; + f->offset = 0; /* filled by compute_struct_layout */ + f->next = NULL; + if (!tail) { fields = tail = f; } + else { tail->next = f; tail = f; } + p = p->next; + } + size_t total = compute_struct_layout(fields); + StructDef *def = SAFE_MALLOC(StructDef); + def->name = safe_strdup(&$2); + def->fields = fields; + def->total_size = total; + register_struct_def(def); + $$ = create_struct_def_node($2, fields); + SAFE_FREE($2); + } + ; + +struct_field_list + : struct_field + { $$ = $1; } + | struct_field_list struct_field + { + /* append $2 to the end of $1 */ + Parameter *tail = $1; + while (tail->next) tail = tail->next; + tail->next = $2; + $$ = $1; + } + ; + +struct_field + : type declarator SEMICOLON + { + $$ = create_parameter_ex($2.name, $1, $2.pointer_level, NULL, + (TypeModifiers){0}); + SAFE_FREE($2.name); + } ; function_def @@ -318,6 +375,55 @@ declaration: SAFE_FREE(var); free_expression_list($6); } + | optional_modifiers STRUCT IDENTIFIER declarator + { + Variable *var = variable_new($4.name); + var->var_type = VAR_STRUCT; + var->struct_name = safe_strdup(&$3); + add_variable_to_scope($4.name, var); + SAFE_FREE(var); + + Variable *scope_var = get_variable($4.name); + StructDef *def = get_struct_def($3); + if (scope_var && def) { + scope_var->value.array_data = calloc(1, def->total_size); + } + + $$ = create_declaration_node_ex($4.name, + create_struct_def_node($3, def ? def->fields : NULL), + $4.pointer_level); + $$->var_type = VAR_STRUCT; + if ($$->data.op.right) + $$->data.op.right->data.name = ARENA_STRDUP($3); + SAFE_FREE($3); + SAFE_FREE($4.name); + } + | optional_modifiers STRUCT IDENTIFIER declarator EQUALS LBRACE initializer_list RBRACE + { + Variable *var = variable_new($4.name); + var->var_type = VAR_STRUCT; + var->struct_name = safe_strdup(&$3); + add_variable_to_scope($4.name, var); + SAFE_FREE(var); + + /* Fetch the scope-owned copy, allocate blob, then populate */ + Variable *scope_var = get_variable($4.name); + StructDef *def = get_struct_def($3); + if (scope_var && def) { + scope_var->value.array_data = calloc(1, def->total_size); + populate_struct_variable($4.name, $7); + } + + $$ = create_declaration_node_ex($4.name, + create_struct_def_node($3, def ? def->fields : NULL), + $4.pointer_level); + $$->var_type = VAR_STRUCT; + if ($$->data.op.right) + $$->data.op.right->data.name = ARENA_STRDUP($3); + SAFE_FREE($3); + SAFE_FREE($4.name); + free_expression_list($7); + } ; array_init: @@ -458,13 +564,16 @@ increment: function_call: SLORP LPAREN identifier RPAREN - { - $$ = create_function_call_node("slorp", create_argument_list($3, NULL)); + { + $$ = create_function_call_node( + (String){ .data = "slorp", .len = sizeof("slorp") - 1 }, + create_argument_list($3, NULL) + ); } | IDENTIFIER LPAREN arg_list RPAREN { $$ = create_function_call_node($1, $3); - SAFE_FREE($1); + SAFE_FREE($1.data); } ; @@ -517,6 +626,7 @@ expression: | array_access | sizeof_expression | function_call + | struct_access ; sizeof_expression: @@ -529,7 +639,7 @@ literal: | CHAR { $$ = create_char_node($1); } | SHORT_LITERAL { $$ = create_short_node($1); } | BOOLEAN { $$ = create_boolean_node($1); } - | STRING_LITERAL { $$ = create_string_literal_node($1); free($1);} + | STRING_LITERAL { $$ = create_string_literal_node($1); SAFE_FREE($1.data);} ; identifier: @@ -552,6 +662,8 @@ assignment_target: { $$ = $1; } | array_access { $$ = $1; } + | struct_access + { $$ = $1; } | TIMES assignment_target %prec UMINUS { $$ = create_unary_operation_node(OP_DEREFERENCE, $2); } ; @@ -619,6 +731,13 @@ array_access: } ; +struct_access: + expression DOT IDENTIFIER + { + $$ = create_struct_access_node($1, $3); + SAFE_FREE($3); + } + ; %% int main(int argc, char *argv[]) { @@ -704,13 +823,15 @@ void cleanup() { free_static_variable_map(); + free_struct_registry(); + CLEAN_JUMP_BUFFER(); // Clean up flex's internal state yylex_destroy(); } -TypeModifiers get_variable_modifiers(const char* name) { +TypeModifiers get_variable_modifiers(const String name) { TypeModifiers mods = {false, false, false, false, false, false, false, false}; // Default modifiers Variable *var = get_variable(name); if (var != NULL) { diff --git a/lib/arena.c b/lib/arena.c index d496741..1febbda 100644 --- a/lib/arena.c +++ b/lib/arena.c @@ -74,13 +74,16 @@ void *arena_alloc(Arena *arena, size_t size_bytes) * @param str The string to copy. * @return The pointer to the copied string. */ -char *arena_strdup(Arena *arena, const char *str) +String arena_strdup(Arena *arena, String str) { - size_t len = strlen(str); - char *result = (char *)arena_alloc(arena, len + 1); - memcpy(result, str, len); - result[len] = '\0'; - return result; + String out; + out.len = str.len; + out.data = arena_alloc(arena, out.len + 1); + + memcpy(out.data, str.data, out.len); + out.data[out.len] = '\0'; // optional + + return out; } /* diff --git a/lib/arena.h b/lib/arena.h index a62636a..9fd076c 100644 --- a/lib/arena.h +++ b/lib/arena.h @@ -2,6 +2,7 @@ #define AREANA_H #include "mem.h" +#include "string_value.h" // Default region size 4KB or 1 page of memory #define DEFAULT_REGION_SIZE (4 * 1024) @@ -20,7 +21,7 @@ typedef struct Arena { Region *region_new(size_t size); void region_free(Region *region); void *arena_alloc(Arena *arena, size_t size_bytes); -char *arena_strdup(Arena *arena, const char *str); +String arena_strdup(Arena *arena, const String str); void arena_reset(Arena *arena); void arena_free(Arena *arena); #endif // AREANA_H diff --git a/lib/hm.c b/lib/hm.c index e5f08a0..bc30a9c 100644 --- a/lib/hm.c +++ b/lib/hm.c @@ -110,7 +110,7 @@ void dump(HashMap *hm) if (hm->nodes[i]) { Variable *v = (Variable *)hm->nodes[i]->value; - printf("key: %p, value: %s, is_array: %s\n", hm->nodes[i]->key, v->name, v->is_array ? "true" : "false"); + printf("key: %p, value: %s, is_array: %s\n", hm->nodes[i]->key, v->name.data, v->is_array ? "true" : "false"); } } } @@ -241,10 +241,19 @@ void hm_free(HashMap *hm) { SAFE_FREE(var->value.array_data); } - else if (var->var_type == VAR_STRING && var->value.strvalue) + else if (var->var_type == VAR_STRUCT && var->value.array_data) + { + free(var->value.array_data); + var->value.array_data = NULL; + } + else if (var->var_type == VAR_STRING && var->value.strvalue.data) { SAFE_FREE(var->value.strvalue); } + if (var->struct_name.data) + { + SAFE_FREE(var->struct_name); + } } SAFE_FREE(hm->nodes[i]->value); diff --git a/lib/mem.c b/lib/mem.c index 59318f1..04f9ab2 100644 --- a/lib/mem.c +++ b/lib/mem.c @@ -1,4 +1,5 @@ #include "mem.h" +#include "string_value.h" #include /** @@ -348,21 +349,21 @@ void *safe_memcpy(void *dest, const void *src, size_t n) * @note Sets errno on failure * @note Returned string must be freed with safe_free */ -char *safe_strdup(const char *str) +String safe_strdup(const String *str) { - if (!str) - { + if (!str || !str->data) { errno = EINVAL; - return NULL; + return (String){ .data = NULL, .len = 0 }; } - size_t len = strlen(str) + 1; - char *new_str = safe_malloc(len); - if (new_str) - { - memcpy(new_str, str, len); - } - return new_str; + String out; + out.len = str->len; + out.data = safe_malloc(out.len + 1); + + memcpy(out.data, str->data, out.len); + out.data[out.len] = '\0'; // optional + + return out; } diff --git a/lib/mem.h b/lib/mem.h index 2ddea7c..6df5271 100644 --- a/lib/mem.h +++ b/lib/mem.h @@ -3,6 +3,7 @@ #ifndef MEM_H #define MEM_H +#include "string_value.h" #include #include #include @@ -32,7 +33,7 @@ void *safe_malloc(size_t size); void *safe_malloc_array(size_t nmemb, size_t size); void safe_free(void **ptr, const char *file, int line, const char *func); void *safe_memcpy(void *dest, const void *src, size_t n); -char *safe_strdup(const char *str); +String safe_strdup(const String *str); int is_safe_malloc_ptr(const void *ptr); void *safe_calloc(size_t count, size_t size); diff --git a/lib/string_value.h b/lib/string_value.h new file mode 100644 index 0000000..c2566b0 --- /dev/null +++ b/lib/string_value.h @@ -0,0 +1,13 @@ +#ifndef STRING_VALUE_H +#define STRING_VALUE_H + +#include + +#define MAX_BUFFER_LEN 512 + +typedef struct { + char *data; + size_t len; +} String; + +#endif diff --git a/semantic_analyzer.c b/semantic_analyzer.c index 49fb231..73d8586 100644 --- a/semantic_analyzer.c +++ b/semantic_analyzer.c @@ -7,8 +7,8 @@ #include extern int yylineno; -extern void yyerror(const char *s); -extern char *safe_strdup(const char *str); +extern void yyerror(const char *s); +extern String safe_strdup(const String *str); extern Scope *current_scope; /* Create a new semantic analyzer */ @@ -107,14 +107,18 @@ bool semantic_analyze(ASTNode *root) { /* Add a semantic error */ void add_semantic_error(SemanticAnalyzer *analyzer, SemanticErrorType type, - const char *message, int line_number) { + char *message, int line_number) { if (!analyzer || !message) return; SemanticError *error = SAFE_MALLOC(SemanticError); if (!error) return; error->type = type; - error->message = safe_strdup(message); + String s = { + .data = message, + .len = MAX_BUFFER_LEN + }; + error->message = safe_strdup(&s); error->line_number = (line_number > 0) ? line_number : 1; error->next = analyzer->errors; @@ -193,9 +197,9 @@ void print_semantic_errors(SemanticAnalyzer *analyzer) { break; default: if (error->line_number > 0) { - fprintf(stderr, "Error: %s at line %d\n", error->message, error->line_number); + fprintf(stderr, "Error: %s at line %d\n", error->message.data, error->line_number); } else { - fprintf(stderr, "Error: %s\n", error->message); + fprintf(stderr, "Error: %s\n", error->message.data); } break; } @@ -360,6 +364,18 @@ VarType infer_expression_type(ASTNode *node, SemanticAnalyzer *analyzer) { return NONE; } + + case NODE_STRUCT_ACCESS: { + ASTNode *obj = node->data.struct_access.object; + Variable *var = (obj->type == NODE_IDENTIFIER) + ? get_variable(obj->data.name) : NULL; + if (!var || var->var_type != VAR_STRUCT) return NONE; + StructDef *def = get_struct_def(var->struct_name); + if (!def) return NONE; + StructField *fld = find_struct_field(def, + node->data.struct_access.member_name); + return fld ? fld->type : NONE; + } default: return NONE; @@ -412,7 +428,7 @@ bool validate_binary_operation(ASTNode *left, ASTNode *right, OperatorType op, S /* Only report errors for really incompatible types */ if ((left_type == VAR_STRING || left_type == VAR_BOOL) || (right_type == VAR_STRING || right_type == VAR_BOOL)) { - char error_msg[256]; + char error_msg[MAX_BUFFER_LEN]; snprintf(error_msg, sizeof(error_msg), "Arithmetic operation requires numeric types, got %s and %s", vartype_to_string(left_type), vartype_to_string(right_type)); @@ -447,7 +463,7 @@ bool validate_binary_operation(ASTNode *left, ASTNode *right, OperatorType op, S /* Only report errors for clearly incompatible types */ if ((left_type == VAR_STRING || left_type == VAR_BOOL) || (right_type == VAR_STRING || right_type == VAR_BOOL)) { - char error_msg[256]; + char error_msg[MAX_BUFFER_LEN]; snprintf(error_msg, sizeof(error_msg), "Relational comparison requires numeric types, got %s and %s", vartype_to_string(left_type), vartype_to_string(right_type)); @@ -472,11 +488,11 @@ bool validate_binary_operation(ASTNode *left, ASTNode *right, OperatorType op, S void* semantic_visit_identifier(Visitor *self, ASTNode *node) { SemanticAnalyzer *analyzer = (SemanticAnalyzer*)self; - if (!node || !node->data.name) return NULL; + if (!node || !node->data.name.data) return NULL; if (analyzer->is_collecting_phase) return NULL; - const char *name = node->data.name; + const String name = node->data.name; SymbolEntry *symbol = find_symbol(analyzer, name); if (!symbol) { @@ -484,7 +500,7 @@ void* semantic_visit_identifier(Visitor *self, ASTNode *node) { bool found_in_deeper_scope = false; while (entry) { - if (strcmp(entry->name, name) == 0) { + if (strcmp(entry->name.data, name.data) == 0) { if (entry->scope_depth > analyzer->scope_depth) { found_in_deeper_scope = true; break; @@ -494,16 +510,16 @@ void* semantic_visit_identifier(Visitor *self, ASTNode *node) { } if (found_in_deeper_scope) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Variable '%s' is out of scope", name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Variable '%s' is out of scope", name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_SCOPE_ERROR, error_msg, node->line_number > 0 ? node->line_number : 1); } else { Variable *var = get_variable(name); if (!var) { if (!is_builtin_function(name)) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Undefined variable '%s'", name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Undefined variable '%s'", name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_UNDEFINED_VARIABLE, error_msg, node->line_number > 0 ? node->line_number : 1); } @@ -517,15 +533,15 @@ void* semantic_visit_identifier(Visitor *self, ASTNode *node) { void* semantic_visit_function_call(Visitor *self, ASTNode *node) { SemanticAnalyzer *analyzer = (SemanticAnalyzer*)self; - if (!node || !node->data.func_call.function_name) return NULL; + if (!node || !node->data.func_call.function_name.data) return NULL; - const char *func_name = node->data.func_call.function_name; + const String func_name = node->data.func_call.function_name; if (!is_builtin_function(func_name)) { Function *func = get_function(func_name); if (!func) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Undefined function '%s'", func_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Undefined function '%s'", func_name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_UNDEFINED_FUNCTION, error_msg, node->line_number > 0 ? node->line_number : 1); } @@ -551,9 +567,9 @@ void* semantic_visit_binary_operation(Visitor *self, ASTNode *node) { void semantic_visit_declaration(Visitor *self, ASTNode *node) { SemanticAnalyzer *analyzer = (SemanticAnalyzer*)self; - if (!node || !node->data.op.left || !node->data.op.left->data.name) return; + if (!node || !node->data.op.left || !node->data.op.left->data.name.data) return; - const char *var_name = node->data.op.left->data.name; + const String var_name = node->data.op.left->data.name; if (node->data.op.right) { VarType declared_type = node->var_type; @@ -563,10 +579,10 @@ void semantic_visit_declaration(Visitor *self, ASTNode *node) { if (declared_type != NONE && init_type != NONE && !check_type_compatibility_ex(declared_type, declared_pointer_level, init_type, init_pointer_level)) { - char error_msg[256]; + char error_msg[MAX_BUFFER_LEN]; snprintf(error_msg, sizeof(error_msg), "Type mismatch in initialization of '%s': expected %s, got %s", - var_name, vartype_to_string(declared_type), vartype_to_string(init_type)); + var_name.data, vartype_to_string(declared_type), vartype_to_string(init_type)); add_semantic_error(analyzer, SEMANTIC_ERROR_TYPE_MISMATCH, error_msg, 1); } @@ -593,7 +609,7 @@ void semantic_visit_assignment(Visitor *self, ASTNode *node) { } if (node->data.op.left->type == NODE_IDENTIFIER) { - const char *var_name = node->data.op.left->data.name; + const String var_name = node->data.op.left->data.name; if (analyzer->is_collecting_phase) return; @@ -603,8 +619,8 @@ void semantic_visit_assignment(Visitor *self, ASTNode *node) { Variable *var = get_variable(var_name); if (!var) { if (!is_builtin_function(var_name)) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Assignment to undefined variable '%s'", var_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Assignment to undefined variable '%s'", var_name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_UNDEFINED_VARIABLE, error_msg, node->line_number > 0 ? node->line_number : 1); } @@ -612,23 +628,26 @@ void semantic_visit_assignment(Visitor *self, ASTNode *node) { } if (var->modifiers.is_const) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", var_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", var_name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_CONST_ASSIGNMENT, error_msg, node->line_number > 0 ? node->line_number : 1); } } else { if (symbol->is_const) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", var_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", var_name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_CONST_ASSIGNMENT, error_msg, node->line_number > 0 ? node->line_number : 1); } } } - if (node->data.op.left->type != NODE_IDENTIFIER && node->data.op.left->type != NODE_ARRAY_ACCESS && - !(node->data.op.left->type == NODE_UNARY_OPERATION && node->data.op.left->data.unary.op == OP_DEREFERENCE)) { + if (node->data.op.left->type != NODE_IDENTIFIER && + node->data.op.left->type != NODE_ARRAY_ACCESS && + node->data.op.left->type != NODE_STRUCT_ACCESS && + !(node->data.op.left->type == NODE_UNARY_OPERATION && + node->data.op.left->data.unary.op == OP_DEREFERENCE)) { add_semantic_error(analyzer, SEMANTIC_ERROR_INVALID_OPERATION, "Left-hand side of assignment is not assignable", node->line_number > 0 ? node->line_number : 1); @@ -649,7 +668,7 @@ void semantic_visit_assignment(Visitor *self, ASTNode *node) { } void semantic_visit_function_definition(Visitor *self, ASTNode *node) { - if (!node || !node->data.function_def.name) return; + if (!node || !node->data.function_def.name.data) return; if (node->data.function_def.body) { ast_accept(node->data.function_def.body, self); @@ -657,13 +676,13 @@ void semantic_visit_function_definition(Visitor *self, ASTNode *node) { } /* Symbol table management functions */ -void add_symbol(SemanticAnalyzer *analyzer, const char *name, VarType type, int pointer_level, bool is_const, bool is_function, VarType return_type, int return_pointer_level, int line_number) { - if (!analyzer || !name) return; +void add_symbol(SemanticAnalyzer *analyzer, const String name, VarType type, int pointer_level, bool is_const, bool is_function, VarType return_type, int return_pointer_level, int line_number) { + if (!analyzer || !name.data) return; SymbolEntry *entry = SAFE_MALLOC(SymbolEntry); if (!entry) return; - entry->name = safe_strdup(name); + entry->name = safe_strdup(&name); entry->type = type; entry->pointer_level = pointer_level; entry->is_const = is_const; @@ -677,13 +696,13 @@ void add_symbol(SemanticAnalyzer *analyzer, const char *name, VarType type, int analyzer->symbol_table = entry; } -SymbolEntry* find_symbol(SemanticAnalyzer *analyzer, const char *name) { - if (!analyzer || !name) return NULL; +SymbolEntry* find_symbol(SemanticAnalyzer *analyzer, const String name) { + if (!analyzer || !name.data) return NULL; SymbolEntry *entry = analyzer->symbol_table; while (entry) { - if (strcmp(entry->name, name) == 0) { + if (strcmp(entry->name.data, name.data) == 0) { /* Check if this symbol is accessible from current scope */ if (entry->scope_depth <= analyzer->scope_depth) { return entry; /* Symbol is accessible */ @@ -717,8 +736,8 @@ void collect_declarations(SemanticAnalyzer *analyzer, ASTNode *node) { switch (node->type) { case NODE_DECLARATION: - if (node->data.op.left && node->data.op.left->data.name) { - const char *var_name = node->data.op.left->data.name; + if (node->data.op.left && node->data.op.left->data.name.data) { + const String var_name = node->data.op.left->data.name; VarType var_type = node->var_type; bool is_const = node->modifiers.is_const; @@ -731,11 +750,11 @@ void collect_declarations(SemanticAnalyzer *analyzer, ASTNode *node) { break; case NODE_FUNCTION_DEF: - if (node->data.function_def.name) { + if (node->data.function_def.name.data) { SymbolEntry *existing = find_symbol(analyzer, node->data.function_def.name); if (existing && existing->is_function) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Function '%s' is already defined", node->data.function_def.name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Function '%s' is already defined", node->data.function_def.name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_REDEFINITION, error_msg, node->line_number > 0 ? node->line_number : 1); } else { @@ -749,7 +768,7 @@ void collect_declarations(SemanticAnalyzer *analyzer, ASTNode *node) { Parameter *param = node->data.function_def.parameters; while (param) { - if (param->name) { + if (param->name.data) { add_symbol(analyzer, param->name, param->type, param->pointer_level, false, false, NONE, 0, node->line_number > 0 ? node->line_number : 1); } @@ -996,6 +1015,34 @@ void semantic_analyze_with_scope_tracking(SemanticAnalyzer *analyzer, ASTNode *n semantic_visit_declaration((Visitor*)analyzer, node); break; } + + case NODE_STRUCT_ACCESS: { + /* Check the object is a known struct variable with that field */ + ASTNode *obj = node->data.struct_access.object; + if (obj) semantic_analyze_with_scope_tracking(analyzer, obj); + if (obj && obj->type == NODE_IDENTIFIER) { + Variable *var = get_variable(obj->data.name); + if (!var) break; + if (var->var_type != VAR_STRUCT) { + add_semantic_error(analyzer, SEMANTIC_ERROR_TYPE_MISMATCH, + "Member access on non-struct variable", + node->line_number > 0 ? node->line_number : 1); + break; + } + StructDef *def = get_struct_def(var->struct_name); + if (def && !find_struct_field(def, + node->data.struct_access.member_name)) { + char msg[MAX_BUFFER_LEN]; + snprintf(msg, sizeof(msg), + "Struct '%s' has no member '%s'", + var->struct_name.data, + node->data.struct_access.member_name.data); + add_semantic_error(analyzer, SEMANTIC_ERROR_UNDEFINED_VARIABLE, + msg, node->line_number > 0 ? node->line_number : 1); + } + } + break; + } default: /* For other node types, only process simple operands to avoid crashes */ @@ -1034,8 +1081,8 @@ void semantic_analyze_node(SemanticAnalyzer *analyzer, ASTNode *node) { case NODE_DECLARATION: { /* Add variable to current scope when we encounter declaration */ - if (node->data.op.left && node->data.op.left->data.name) { - const char *var_name = node->data.op.left->data.name; + if (node->data.op.left && node->data.op.left->data.name.data) { + const String var_name = node->data.op.left->data.name; VarType var_type = node->var_type; bool is_const = node->modifiers.is_const; @@ -1051,14 +1098,14 @@ void semantic_analyze_node(SemanticAnalyzer *analyzer, ASTNode *node) { case NODE_IDENTIFIER: { /* Check if identifier is defined when we encounter it */ - const char *name = node->data.name; + const String name = node->data.name; SymbolEntry *symbol = NULL; if (!find_semantic_variable(analyzer, name, &symbol)) { /* Check if it's a built-in function or keyword */ if (!is_builtin_function(name)){ - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Undefined variable '%s'", name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Undefined variable '%s'", name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_UNDEFINED_VARIABLE, error_msg, node->line_number > 0 ? node->line_number : 1); } @@ -1070,20 +1117,24 @@ void semantic_analyze_node(SemanticAnalyzer *analyzer, ASTNode *node) { /* Check assignment target and value */ if (node->data.op.left) { if (node->data.op.left->type == NODE_IDENTIFIER) { - const char *var_name = node->data.op.left->data.name; + const String var_name = node->data.op.left->data.name; SymbolEntry *symbol = NULL; if (!find_semantic_variable(analyzer, var_name, &symbol)) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Assignment to undefined variable '%s'", var_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Assignment to undefined variable '%s'", var_name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_UNDEFINED_VARIABLE, error_msg, node->line_number > 0 ? node->line_number : 1); } else if (symbol && symbol->is_const) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", var_name); + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", var_name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_CONST_ASSIGNMENT, error_msg, node->line_number > 0 ? node->line_number : 1); } + } else if (node->data.op.left->type != NODE_ARRAY_ACCESS && + node->data.op.left->type != NODE_STRUCT_ACCESS && + !(node->data.op.left->type == NODE_UNARY_OPERATION && + node->data.op.left->data.unary.op == OP_DEREFERENCE)) { } else { /* Analyze left side (could be array access, etc.) */ semantic_analyze_node(analyzer, node->data.op.left); @@ -1256,13 +1307,13 @@ void exit_semantic_scope(SemanticAnalyzer *analyzer) { free_semantic_scope(old_scope); } -bool add_semantic_variable(SemanticAnalyzer *analyzer, const char *name, VarType type, int pointer_level, bool is_const) { - if (!analyzer || !analyzer->current_scope || !name) return false; +bool add_semantic_variable(SemanticAnalyzer *analyzer, const String name, VarType type, int pointer_level, bool is_const) { + if (!analyzer || !analyzer->current_scope || !name.data) return false; /* Check if variable already exists in current scope */ - if (hm_get(analyzer->current_scope->variables, name, strlen(name) + 1)) { - char error_msg[256]; - snprintf(error_msg, sizeof(error_msg), "Variable '%s' already declared in current scope", name); + if (hm_get(analyzer->current_scope->variables, name.data, name.len + 1)) { + char error_msg[MAX_BUFFER_LEN]; + snprintf(error_msg, sizeof(error_msg), "Variable '%s' already declared in current scope", name.data); add_semantic_error(analyzer, SEMANTIC_ERROR_REDEFINITION, error_msg, 1); return false; } @@ -1271,7 +1322,7 @@ bool add_semantic_variable(SemanticAnalyzer *analyzer, const char *name, VarType SymbolEntry *entry = SAFE_MALLOC(SymbolEntry); if (!entry) return false; - entry->name = safe_strdup(name); + entry->name = safe_strdup(&name); entry->type = type; entry->pointer_level = pointer_level; entry->is_const = is_const; @@ -1283,20 +1334,20 @@ bool add_semantic_variable(SemanticAnalyzer *analyzer, const char *name, VarType entry->next = NULL; /* Add to current scope */ - hm_put(analyzer->current_scope->variables, name, strlen(name) + 1, entry, sizeof(SymbolEntry*)); + hm_put(analyzer->current_scope->variables, name.data, name.len + 1, entry, sizeof(SymbolEntry*)); return true; } -bool find_semantic_variable(SemanticAnalyzer *analyzer, const char *name, SymbolEntry **result) { - if (!analyzer || !name || !result) return false; +bool find_semantic_variable(SemanticAnalyzer *analyzer, const String name, SymbolEntry **result) { + if (!analyzer || !name.data || !result) return false; *result = NULL; /* Search through scope chain */ SemanticScope *scope = analyzer->current_scope; while (scope) { - SymbolEntry **entry_ptr = (SymbolEntry**)hm_get(scope->variables, name, strlen(name) + 1); + SymbolEntry **entry_ptr = (SymbolEntry**)hm_get(scope->variables, name.data, name.len + 1); if (entry_ptr && *entry_ptr) { *result = *entry_ptr; return true; diff --git a/semantic_analyzer.h b/semantic_analyzer.h index 52d5299..e6e8040 100644 --- a/semantic_analyzer.h +++ b/semantic_analyzer.h @@ -5,6 +5,7 @@ #include "visitor.h" #include "ast.h" +#include "lib/string_value.h" /* Error types for semantic analysis */ typedef enum { @@ -21,7 +22,7 @@ typedef enum { /* Semantic error structure */ typedef struct SemanticError { SemanticErrorType type; - char *message; + String message; int line_number; struct SemanticError *next; } SemanticError; @@ -37,7 +38,7 @@ typedef struct SemanticScope { /* Symbol table for pre-collected declarations */ typedef struct SymbolEntry { - char *name; + String name; VarType type; int pointer_level; bool is_const; @@ -69,8 +70,8 @@ void semantic_analyzer_free(SemanticAnalyzer *analyzer); bool semantic_analyze(ASTNode *root); /* Symbol table management */ -void add_symbol(SemanticAnalyzer *analyzer, const char *name, VarType type, int pointer_level, bool is_const, bool is_function, VarType return_type, int return_pointer_level, int line_number); -SymbolEntry* find_symbol(SemanticAnalyzer *analyzer, const char *name); +void add_symbol(SemanticAnalyzer *analyzer, const String name, VarType type, int pointer_level, bool is_const, bool is_function, VarType return_type, int return_pointer_level, int line_number); +SymbolEntry* find_symbol(SemanticAnalyzer *analyzer, const String name); void free_symbol_table(SymbolEntry *symbols); /* Semantic scope management */ @@ -80,8 +81,8 @@ void enter_semantic_scope(SemanticAnalyzer *analyzer, bool is_function_scope); void exit_semantic_scope(SemanticAnalyzer *analyzer); /* Variable management in semantic scopes */ -bool add_semantic_variable(SemanticAnalyzer *analyzer, const char *name, VarType type, int pointer_level, bool is_const); -bool find_semantic_variable(SemanticAnalyzer *analyzer, const char *name, SymbolEntry **result); +bool add_semantic_variable(SemanticAnalyzer *analyzer, const String name, VarType type, int pointer_level, bool is_const); +bool find_semantic_variable(SemanticAnalyzer *analyzer, const String name, SymbolEntry **result); /* Two-phase analysis */ void collect_declarations(SemanticAnalyzer *analyzer, ASTNode *root); @@ -91,7 +92,7 @@ void semantic_analyze_node(SemanticAnalyzer *analyzer, ASTNode *node); /* Error reporting functions */ void add_semantic_error(SemanticAnalyzer *analyzer, SemanticErrorType type, - const char *message, int line_number); + char *message, int line_number); void print_semantic_errors(SemanticAnalyzer *analyzer); void free_semantic_errors(SemanticError *errors); diff --git a/stdrot.c b/stdrot.c index f118413..06acc96 100644 --- a/stdrot.c +++ b/stdrot.c @@ -22,7 +22,11 @@ #include /* ── Global execution context ────────────────────────────────────────────── */ -ExecutionContext g_exec_context = {0, NULL, NULL}; +ExecutionContext g_exec_context = { + 0, + { NULL, 0 }, + { NULL, 0 } +}; /* ── External interpreter functions ──────────────────────────────────────── */ extern void yyerror(const char *s); @@ -32,14 +36,14 @@ extern double evaluate_expression_double(ASTNode *node); extern short evaluate_expression_short(ASTNode *node); extern bool evaluate_expression_bool(ASTNode *node); extern bool is_expression(ASTNode *node, VarType type); -extern Variable *get_variable(const char *name); -extern TypeModifiers get_variable_modifiers(const char* name); +extern Variable *get_variable(const String name); +extern TypeModifiers get_variable_modifiers(const String name); extern void *evaluate_multi_array_access(ASTNode *node); -extern bool set_int_variable(const char *name, int value, TypeModifiers mods); -extern bool set_float_variable(const char *name, float value, TypeModifiers mods); -extern bool set_double_variable(const char *name, double value, TypeModifiers mods); -extern bool set_short_variable(const char *name, short value, TypeModifiers mods); -extern bool set_bool_variable(const char *name, bool value, TypeModifiers mods); +extern bool set_int_variable(const String name, int value, TypeModifiers mods); +extern bool set_float_variable(const String name, float value, TypeModifiers mods); +extern bool set_double_variable(const String name, double value, TypeModifiers mods); +extern bool set_short_variable(const String name, short value, TypeModifiers mods); +extern bool set_bool_variable(const String name, bool value, TypeModifiers mods); /* ── Dynamic library state ────────────────────────────────────────────────── */ static void *lib_handle = NULL; @@ -49,7 +53,7 @@ static int function_count = 0; /* Symbol cache to avoid repeated dlsym calls */ #define STDROT_CACHE_SIZE 64 typedef struct { - const char *name; + String name; void *ptr; } SymbolCache; @@ -57,13 +61,13 @@ static SymbolCache symbol_cache[STDROT_CACHE_SIZE]; static int cache_count = 0; /* ── Forward declarations of stub functions ──────────────────────────────── */ -void yapping(const char* format, ...); -void yappin(const char* format, ...); -void baka(const char* format, ...); +void yapping(const String format, ...); +void yappin(const String format, ...); +void baka(const String format, ...); void ragequit(int exit_code); void chill(unsigned int seconds); char slorp_char(char chr); -char *slorp_string(char *string, size_t size); +String slorp_string(String string, size_t size); int slorp_int(int val); short slorp_short(short val); float slorp_float(float var); @@ -71,21 +75,22 @@ double slorp_double(double var); /* ── Dynamic symbol lookup with caching ──────────────────────────────────── */ -static void *stdrot_lookup_symbol(const char *symbol_name) +static void *stdrot_lookup_symbol(const String symbol_name) { - if (!lib_handle || !symbol_name) return NULL; + if (!lib_handle || !symbol_name.data) return NULL; /* Check cache first */ for (int i = 0; i < cache_count; i++) { - if (strcmp(symbol_cache[i].name, symbol_name) == 0) { + if (strcmp(symbol_cache[i].name.data, symbol_name.data) == 0) { return symbol_cache[i].ptr; } } /* Not in cache, lookup via dlsym */ - void *ptr = dlsym(lib_handle, symbol_name); + void *ptr = dlsym(lib_handle, symbol_name.data); if (ptr && cache_count < STDROT_CACHE_SIZE) { - symbol_cache[cache_count].name = symbol_name; + symbol_cache[cache_count].name.data = symbol_name.data; + symbol_cache[cache_count].name.len = symbol_name.len; symbol_cache[cache_count].ptr = ptr; cache_count++; } @@ -145,19 +150,19 @@ void stdrot_unload(void) /* ── Runtime query ────────────────────────────────────────────────────────── */ -bool is_builtin_function(const char *func_name) +bool is_builtin_function(const String func_name) { - if (!func_name || !functions) return false; + if (!func_name.data || !functions) return false; for (int i = 0; i < function_count; i++) { - if (strcmp(func_name, functions[i].name) == 0) { + if (strcmp(func_name.data, functions[i].name) == 0) { return true; } } return false; } -void execute_builtin_function(const char *func_name, ArgumentList *args) +void execute_builtin_function(const String func_name, ArgumentList *args) { execute_func_call(func_name, args); } @@ -227,7 +232,8 @@ static void ast_expr_to_stdrot_value(ASTNode *expr, StdrotValue *out) case VAR_CHAR: if (var->is_array) { out->type = STDROT_STRING; - out->val.str = (char *)var->value.array_data; + out->val.str.data = (char *)var->value.array_data; + out->val.str.len = (size_t)var->array_length; } else { out->type = STDROT_CHAR; out->val.c = (char)var->value.ivalue; @@ -235,7 +241,8 @@ static void ast_expr_to_stdrot_value(ASTNode *expr, StdrotValue *out) return; case VAR_STRING: out->type = STDROT_STRING; - out->val.str = (char *)var->value.array_data; + out->val.str.data = (char *)var->value.array_data; + out->val.str.len = var->value.strvalue.len; return; default: return; @@ -258,15 +265,15 @@ static void ast_expr_to_stdrot_value(ASTNode *expr, StdrotValue *out) } else if (is_expression(expr, VAR_DOUBLE)) { out->type = STDROT_DOUBLE; out->val.d = evaluate_expression_double(expr); - } else if (is_expression(expr, VAR_INT) || expr->type == NODE_ARRAY_ACCESS || expr->type == NODE_OPERATION || expr->type == NODE_UNARY_OPERATION) { + } else if (is_expression(expr, VAR_INT) || expr->type == NODE_ARRAY_ACCESS || expr->type == NODE_OPERATION || expr->type == NODE_UNARY_OPERATION || expr->type == NODE_STRUCT_ACCESS) { out->type = STDROT_INT; out->val.i = evaluate_expression_int(expr); } } -void execute_func_call(const char *func_name, ArgumentList *args) +void execute_func_call(const String func_name, ArgumentList *args) { - if (!func_name || !functions) { + if (!func_name.data || !functions) { yyerror("Function not found"); return; } @@ -274,7 +281,7 @@ void execute_func_call(const char *func_name, ArgumentList *args) /* Look up function in the registry */ StdrotEntry *entry = NULL; for (int i = 0; i < function_count; i++) { - if (strcmp(functions[i].name, func_name) == 0) { + if (strcmp(functions[i].name, func_name.data) == 0) { entry = &functions[i]; break; } @@ -286,7 +293,7 @@ void execute_func_call(const char *func_name, ArgumentList *args) } /* Set execution context - get line number from first argument node */ - g_exec_context.function_name = func_name; + g_exec_context.function_name.data = func_name.data; g_exec_context.line_number = 0; if (args && args->expr && args->expr->line_number > 0) { g_exec_context.line_number = args->expr->line_number; @@ -312,7 +319,7 @@ void execute_func_call(const char *func_name, ArgumentList *args) /* Generic write-back: if first arg is an identifier and function returned a value, * write the returned value back to that variable. */ if (result.type != STDROT_NONE && args && args->expr && args->expr->type == NODE_IDENTIFIER) { - const char *name = args->expr->data.name; + const String name = args->expr->data.name; Variable *var = get_variable(name); if (var) { switch (result.type) { @@ -332,12 +339,19 @@ void execute_func_call(const char *func_name, ArgumentList *args) set_int_variable(name, result.val.c, var->modifiers); break; case STDROT_STRING: - if (var->is_array && var->array_length > 0 && result.val.str) { + if (var->is_array && var->var_type == VAR_CHAR + && var->array_length > 0) { char *dst = (char *)var->value.array_data; - /* Avoid overlapping copy */ - if (dst != result.val.str) { - strncpy(dst, result.val.str, var->array_length - 1); - dst[var->array_length - 1] = '\0'; + + if (result.val.str.data && result.val.str.data != dst) { + + size_t max = var->array_length - 1; + size_t n = result.val.str.len; + + if (n > max) n = max; + + memcpy(dst, result.val.str.data, n); + dst[n] = '\0'; } } break; @@ -353,78 +367,122 @@ void execute_func_call(const char *func_name, ArgumentList *args) /* ── Stub functions (thin wrappers that forward to .so) ────────────────────── */ -void yapping(const char* format, ...) +void yapping(const String format, ...) { + String s = { + .data = "v_yapping", + .len = sizeof("v_yapping") - 1 + }; va_list ap; va_start(ap, format); - void (*fn)(const char *, va_list) = (void (*)(const char *, va_list))stdrot_lookup_symbol("v_yapping"); + void (*fn)(const String, va_list) = (void (*)(const String, va_list))stdrot_lookup_symbol(s); if (fn) fn(format, ap); va_end(ap); } -void yappin(const char* format, ...) +void yappin(const String format, ...) { + String s = { + .data = "v_yappin", + .len = sizeof("v_yappin") - 1 + }; va_list ap; va_start(ap, format); - void (*fn)(const char *, va_list) = (void (*)(const char *, va_list))stdrot_lookup_symbol("v_yappin"); + void (*fn)(const String , va_list) = (void (*)(const String , va_list))stdrot_lookup_symbol(s); if (fn) fn(format, ap); va_end(ap); } -void baka(const char* format, ...) +void baka(const String format, ...) { + String s = { + .data = "v_baka", + .len = sizeof("v_baka") - 1 + }; va_list ap; va_start(ap, format); - void (*fn)(const char *, va_list) = (void (*)(const char *, va_list))stdrot_lookup_symbol("v_baka"); + void (*fn)(const String , va_list) = (void (*)(const String , va_list))stdrot_lookup_symbol(s); if (fn) fn(format, ap); va_end(ap); } void ragequit(int exit_code) { - void (*fn)(int) = (void (*)(int))stdrot_lookup_symbol("ragequit"); + String s = { + .data = "ragequit", + .len = sizeof("ragequit") - 1 + }; + void (*fn)(int) = (void (*)(int))stdrot_lookup_symbol(s); if (fn) fn(exit_code); } void chill(unsigned int seconds) { - void (*fn)(unsigned int) = (void (*)(unsigned int))stdrot_lookup_symbol("chill"); + String s = { + .data = "chill", + .len = sizeof("chill") - 1 + }; + void (*fn)(unsigned int) = (void (*)(unsigned int))stdrot_lookup_symbol(s); if (fn) fn(seconds); } char slorp_char(char chr) { - char (*fn)(char) = (char (*)(char))stdrot_lookup_symbol("slorp_char"); + String s = { + .data = "slorp_char", + .len = sizeof("slorp_char") - 1 + }; + char (*fn)(char) = (char (*)(char))stdrot_lookup_symbol(s); return fn ? fn(chr) : chr; } -char *slorp_string(char *string, size_t size) +String slorp_string(String string, size_t size) { - char *(*fn)(char *, size_t) = (char *(*)(char *, size_t))stdrot_lookup_symbol("slorp_string"); + String s = { + .data = "slorp_string", + .len = sizeof("slorp_string") - 1 + }; + String (*fn)(String , size_t) = (String (*)(String , size_t))stdrot_lookup_symbol(s); return fn ? fn(string, size) : string; } int slorp_int(int val) { - int (*fn)(int) = (int (*)(int))stdrot_lookup_symbol("slorp_int"); + String s = { + .data = "slorp_int", + .len = sizeof("slorp_int") - 1 + }; + int (*fn)(int) = (int (*)(int))stdrot_lookup_symbol(s); return fn ? fn(val) : val; } short slorp_short(short val) { - short (*fn)(short) = (short (*)(short))stdrot_lookup_symbol("slorp_short"); + String s = { + .data = "slorp_short", + .len = sizeof("slorp_short") - 1 + }; + short (*fn)(short) = (short (*)(short))stdrot_lookup_symbol(s); return fn ? fn(val) : val; } float slorp_float(float var) { - float (*fn)(float) = (float (*)(float))stdrot_lookup_symbol("slorp_float"); + String s = { + .data = "slorp_float", + .len = sizeof("slorp_float") - 1 + }; + float (*fn)(float) = (float (*)(float))stdrot_lookup_symbol(s); return fn ? fn(var) : var; } double slorp_double(double var) { - double (*fn)(double) = (double (*)(double))stdrot_lookup_symbol("slorp_double"); + String s = { + .data = "slorp_double", + .len = sizeof("slorp_double") - 1 + }; + double (*fn)(double) = (double (*)(double))stdrot_lookup_symbol(s); return fn ? fn(var) : var; } diff --git a/stdrot.h b/stdrot.h index 24f2764..cfda89d 100644 --- a/stdrot.h +++ b/stdrot.h @@ -13,6 +13,7 @@ #include "ast.h" #include "stdrot/stdrot_api.h" +#include "lib/string_value.h" #include #include @@ -24,18 +25,18 @@ void stdrot_load(void); void stdrot_unload(void); /* ── Runtime query / dispatch ────────────────────────────────────────────── */ -bool is_builtin_function(const char *func_name); -void execute_builtin_function(const char *func_name, ArgumentList *args); -void execute_func_call(const char *func_name, ArgumentList *args); +bool is_builtin_function(const String func_name); +void execute_builtin_function(const String func_name, ArgumentList *args); +void execute_func_call(const String func_name, ArgumentList *args); /* ── Stub functions (forward declarations for use by ast.c) ──────────────── */ -void yapping(const char* format, ...); -void yappin(const char* format, ...); -void baka(const char* format, ...); +void yapping(const String format, ...); +void yappin(const String format, ...); +void baka(const String format, ...); void ragequit(int exit_code); void chill(unsigned int seconds); char slorp_char(char chr); -char *slorp_string(char *string, size_t size); +String slorp_string(String string, size_t size); int slorp_int(int val); short slorp_short(short val); float slorp_float(float var); diff --git a/stdrot/baka.c b/stdrot/baka.c index 3dc7b2a..933b374 100644 --- a/stdrot/baka.c +++ b/stdrot/baka.c @@ -51,9 +51,12 @@ static void process_baka_format(const char *format, const StdrotValue *args, int if (spec == '\0') break; char specifier[32]; - int length = format - start + 1; - if (length >= (int)sizeof(specifier)) length = sizeof(specifier) - 1; - strncpy(specifier, start, length); + size_t length = (size_t)(format - start + 1); + + if (length >= sizeof(specifier)) + length = sizeof(specifier) - 1; + + memcpy(specifier, start, length); specifier[length] = '\0'; const StdrotValue *arg = &args[arg_idx]; @@ -84,8 +87,8 @@ static void process_baka_format(const char *format, const StdrotValue *args, int buffer_offset += snprintf(buffer + buffer_offset, sizeof(buffer) - buffer_offset, "%c", arg->val.i); } } else if (spec == 's') { - if (arg->type == STDROT_STRING && arg->val.str) { - buffer_offset += snprintf(buffer + buffer_offset, sizeof(buffer) - buffer_offset, "%s", arg->val.str); + if (arg->type == STDROT_STRING && arg->val.str.data) { + buffer_offset += snprintf(buffer + buffer_offset, sizeof(buffer) - buffer_offset, "%s", arg->val.str.data); } } @@ -103,8 +106,8 @@ static void process_baka_format(const char *format, const StdrotValue *args, int static StdrotValue stdrot_baka(StdrotValue *args, int arg_count) { - if (arg_count > 0 && args[0].type == STDROT_STRING && args[0].val.str) { - process_baka_format(args[0].val.str, &args[1], arg_count - 1); + if (arg_count > 0 && args[0].type == STDROT_STRING && args[0].val.str.data) { + process_baka_format(args[0].val.str.data, &args[1], arg_count - 1); } return (StdrotValue){STDROT_NONE, {0}}; } diff --git a/stdrot/bet.c b/stdrot/bet.c index 8ce5d16..439269c 100644 --- a/stdrot/bet.c +++ b/stdrot/bet.c @@ -37,7 +37,7 @@ StdrotValue stdrot_bet(StdrotValue *args, int argc) { // Optional second argument is the message const char *message = NULL; if (argc > 1 && args[1].type == STDROT_STRING) { - message = args[1].val.str; + message = args[1].val.str.data; } bet(condition, message); diff --git a/stdrot/slorp.c b/stdrot/slorp.c index 5545855..d558962 100644 --- a/stdrot/slorp.c +++ b/stdrot/slorp.c @@ -134,10 +134,10 @@ static StdrotValue stdrot_slorp(StdrotValue *args, int argc) out.val.c = slorp_char(args[0].val.c); break; case STDROT_STRING: - if (args[0].val.str) { - size_t size = strlen(args[0].val.str); + if (args[0].val.str.data) { + size_t size = args[0].val.str.len; if (size == 0) size = 1024; - slorp_string(args[0].val.str, size); + slorp_string(args[0].val.str.data, size); out.type = STDROT_STRING; out.val.str = args[0].val.str; } diff --git a/stdrot/stdrot_api.h b/stdrot/stdrot_api.h index 53113f9..3bcc42c 100644 --- a/stdrot/stdrot_api.h +++ b/stdrot/stdrot_api.h @@ -27,6 +27,7 @@ #ifndef STDROT_API_H #define STDROT_API_H +#include "../lib/string_value.h" #include #include @@ -36,8 +37,8 @@ */ typedef struct { int line_number; - const char *function_name; - const char *condition_text; + String function_name; + String condition_text; } ExecutionContext; extern ExecutionContext g_exec_context; @@ -64,7 +65,7 @@ typedef struct { short s; bool b; char c; - char *str; + String str; } val; } StdrotValue; diff --git a/stdrot/yapping.c b/stdrot/yapping.c index 2295d4c..2ad4198 100644 --- a/stdrot/yapping.c +++ b/stdrot/yapping.c @@ -64,9 +64,12 @@ static void process_yapping_format(const char *format, const StdrotValue *args, if (spec == '\0') break; char specifier[32]; - int length = format - start + 1; - if (length >= (int)sizeof(specifier)) length = sizeof(specifier) - 1; - strncpy(specifier, start, length); + size_t length = (size_t)(format - start + 1); + + if (length >= sizeof(specifier)) + length = sizeof(specifier) - 1; + + memcpy(specifier, start, length); specifier[length] = '\0'; const StdrotValue *arg = &args[arg_idx]; @@ -99,7 +102,7 @@ static void process_yapping_format(const char *format, const StdrotValue *args, } } else if (spec == 's') { if (arg->type == STDROT_STRING) { - buffer_offset += snprintf(buffer + buffer_offset, sizeof(buffer) - buffer_offset, "%s", arg->val.str); + buffer_offset += snprintf(buffer + buffer_offset, sizeof(buffer) - buffer_offset, "%s", arg->val.str.data); } } @@ -123,7 +126,7 @@ static void process_yapping_format(const char *format, const StdrotValue *args, static StdrotValue stdrot_yapping(StdrotValue *args, int arg_count) { if (arg_count > 0 && args[0].type == STDROT_STRING) { - process_yapping_format(args[0].val.str, &args[1], arg_count - 1, 1); + process_yapping_format(args[0].val.str.data, &args[1], arg_count - 1, 1); } return (StdrotValue){STDROT_NONE, {0}}; } @@ -132,7 +135,7 @@ static StdrotValue stdrot_yapping(StdrotValue *args, int arg_count) static StdrotValue stdrot_yappin(StdrotValue *args, int arg_count) { if (arg_count > 0 && args[0].type == STDROT_STRING) { - process_yapping_format(args[0].val.str, &args[1], arg_count - 1, 0); + process_yapping_format(args[0].val.str.data, &args[1], arg_count - 1, 0); } return (StdrotValue){STDROT_NONE, {0}}; } diff --git a/test_cases/gang.brainrot b/test_cases/gang.brainrot new file mode 100644 index 0000000..62110c3 --- /dev/null +++ b/test_cases/gang.brainrot @@ -0,0 +1,17 @@ +🚽 Example: 2D point struct +gang Point { + rizz x; + rizz y; + chad magnitude; +}; + +skibidi main { + gang Point p; + p.x = 3; + p.y = 4; + p.magnitude = 5.0; + yapping("Point: %d %d %.1f", p.x, p.y, p.magnitude); + + gang Point q = {10, 20, 0.0}; + yapping("Q: %d %d %.1f", q.x, q.y, q.magnitude); +} diff --git a/tests/expected_results.json b/tests/expected_results.json index bdc6d03..bc7e645 100644 --- a/tests/expected_results.json +++ b/tests/expected_results.json @@ -72,5 +72,6 @@ "semantic_error_pointer_deref": "Error: Cannot dereference a non-pointer expression at line 3", "bet": "Assertion passed!\n", "bet_int": "x is positive\n", - "bet_fail": "Error: bet: assertion failed at line 2: this assertion must fail" + "bet_fail": "Error: bet: assertion failed at line 2: this assertion must fail", + "gang": "Point: 3 4 5.0\nQ: 10 20 0.0\n" } diff --git a/visitor.c b/visitor.c index 85fbdc3..f5f5acd 100644 --- a/visitor.c +++ b/visitor.c @@ -200,6 +200,17 @@ void ast_accept(ASTNode *node, Visitor *visitor) { if (visitor->visit_error_statement) visitor->visit_error_statement(visitor, node); break; + + case NODE_STRUCT_DEF: + break; + + case NODE_STRUCT_ACCESS: + /* Visit the object sub-expression */ + if (node->data.struct_access.object) + ast_accept(node->data.struct_access.object, visitor); + /* No dedicated visitor hook needed — access is handled by + evaluate_struct_member_address at evaluation time. */ + break; default: // Unknown node type - just continue