Skip to content

Commit

Permalink
Refactor AST structure & fix parser memory crash for array operations
Browse files Browse the repository at this point in the history
- Refactored `ASTNode` to separate fields for distinct node types, preventing memory conflicts.
- Updated parser functions to use correct `ASTNodeType` for declarations and assignments.
- Fixed crash in `print_ast` caused by uninitialized or overlapping fields.
- Ensured proper memory initialization using calloc for zero-initialized nodes.
- Enhanced debugging and error handling for better resilience against invalid memory access.

#175
  • Loading branch information
KennyOliver committed Jan 7, 2025
1 parent 133ac17 commit e2c4590
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 96 deletions.
9 changes: 5 additions & 4 deletions src/interpreter/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ InterpretResult interpret_constant(ASTNode *node, Environment *env) {
}

// Extract the constant name and value
char *const_name = strdup(node->assignment.variable_name);
char *const_name = strdup(node->const_declaration.constant_name);
InterpretResult value_res = interpret_node(node->assignment.rhs, env);
LiteralValue const_value = value_res.value;

Expand All @@ -240,7 +240,7 @@ InterpretResult interpret_constant(ASTNode *node, Environment *env) {
}

InterpretResult interpret_assignment(ASTNode *node, Environment *env) {
if (!node->assignment.variable_name) {
if (!node->var_declaration.variable_name) {
debug_print_int("Assignment variable name missing for node type: %d\n",
node->type);
return make_result((LiteralValue){.type = TYPE_ERROR}, false, false);
Expand All @@ -262,7 +262,7 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) {
while (target_env) {
for (size_t i = 0; i < target_env->variable_count; i++) {
if (strcmp(target_env->variables[i].variable_name,
node->assignment.variable_name) == 0) {
node->var_declaration.variable_name) == 0) {
existing_var = &target_env->variables[i];
break;
}
Expand All @@ -277,7 +277,8 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) {
Environment *scope_to_modify = existing_var ? target_env : env;

// Create a new variable instance
Variable new_var = {.variable_name = strdup(node->assignment.variable_name),
Variable new_var = {.variable_name =
strdup(node->var_declaration.variable_name),
.value = new_value,
.is_constant = false};

Expand Down
10 changes: 5 additions & 5 deletions src/interpreter/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,15 @@ ASTNode *copy_ast_node(ASTNode *node) {
// Deep copy based on node type
switch (node->type) {
case AST_ASSIGNMENT:
if (node->assignment.variable_name) {
new_node->assignment.variable_name =
strdup(node->assignment.variable_name);
if (!new_node->assignment.variable_name) {
if (node->var_declaration.variable_name) {
new_node->var_declaration.variable_name =
strdup(node->var_declaration.variable_name);
if (!new_node->var_declaration.variable_name) {
fatal_error("Memory allocation failed for variable name in "
"assignment.\n");
}
} else {
new_node->assignment.variable_name = NULL;
new_node->var_declaration.variable_name = NULL;
}
new_node->assignment.rhs = copy_ast_node(node->assignment.rhs);
break;
Expand Down
78 changes: 45 additions & 33 deletions src/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,74 +84,86 @@ ASTNode *parse_expression_statement(ParserState *state) {
}

ASTNode *parse_declaration(ParserState *state, ASTNodeType type) {
// Parse keyword (`let` or `const`)
if (type == AST_CONST_DECLARATION) {
debug_print_par("Starting constant declaration parse\n");
expect_token(state, TOKEN_KEYWORD, "Expected `const` keyword");
} else {
} else if (type == AST_VAR_DECLARATION) {
debug_print_par("Starting variable declaration parse\n");
expect_token(state, TOKEN_KEYWORD, "Expected `let` keyword");
} else {
parser_error("Unknown declaration type", get_current_token(state));
}

// Parse variable/constant name
Token *name = get_current_token(state);
if (type == AST_CONST_DECLARATION) {
expect_token(state, TOKEN_IDENTIFIER, "Expected constant name");
expect_token(state, TOKEN_OPERATOR, "Expected `=` after constant name");
} else {
expect_token(state, TOKEN_IDENTIFIER, "Expected variable name");
expect_token(state, TOKEN_OPERATOR, "Expected `=` after variable name");
if (name->type != TOKEN_IDENTIFIER && name->type != TOKEN_FUNCTION_NAME) {
parser_error("Expected name in declaration", name);
}

// Create AST node
ASTNode *node = malloc(sizeof(ASTNode));
if (!node) {
parser_error("Memory allocation failed", get_current_token(state));
char *decl_name = strdup(name->lexeme);
if (!decl_name) {
parser_error("Memory allocation failed for declaration name", name);
}
node->type = type;
advance_token(state); // Consume name

// Create a deep copy of the variable/constant name
if (name && name->lexeme) {
node->assignment.variable_name = strdup(name->lexeme);
if (!node->assignment.variable_name) {
parser_error(AST_CONST_DECLARATION
? "Memory allocation failed for constant name"
: "Memory allocation failed for variable name",
name);
}
} else {
parser_error(AST_CONST_DECLARATION ? "Invalid constant name token"
: "Invalid variable name token",
name);
}
// Expect `=` operator
expect_token(state, TOKEN_OPERATOR, "Expected `=` after name");

node->assignment.lhs = NULL;
node->assignment.rhs = parse_expression(state);
node->next = NULL;
// Parse initializer expression
ASTNode *initializer = parse_expression(state);
if (!initializer) {
parser_error("Expected initializer expression",
get_current_token(state));
}

// Expect `;` delimiter
expect_token(state, TOKEN_DELIMITER,
AST_CONST_DECLARATION
type == AST_CONST_DECLARATION
? "Expected `;` after constant declaration"
: "Expected `;` after variable declaration");

// Create AST node based on type
ASTNode *node = malloc(sizeof(ASTNode));
if (!node) {
parser_error("Memory allocation failed for declaration node",
get_current_token(state));
}
node->type = type;
node->next = NULL;

if (type == AST_VAR_DECLARATION) {
node->var_declaration.variable_name = decl_name;
node->var_declaration.initializer = initializer;
} else if (type == AST_CONST_DECLARATION) {
node->const_declaration.constant_name = decl_name;
node->const_declaration.initializer = initializer;
}

return node;
}

ASTNode *parse_variable_declaration(ParserState *state) {
return parse_declaration(state, AST_ASSIGNMENT);
return parse_declaration(state, AST_VAR_DECLARATION);
}

ASTNode *parse_constant_declaration(ParserState *state) {
return parse_declaration(state, AST_CONST_DECLARATION);
}

ASTNode *create_variable_reference_node(char *name) {
ASTNode *node = malloc(sizeof(ASTNode));
ASTNode *node = calloc(
1, sizeof(ASTNode)); // use `calloc` instead of `malloc` to ensure
// all fields are initialized to zero (`NULL`)
if (!node) {
parser_error("Memory allocation failed for variable reference node",
NULL);
}
node->type = AST_VARIABLE_REFERENCE;
node->variable_name = strdup(name);
if (!node->variable_name) {
free(node);
parser_error("Memory allocation failed for variable name", NULL);
}
node->next = NULL;
return node;
}
Expand Down
55 changes: 29 additions & 26 deletions src/parser/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ void free_ast(ASTNode *node) {

switch (node->type) {
case AST_ASSIGNMENT:
free(node->assignment.variable_name);
// free(node->assignment.variable_name);
free_ast(node->assignment.lhs);
free_ast(node->assignment.rhs);
break;
Expand Down Expand Up @@ -170,11 +170,29 @@ void print_ast(ASTNode *node, int depth) {
while (node != NULL) {
print_indent(depth);
switch (node->type) {
case AST_VAR_DECLARATION:
printf("Variable Declaration: %s\n",
node->var_declaration.variable_name);
if (node->var_declaration.initializer) {
print_indent(depth + 1);
printf("Initializer:\n");
print_ast(node->var_declaration.initializer, depth + 2);
}
break;

case AST_CONST_DECLARATION:
printf("Constant Declaration: %s\n",
node->const_declaration.constant_name);
if (node->const_declaration.initializer) {
print_indent(depth + 1);
printf("Initializer:\n");
print_ast(node->const_declaration.initializer, depth + 2);
}
break;

case AST_ASSIGNMENT:
printf("Assignment:\n");
print_indent(depth + 1);
printf("Variable Name: %s\n", node->assignment.variable_name);
print_indent(depth + 1);
printf("LHS:\n");
print_ast(node->assignment.lhs, depth + 2);
print_indent(depth + 1);
Expand Down Expand Up @@ -314,9 +332,14 @@ void print_ast(ASTNode *node, int depth) {
while (case_node != NULL) {
print_indent(depth + 2);
printf("Case:\n");
print_indent(depth + 3);
printf("Condition:\n");
print_ast(case_node->condition, depth + 4);
if (case_node->condition) {
print_indent(depth + 3);
printf("Condition:\n");
print_ast(case_node->condition, depth + 4);
} else {
print_indent(depth + 3);
printf("Condition: None (Default)\n");
}
print_indent(depth + 3);
printf("Body:\n");
print_ast(case_node->body, depth + 4);
Expand All @@ -342,26 +365,6 @@ void print_ast(ASTNode *node, int depth) {
print_ast(node->ternary.false_expr, depth + 2);
break;

case AST_VAR_DECLARATION:
printf("Variable Declaration: %s\n", node->variable_name);
if (node->assignment.rhs) { // Assuming variable declaration can
// have an initializer
print_indent(depth + 1);
printf("Initializer:\n");
print_ast(node->assignment.rhs, depth + 2);
}
break;

case AST_CONST_DECLARATION:
printf("Constant Declaration: %s\n", node->variable_name);
if (node->assignment.rhs) { // Assuming constant declaration can
// have an initializer
print_indent(depth + 1);
printf("Initializer:\n");
print_ast(node->assignment.rhs, depth + 2);
}
break;

case AST_TRY:
printf("Try Block:\n");
print_indent(depth + 1);
Expand Down
Loading

0 comments on commit e2c4590

Please sign in to comment.