From 1d620f21996ece90a942e48f63be9a7282fca273 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 09:57:09 +0000 Subject: [PATCH 01/62] Update 7_try_catch.flv #179 --- src/tests/7_try_catch.flv | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tests/7_try_catch.flv b/src/tests/7_try_catch.flv index 7d88e9f..e5b85f8 100644 --- a/src/tests/7_try_catch.flv +++ b/src/tests/7_try_catch.flv @@ -4,3 +4,12 @@ try { } rescue { serve("Caught an error: Recipe needs improvement."); } + +try { + int("abc"); + serve("This won't run!"); +} rescue { + serve("Runtime error: Can't cast string `abc` to int."); +} finish { + serve("This always executes!"); +} From 2a92053cb69958ce0ebd20f2d35a5a0029299708 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 09:58:06 +0000 Subject: [PATCH 02/62] Update language_design.md #179 --- docs/language_design.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/language_design.md b/docs/language_design.md index 9b8c10e..2b08ba8 100644 --- a/docs/language_design.md +++ b/docs/language_design.md @@ -33,7 +33,8 @@ This `docs/` page details the core design of FlavorLang's syntax, the various da | `create` | Define a function | Creates a reusable block of logic. | ✅ | | `deliver` | Return statement | Returns a value and stops function execution. | ✅ | | `try` | Try block | Executes code that might fail. | ❌ | -| `crumbs` | Catch block | Handles errors during execution. | ❌ | +| `rescue` | Catch block | Handles errors during execution. | ❌ | +| `finish` | Finally block | Optional cleanup & always runs. | ❌ | | `burn` | Force exit or raise an error | Stops execution immediately with a message. | ✅ | | `serve` | Print or output | Outputs a value or message immediately. | ✅ | | `sample` | Input from console | Reads user input. | ✅ | From 4564c086702fd01ea3c1c140034538ff4bde79f1 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:16:47 +0000 Subject: [PATCH 03/62] Add: `AST_TRY`, `AST_RESCUE`, `AST_FINISH` #179 --- src/shared/ast_types.h | 57 ++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/shared/ast_types.h b/src/shared/ast_types.h index e4593fc..0217992 100644 --- a/src/shared/ast_types.h +++ b/src/shared/ast_types.h @@ -23,7 +23,10 @@ typedef enum { AST_BREAK, AST_TERNARY, AST_VARIABLE, - AST_CONSTANT + AST_CONSTANT, + AST_TRY, + AST_RESCUE, + AST_FINISH } ASTNodeType; // Literal Node @@ -117,6 +120,20 @@ typedef struct { struct ASTNode *false_expr; } ASTTernary; +// AST Rescue Node (optional error object) +typedef struct ASTCatchNode { + char *error_variable; // Optional: Variable name to hold the error object + struct ASTNode *body; // Body of the rescue block + struct ASTCatchNode *next; // For multiple rescue clauses (future feature) +} ASTCatchNode; + +// AST Try Node +typedef struct { + struct ASTNode *try_block; // Code within the try block + ASTCatchNode *catch_blocks; // Linked list of rescue blocks + struct ASTNode *finally_block; // Optional finish block +} ASTTry; + // AST Node Structure typedef struct ASTNode { ASTNodeType type; @@ -127,32 +144,18 @@ typedef struct ASTNode { struct ASTNode *value; } assignment; - // Literal - LiteralNode literal; - - // Unary operation - ASTUnaryOp unary_op; - - // Binary operation - ASTBinaryOp binary_op; - - // Conditional - ASTConditional conditional; - - // Switch - ASTSwitch switch_case; - - // While loop - ASTWhileLoop while_loop; - - // For loop - ASTForLoop for_loop; - - // Function - ASTFunctionCall function_call; - - // Ternary - ASTTernary ternary; + LiteralNode literal; // Literal + ASTUnaryOp unary_op; // Unary operation + ASTBinaryOp binary_op; // Binary operation + ASTConditional conditional; // Conditional + ASTSwitch switch_case; // Switch + ASTWhileLoop while_loop; // While loop + ASTForLoop for_loop; // For loop + ASTFunctionCall function_call; // Function + ASTTernary ternary; // Ternary + ASTTry try_block; // Try block + ASTCatchNode catch_block; // Catch block + ASTNode finally_block; // Finally block // Variable char *variable_name; From b946fc39b97220512a9ac6965282944bea01fdcd Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:32:02 +0000 Subject: [PATCH 04/62] Update: Lexer now recognizes `finish` keyword #179 --- src/lexer/keywords.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lexer/keywords.c b/src/lexer/keywords.c index 9788b62..684c275 100644 --- a/src/lexer/keywords.c +++ b/src/lexer/keywords.c @@ -16,8 +16,9 @@ const char *KEYWORDS[] = { "break", // break "create", // function "deliver", // return - "try", // try - "rescue", // catch + "try", // try block + "rescue", // catch block + "finish", // finally block "plate", // write file "garnish", // append file "taste", // read file From b6e95784fd682cde54baf73ba17b4ba81e711f94 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:32:22 +0000 Subject: [PATCH 05/62] Update: Parser now handles `try`-`rescue`-`finish` #179 --- src/parser/parser.c | 95 ++++++++++++++++++++++++++++++++++++++++++ src/parser/parser.h | 3 ++ src/shared/ast_types.h | 7 ++-- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/parser/parser.c b/src/parser/parser.c index 3b876da..ace11ce 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -50,6 +50,8 @@ ASTNode *parse_statement(ParserState *state) { return parse_break_statement(state); if (match_token(state, "deliver")) return parse_function_return(state); + if (match_token(state, "try")) + return parse_try_block(state); // Handle function calls if (token->type == TOKEN_FUNCTION_NAME) { @@ -820,3 +822,96 @@ ASTNode *parse_function_call(ParserState *state) { node->next = NULL; return node; } + +ASTNode *parse_try_block(ParserState *state) { + expect_token(state, TOKEN_KEYWORD, "Expected `try` keyword"); + expect_token(state, TOKEN_BRACE_OPEN, "Expected `{` to start try block"); + + // Parse `try` block statements + ASTNode *try_body = parse_block(state); + expect_token(state, TOKEN_BRACE_CLOSE, "Expected `}` to end try block"); + + // Initialize try structure + ASTTry try_block = {0}; + try_block.try_block = try_body; + try_block.catch_blocks = NULL; // initialize `rescue` (catch) blocks + try_block.finally_block = NULL; // initialize finish block + + // Parse optional `catch` blocks + while (match_token(state, "rescue")) { + ASTCatchNode *catch = parse_catch_block(state); + if (!try_block.catch_blocks) { + try_block.catch_blocks = catch; + } else { + // For future: Handle multiple `rescue` (catch) blocks + ASTCatchNode *last = try_block.catch_blocks; + while (last->next) { + last = last->next; + } + last->next = catch; + } + } + + // Parse optional `finish` block + if (match_token(state, "finish")) { + ASTNode *finally_body = parse_finally_block(state); + try_block.finally_block = finally_body; + } + + ASTNode *node = malloc(sizeof(ASTNode)); + if (!node) { + parser_error("Memory allocation failed for `try` block", + get_current_token(state)); + } + node->type = AST_TRY; + node->try_block = try_block; + node->next = NULL; + + return node; +} + +ASTCatchNode *parse_catch_block(ParserState *state) { + expect_token(state, TOKEN_KEYWORD, "Expected `rescue` keyword"); + + // Optional: Parse error object variable + char *error_var = NULL; + Token *current = get_current_token(state); + if (current->type == TOKEN_PAREN_OPEN) { + advance_token(state); // consume `(` + Token *var_token = get_current_token(state); + if (var_token->type == TOKEN_IDENTIFIER) { + error_var = strdup(var_token->lexeme); + if (!error_var) { + parser_error("Memory allocation failed for error variable name", + var_token); + } + advance_token(state); // consume variable name + } + expect_token(state, TOKEN_PAREN_CLOSE, + "Expected `)` after error variable"); + } + + expect_token(state, TOKEN_BRACE_OPEN, "Expected `{` to start rescue block"); + + // Parse `rescue` (catch) block statements + ASTNode *catch_body = parse_block(state); + expect_token(state, TOKEN_BRACE_CLOSE, "Expected `}` to end rescue block"); + + ASTCatchNode *catch_node = malloc(sizeof(ASTCatchNode)); + if (!catch_node) { + parser_error("Memory allocation failed for rescue block", current); + } + catch_node->error_variable = error_var; + catch_node->body = catch_body; + catch_node->next = NULL; + + return catch_node; +} + +ASTNode *parse_finally_block(ParserState *state) { + expect_token(state, TOKEN_KEYWORD, "Expected `finish` keyword"); + expect_token(state, TOKEN_BRACE_OPEN, "Expected `{` to start finish block"); + ASTNode *finally_body = parse_block(state); + expect_token(state, TOKEN_BRACE_CLOSE, "Expected `}` to end finish block"); + return finally_body; +} diff --git a/src/parser/parser.h b/src/parser/parser.h index 2c669b8..6d0a5d5 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -25,6 +25,9 @@ ASTNode *parse_switch_block(ParserState *state); ASTNode *parse_function_declaration(ParserState *state); ASTNode *parse_function_call(ParserState *state); ASTNode *parse_function_return(ParserState *state); +ASTNode *parse_try_block(ParserState *state); +ASTCatchNode *parse_catch_block(ParserState *state); +ASTNode *parse_finally_block(ParserState *state); // Expression parsing ASTNode *parse_expression(ParserState *state); diff --git a/src/shared/ast_types.h b/src/shared/ast_types.h index 0217992..9b1344e 100644 --- a/src/shared/ast_types.h +++ b/src/shared/ast_types.h @@ -153,9 +153,10 @@ typedef struct ASTNode { ASTForLoop for_loop; // For loop ASTFunctionCall function_call; // Function ASTTernary ternary; // Ternary - ASTTry try_block; // Try block - ASTCatchNode catch_block; // Catch block - ASTNode finally_block; // Finally block + + ASTTry try_block; // Try Block + // ASTCatchNode *catch_block; // Rescue Block + // struct ASTNode *finally_block; // Finish Block // Variable char *variable_name; From e2bb6abccb4843f8f001f52341354ea332cf8604 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:43:14 +0000 Subject: [PATCH 06/62] Update: `InterpretResult` now has `is_error` field #179 --- src/interpreter/interpreter_types.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/interpreter/interpreter_types.h b/src/interpreter/interpreter_types.h index 99cf9ea..66b5ba9 100644 --- a/src/interpreter/interpreter_types.h +++ b/src/interpreter/interpreter_types.h @@ -59,9 +59,10 @@ typedef struct { } Environment; typedef struct { - LiteralValue value; // The result of interpreting a node + LiteralValue value; // Result of interpreting a node bool did_return; // True if this node caused a function return to bubble up - bool did_break; + bool did_break; // True if this node caused a loop break to bubble up + bool is_error; // True if this node caused an error } InterpretResult; #endif From 2260fc4e29fb6dd07a3ef5f53f1643fe743f6fa3 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:44:32 +0000 Subject: [PATCH 07/62] Add: `raise_error` to replace `error_interpreter` #179 --- src/interpreter/utils.c | 26 ++++++++++++++++++++------ src/interpreter/utils.h | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 915c6a8..78ad73f 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -1,17 +1,31 @@ #include "utils.h" -void error_interpreter(const char *format, ...) { +InterpretResult raise_error(const char *format, ...) { + char error_message[1024]; va_list args; va_start(args, format); - printf("\033[31m"); // red text color printf("Error: "); - vprintf(format, args); + vsnprintf(error_message, sizeof(error_message), format, args); printf("\033[0m\n"); // reset text color - va_end(args); - fflush(stdout); - exit(1); + + // Create an error LiteralValue + LiteralValue error_value; + error_value.type = TYPE_ERROR; + error_value.data.string = strdup(error_message); + + if (!error_value.data.string) { + fprintf(stderr, + "Error: Failed to allocate memory for error message.\n"); + exit(1); + } + + InterpretResult res = {.value = error_value, + .did_return = false, + .did_break = false, + .is_error = true}; + return res; } void initialize_builtin_function(Environment *env, const char *name) { diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h index 4c3dfe0..8de157a 100644 --- a/src/interpreter/utils.h +++ b/src/interpreter/utils.h @@ -14,7 +14,7 @@ void init_environment(Environment *env); // Free the environment void free_environment(Environment *env); -void error_interpreter(const char *format, ...); +InterpretResult raise_error(const char *format, ...); void free_parameter_list(ASTFunctionParameter *head); ASTFunctionParameter * From 59312a3a8119ed2102af85b545b61eaaf3a72ae4 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:47:30 +0000 Subject: [PATCH 08/62] Refactor: `builtin_error` #179 --- src/interpreter/builtins.c | 9 ++++----- src/interpreter/builtins.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index fa26198..3074ba4 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -262,7 +262,7 @@ LiteralValue builtin_output(ASTNode *node, Environment *env) { } // Built-in `burn()` function to raise errors -LiteralValue builtin_error(ASTNode *node, Environment *env) { +InterpretResult builtin_error(ASTNode *node, Environment *env) { ASTNode *arg_node = node->function_call.arguments; char error_message[512] = "Error raised by burn(): "; @@ -298,7 +298,7 @@ LiteralValue builtin_error(ASTNode *node, Environment *env) { sizeof(error_message) - strlen(error_message) - 1); break; default: - strncat(error_message, "Unknown literal type in b`urn()`.", + strncat(error_message, "Unknown literal type in `burn()`.", sizeof(error_message) - strlen(error_message) - 1); break; } @@ -311,9 +311,8 @@ LiteralValue builtin_error(ASTNode *node, Environment *env) { arg_node = arg_node->next; } - error_interpreter(error_message); - - return (LiteralValue){.type = TYPE_ERROR}; // keep compiler happy + // Propagate the exception + return raise_error("%s", error_message); } bool is_valid_int(const char *str, INT_SIZE *out_value) { diff --git a/src/interpreter/builtins.h b/src/interpreter/builtins.h index 84bbd06..3a7ea60 100644 --- a/src/interpreter/builtins.h +++ b/src/interpreter/builtins.h @@ -24,7 +24,7 @@ typedef struct { LiteralValue builtin_input(ASTNode *node, Environment *env); LiteralValue builtin_random(ASTNode *node, Environment *env); LiteralValue builtin_output(ASTNode *node, Environment *env); -LiteralValue builtin_error(ASTNode *node, Environment *env); +InterpretResult builtin_error(ASTNode *node, Environment *env); LiteralValue builtin_cast(ASTNode *node, Environment *env); LiteralValue builtin_time(); LiteralValue builtin_file_read(ASTNode *node, Environment *env); From 0aee1451aeeb9c78dd3ff794b38e4a5cb53c8ebf Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:00:02 +0000 Subject: [PATCH 09/62] In process of replacing `error_interpreter` with `raise_error` #179 --- src/interpreter/interpreter.c | 437 ++++++++++++++++++++++++---------- src/interpreter/interpreter.h | 8 +- 2 files changed, 321 insertions(+), 124 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 3580d25..fde7504 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -24,121 +24,139 @@ static InterpretResult make_result(LiteralValue val, bool did_return, InterpretResult interpret_node(ASTNode *node, Environment *env) { if (!node) { - fprintf(stderr, "Error: Attempt to interpret NULL node\n"); - return make_result((LiteralValue){.type = TYPE_ERROR}, false, false); + return make_result(create_default_value(), false, false); } debug_print_int("`interpret_node()` called\n"); + InterpretResult result = {0}; + switch (node->type) { case AST_LITERAL: debug_print_int("\tMatched: `AST_LITERAL`\n"); - return make_result(interpret_literal(node), false, false); + result = make_result(interpret_literal(node), false, false); + break; case AST_ASSIGNMENT: debug_print_int("\tMatched: `AST_ASSIGNMENT`\n"); - return interpret_assignment(node, env); + result = interpret_assignment(node, env); + break; case AST_UNARY_OP: debug_print_int("\tMatched: `AST_UNARY_OP`\n"); - { - LiteralValue unary_result = interpret_unary_op(node, env); - return make_result(unary_result, false, false); - } + result = interpret_unary_op(node, env); + break; case AST_BINARY_OP: debug_print_int("\tMatched: `AST_BINARY_OP`\n"); - return make_result(interpret_binary_op(node, env), false, false); + result = interpret_binary_op(node, env); + break; case AST_CONDITIONAL: { debug_print_int("\tMatched: `AST_CONDITIONAL`\n"); - InterpretResult cond_res = interpret_conditional(node, env); - if (cond_res.did_return || cond_res.did_break) { - return cond_res; - } - return make_result(cond_res.value, false, false); + result = interpret_conditional(node, env); + break; } case AST_FUNCTION_CALL: { debug_print_int("\tMatched: `AST_FUNCTION_CALL`\n"); - // interpret_function_call(...) returns a LiteralValue - LiteralValue fc_val = interpret_function_call(node, env); - return make_result(fc_val, false, false); + // interpret_function_call(...) returns an InterpretResult + result = interpret_function_call(node, env); + break; } case AST_FUNCTION_DECLARATION: { debug_print_int("\tMatched: `AST_FUNCTION_DECLARATION`\n"); interpret_function_declaration(node, env); // No direct return from a function declaration - return make_result(create_default_value(), false, false); + result = make_result(create_default_value(), false, false); + break; } case AST_FUNCTION_RETURN: { debug_print_int("\tMatched: `AST_FUNCTION_RETURN`\n"); - LiteralValue return_value = - interpret_node(node->assignment.value, env).value; - debug_print_int("Return value before returning: type=%d, value=%lld\n", - return_value.type, - (return_value.type == TYPE_INTEGER) - ? return_value.data.integer - : 0); - - // Return this value, but also set did_return = true - return make_result(return_value, true, false); + // Extract the return value + InterpretResult return_res = + interpret_node(node->assignment.value, env); + if (return_res.is_error) { + // Propagate the error + return return_res; + } + // Set the did_return flag + return_res.did_return = true; + return return_res; } case AST_WHILE_LOOP: { debug_print_int("\tMatched: `AST_WHILE_LOOP`\n"); - InterpretResult loop_res = interpret_while_loop(node, env); - return loop_res; + result = interpret_while_loop(node, env); + break; } case AST_FOR_LOOP: { debug_print_int("\tMatched: `AST_FOR_LOOP`\n"); - LiteralValue for_loop = interpret_for_loop(node, env); - return make_result(for_loop, false, false); + InterpretResult loop_res = interpret_for_loop(node, env); + result = loop_res; + break; } case AST_VARIABLE: { debug_print_int("\tMatched: `AST_VARIABLE`\n"); LiteralValue var_val = interpret_variable(node, env); - return make_result(var_val, false, false); + if (var_val.type == TYPE_ERROR) { + result = make_result(var_val, false, false); + result.is_error = true; + } else { + result = make_result(var_val, false, false); + } + break; } case AST_CONSTANT: { debug_print_int("\tMatched: `AST_CONSTANT`\n"); - return interpret_constant(node, env); + result = interpret_constant(node, env); + break; } case AST_SWITCH: { debug_print_int("\tMatched: `AST_SWITCH`\n"); - InterpretResult switch_res = interpret_switch(node, env); - if (switch_res.did_return || switch_res.did_break) { - return switch_res; - } - return make_result(switch_res.value, false, false); + result = interpret_switch(node, env); + break; } case AST_BREAK: debug_print_int("\tMatched: `AST_BREAK`\n"); - return make_result(create_default_value(), false, true); + result = make_result(create_default_value(), false, true); + break; case AST_TERNARY: debug_print_int("\tMatched: `AST_TERNARY`\n"); - return interpret_ternary(node, env); // Delegate to helper + result = interpret_ternary(node, env); + break; + + case AST_TRY: + debug_print_int("\tMatched: `AST_TRY`\n"); + result = interpret_try(node, env); + break; default: error_interpreter("Unsupported `ASTNode` type.\n"); - return make_result(create_default_value(), false, - false); // keep compiler happy + result = make_result(create_default_value(), false, false); + break; } + + return result; } void interpret_program(ASTNode *program, Environment *env) { ASTNode *current = program; while (current) { debug_print_int("Executing top-level statement\n"); - interpret_node(current, env); + InterpretResult res = interpret_node(current, env); + if (res.is_error) { + fprintf(stderr, "Unhandled exception: %s\n", res.value.data.string); + exit(1); + } current = current->next; } } @@ -178,19 +196,18 @@ LiteralValue interpret_literal(ASTNode *node) { return value; } -LiteralValue interpret_variable(ASTNode *node, Environment *env) { +InterpretResult interpret_variable(ASTNode *node, Environment *env) { Variable *var = get_variable(env, node->variable_name); if (!var) { - error_interpreter("Undefined variable `%s`.\n", node->variable_name); + return raise_error("Undefined variable `%s`.\n", node->variable_name); } - return var->value; + return make_result(var->value, false, false); } InterpretResult interpret_constant(ASTNode *node, Environment *env) { if (node->type != AST_CONSTANT) { - fprintf(stderr, "Error: Invalid node type for constant declaration.\n"); - exit(1); + return raise_error("Invalid node type for constant declaration.\n"); } // Extract the constant name and value @@ -198,20 +215,20 @@ InterpretResult interpret_constant(ASTNode *node, Environment *env) { InterpretResult value_res = interpret_node(node->assignment.value, env); LiteralValue const_value = value_res.value; + if (value_res.is_error) { + return value_res; + } + // Check if the constant already exists Variable *existing_var = get_variable(env, const_name); if (existing_var) { - fprintf(stderr, "Error: Constant `%s` is already defined.\n", - const_name); - return make_result(create_default_value(), false, false); + return raise_error("Constant `%s` is already defined.\n", const_name); } // Add the constant to the environment - Variable new_var = { - .variable_name = strdup(const_name), - .value = const_value, - .is_constant = true // Mark as constant - }; + Variable new_var = {.variable_name = strdup(const_name), + .value = const_value, + .is_constant = true}; add_variable(env, new_var); return make_result(const_value, false, false); @@ -331,9 +348,9 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { return result; // unreachable } -LiteralValue interpret_unary_op(ASTNode *node, Environment *env) { +InterpretResult interpret_unary_op(ASTNode *node, Environment *env) { if (!node || node->type != AST_UNARY_OP) { - error_interpreter("Invalid unary operation node.\n"); + return raise_error("Invalid unary operation node."); } // Extract the operator and operand @@ -342,15 +359,25 @@ LiteralValue interpret_unary_op(ASTNode *node, Environment *env) { // Interpret the operand InterpretResult operand_r = interpret_node(operand_node, env); + if (operand_r.is_error) { + return operand_r; + } + LiteralValue operand = operand_r.value; - // If interpreting the operand caused a return, propagate it - if (operand_r.did_return) { - return operand; + // Evaluate the unary operator + LiteralValue result = evaluate_unary_operator(op, operand); + + if (result.type == TYPE_ERROR) { + InterpretResult res; + res.value = result; + res.did_return = false; + res.did_break = false; + res.is_error = true; + return res; } - // Evaluate the unary operator - return evaluate_unary_operator(op, operand); + return make_result(result, false, false); } LiteralValue evaluate_operator(const char *op, LiteralValue left, @@ -370,8 +397,11 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, if (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0) { // Ensure both operands are boolean if (left.type != TYPE_BOOLEAN || right.type != TYPE_BOOLEAN) { - error_interpreter( - "Logical operator `%s` requires boolean operands.\n", op); + LiteralValue error_val; + error_val.type = TYPE_ERROR; + error_val.data.string = strdup( + "Logical operators `&&` and `||` require boolean operands.\n"); + return error_val; } result.type = TYPE_BOOLEAN; @@ -423,13 +453,19 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.data.integer = (INT_SIZE)(left_value - right_value); } else if (strcmp(op, "/") == 0) { if (right_value == 0) { - error_interpreter("Division by zero\n"); + LiteralValue error_val; + error_val.type = TYPE_ERROR; + error_val.data.string = strdup("Division by zero\n"); + return error_val; } result.type = TYPE_FLOAT; result.data.floating_point = left_value / right_value; } else if (strcmp(op, "//") == 0) { if (right_value == 0) { - error_interpreter("Floor division by zero\n"); + LiteralValue error_val; + error_val.type = TYPE_ERROR; + error_val.data.string = strdup("Floor division by zero\n"); + return error_val; } if (result.type == TYPE_FLOAT) result.data.floating_point = floor(left_value / right_value); @@ -437,7 +473,10 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.data.integer = (INT_SIZE)(left_value / right_value); } else if (strcmp(op, "%") == 0) { if (right_value == 0) { - error_interpreter("Modulo by zero\n"); + LiteralValue error_val; + error_val.type = TYPE_ERROR; + error_val.data.string = strdup("Modulo by zero\n"); + return error_val; } if (result.type == TYPE_FLOAT) { result.data.floating_point = fmod(left_value, right_value); @@ -469,7 +508,10 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.type = TYPE_BOOLEAN; result.data.boolean = (left_value != right_value); } else { - error_interpreter("Unknown operator `%s`\n", op); + LiteralValue error_val; + error_val.type = TYPE_ERROR; + error_val.data.string = strdup("Unknown operator"); + return error_val; } return result; @@ -497,42 +539,46 @@ int is_right_associative(const char *op) { return (strcmp(op, "**") == 0); // exponentiation is right-associative } -LiteralValue interpret_binary_op(ASTNode *node, Environment *env) { +InterpretResult interpret_binary_op(ASTNode *node, Environment *env) { if (!node || node->type != AST_BINARY_OP) { - error_interpreter("Invalid binary operation node."); + return raise_error("Invalid binary operation node."); } // Interpret left operand InterpretResult left_r = interpret_node(node->binary_op.left, env); - LiteralValue left = left_r.value; - if (left.type == TYPE_ERROR) { - return left; // propagate errors + if (left_r.is_error) { + return left_r; } + LiteralValue left = left_r.value; // Interpret right operand InterpretResult right_r = interpret_node(node->binary_op.right, env); - LiteralValue right = right_r.value; - if (right.type == TYPE_ERROR) { - return right; // propagate errors + if (right_r.is_error) { + return right_r; } + LiteralValue right = right_r.value; // Evaluate based on operator const char *op = node->binary_op.operator; - // Check precedence dynamically if subnodes have binary ops - if (node->binary_op.right->type == AST_BINARY_OP) { - int op_prec = get_operator_precedence(op); - int right_prec = - get_operator_precedence(node->binary_op.right->binary_op.operator); + LiteralValue op_val = evaluate_operator(op, left, right); - // Reevaluate RHS if its operator has higher precedence - if (op_prec < right_prec || - (op_prec == right_prec && is_right_associative(op))) { - right = interpret_binary_op(node->binary_op.right, env); - } + if (op_val.type == TYPE_ERROR) { + InterpretResult res; + res.value = op_val; + res.did_return = false; + res.did_break = false; + res.is_error = true; + return res; } - return evaluate_operator(op, left, right); + // Return the operation result + InterpretResult res; + res.value = op_val; + res.did_return = false; + res.did_break = false; + res.is_error = false; + return res; } Variable *get_variable(Environment *env, const char *variable_name) { @@ -653,29 +699,29 @@ InterpretResult interpret_conditional(ASTNode *node, Environment *env) { debug_print_int("`interpret_conditional()` called\n"); if (!node) { // Error - return make_result((LiteralValue){.type = TYPE_ERROR}, false, false); + return raise_error("Invalid conditional node."); } - ASTNode *current_branch = node; + InterpretResult result = {0}; bool condition_met = false; + ASTNode *current_branch = node; + while (current_branch) { if (current_branch->conditional.condition) { InterpretResult cond_res = interpret_node(current_branch->conditional.condition, env); - if (cond_res.did_return || cond_res.did_break) { - // Bubble up immediately + if (cond_res.is_error) { + // Propagate the error return cond_res; } // Check for valid condition types if (cond_res.value.type != TYPE_INTEGER && cond_res.value.type != TYPE_BOOLEAN) { - fprintf(stderr, "Error: Condition expression must be boolean " - "or integer.\n"); - return make_result((LiteralValue){.type = TYPE_ERROR}, false, - false); + return raise_error( + "Condition expression must be boolean or integer.\n"); } bool condition_true = false; @@ -691,7 +737,8 @@ InterpretResult interpret_conditional(ASTNode *node, Environment *env) { while (cs) { InterpretResult body_res = interpret_node(cs, env); - if (body_res.did_return || body_res.did_break) { + if (body_res.is_error || body_res.did_return || + body_res.did_break) { return body_res; } cs = cs->next; @@ -708,7 +755,8 @@ InterpretResult interpret_conditional(ASTNode *node, Environment *env) { while (cs) { InterpretResult body_res = interpret_node(cs, env); - if (body_res.did_return || body_res.did_break) { + if (body_res.is_error || body_res.did_return || + body_res.did_break) { return body_res; } cs = cs->next; @@ -1154,12 +1202,11 @@ void interpret_function_declaration(ASTNode *node, Environment *env) { add_variable(env, var); } -LiteralValue interpret_function_call(ASTNode *node, Environment *env) { +InterpretResult interpret_function_call(ASTNode *node, Environment *env) { debug_print_int("Starting function call interpretation\n"); if (!node || !node->function_call.name) { - fprintf(stderr, "Error: Invalid function call\n"); - return (LiteralValue){.type = TYPE_ERROR}; + return raise_error("Invalid function call"); } const char *func_name = node->function_call.name; @@ -1168,46 +1215,85 @@ LiteralValue interpret_function_call(ASTNode *node, Environment *env) { Variable *func_var = get_variable(env, func_name); if (func_var && func_var->value.type == TYPE_FUNCTION) { Function *func_ref = func_var->value.data.function_ptr; - // Call the function using the helper - return call_user_defined_function(func_ref, node, env); + // Call the user-defined function + LiteralValue func_result = + call_user_defined_function(func_ref, node, env); + // Wrap the LiteralValue in InterpretResult + InterpretResult res; + res.value = func_result; + res.did_return = false; + res.did_break = false; + res.is_error = (func_result.type == TYPE_ERROR); + return res; } // 2. Else, check if it's a built-in or globally defined function Function *func = get_function(env, func_name); if (!func) { // Function not found - fprintf(stderr, "Error: Undefined function `%s`\n", func_name); - exit(1); - return (LiteralValue){.type = TYPE_ERROR}; + return raise_error("Undefined function `%s`\n", func_name); } // If it’s a built-in => call the built-in logic if (func->is_builtin) { + InterpretResult builtin_res = {0}; if (strcmp(func->name, "sample") == 0) { - return builtin_input(node, env); + builtin_res = make_result(builtin_input(node, env), false, false); } else if (strcmp(func->name, "serve") == 0) { - return builtin_output(node, env); + builtin_res = make_result(builtin_output(node, env), false, false); } else if (strcmp(func->name, "burn") == 0) { - return builtin_error(node, env); + builtin_res = builtin_error(node, env); } else if (strcmp(func->name, "random") == 0) { - return builtin_random(node, env); + LiteralValue rand_val = builtin_random(node, env); + if (rand_val.type == TYPE_ERROR) { + builtin_res = make_result(rand_val, false, false); + builtin_res.is_error = true; + } else { + builtin_res = make_result(rand_val, false, false); + } } else if (strcmp(func->name, "string") == 0 || strcmp(func->name, "int") == 0 || strcmp(func->name, "float") == 0) { - return builtin_cast(node, env); + LiteralValue cast_val = builtin_cast(node, env); + if (cast_val.type == TYPE_ERROR) { + builtin_res = make_result(cast_val, false, false); + builtin_res.is_error = true; + } else { + builtin_res = make_result(cast_val, false, false); + } } else if (strcmp(func->name, "get_time") == 0) { - return builtin_time(); + LiteralValue time_val = builtin_time(); + builtin_res = make_result(time_val, false, false); } else if (strcmp(func->name, "taste_file") == 0) { - return builtin_file_read(node, env); + LiteralValue read_val = builtin_file_read(node, env); + if (read_val.type == TYPE_ERROR) { + builtin_res = make_result(read_val, false, false); + builtin_res.is_error = true; + } else { + builtin_res = make_result(read_val, false, false); + } } else if (strcmp(func->name, "plate_file") == 0) { - return builtin_file_write(node, env); + LiteralValue write_val = builtin_file_write(node, env); + if (write_val.type == TYPE_ERROR) { + builtin_res = make_result(write_val, false, false); + builtin_res.is_error = true; + } else { + builtin_res = make_result(write_val, false, false); + } } else if (strcmp(func->name, "garnish_file") == 0) { - return builtin_file_append(node, env); + LiteralValue append_val = builtin_file_append(node, env); + if (append_val.type == TYPE_ERROR) { + builtin_res = make_result(append_val, false, false); + builtin_res.is_error = true; + } else { + builtin_res = make_result(append_val, false, false); + } + } else { + // Unknown built-in + return raise_error("Unknown built-in function `%s`\n", func->name); } - // If no recognized built-in, error - fprintf(stderr, "Error: Unknown built-in `%s`\n", func->name); - return (LiteralValue){.type = TYPE_ERROR}; + return builtin_res; } // Otherwise, user-defined function. @@ -1262,7 +1348,12 @@ LiteralValue interpret_function_call(ASTNode *node, Environment *env) { } free_environment(&local_env); - return result; // if no explicit return => return `0` (or default) + + // If no explicit return, return default value (i.e., `0`) + InterpretResult default_res = + make_result(create_default_value(), false, false); + default_res.is_error = false; + return default_res; } InterpretResult interpret_ternary(ASTNode *node, Environment *env) { @@ -1293,3 +1384,109 @@ InterpretResult interpret_ternary(ASTNode *node, Environment *env) { return false_res; } } + +InterpretResult interpret_try(ASTNode *node, Environment *env) { + if (!node || node->type != AST_TRY) { + return raise_error("Invalid AST node for try block."); + } + + InterpretResult result = {0}; + bool exception_occurred = false; + LiteralValue exception_value = create_default_value(); + + // Execute try block + ASTNode *stmt = node->try_block.try_block; + while (stmt) { + InterpretResult res = interpret_node(stmt, env); + if (res.is_error) { + // An exception has been thrown + exception_occurred = true; + exception_value = res.value; + break; + } + if (res.did_return) { + // Propagate return up + return res; + } + stmt = stmt->next; + } + + // If exception occurred, handle rescue blocks + if (exception_occurred) { + ASTCatchNode *catch = node->try_block.catch_blocks; + bool handled = false; + + while (catch && !handled) { + Environment catch_env; + init_environment(&catch_env); + + // Copy global functions to rescue environment + for (size_t i = 0; i < env->function_count; i++) { + Function *global_func = &env->functions[i]; + Function func_copy = {.name = strdup(global_func->name), + .parameters = copy_function_parameters( + global_func->parameters), + .body = copy_ast_node(global_func->body), + .is_builtin = global_func->is_builtin}; + add_function(&catch_env, func_copy); + } + + // If there's an error variable, bind the exception to it + if (catch->error_variable) { + Variable error_var = {.variable_name = + strdup(catch->error_variable), + .value = exception_value, + .is_constant = false}; + add_variable(&catch_env, error_var); + } + + // Execute catch block + ASTNode *catch_stmt = catch->body; + while (catch_stmt) { + InterpretResult res = interpret_node(catch_stmt, &catch_env); + if (res.is_error) { + // Nested exception, propagate + free_environment(&catch_env); + return res; + } + if (res.did_return) { + // Propagate return up + free_environment(&catch_env); + return res; + } + catch_stmt = catch_stmt->next; + } + + handled = true; // Currently handling only one catch block + free_environment(&catch_env); + catch = catch->next; + } + + if (!handled) { + // No rescue block handled the exception, propagate it + result.value = exception_value; + result.is_error = true; + return result; + } + } + + // Execute finish block if it exists + if (node->try_block.finally_block) { + ASTNode *finish_stmt = node->try_block.finally_block; + while (finish_stmt) { + InterpretResult res = interpret_node(finish_stmt, env); + if (res.is_error) { + // If an exception occurs in finish, prioritize it + return res; + } + if (res.did_return) { + // Propagate return up + return res; + } + finish_stmt = finish_stmt->next; + } + } + + // Normal execution + return result; +} diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index e9839dc..569c090 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -15,17 +15,17 @@ InterpretResult interpret_node(ASTNode *node, Environment *env); LiteralValue interpret_literal(ASTNode *node); -LiteralValue interpret_variable(ASTNode *node, Environment *env); +InterpretResult interpret_variable(ASTNode *node, Environment *env); InterpretResult interpret_constant(ASTNode *node, Environment *env); InterpretResult interpret_assignment(ASTNode *node, Environment *env); -LiteralValue interpret_binary_op(ASTNode *node, Environment *env); +InterpretResult interpret_binary_op(ASTNode *node, Environment *env); InterpretResult interpret_conditional(ASTNode *node, Environment *env); InterpretResult interpret_while_loop(ASTNode *node, Environment *env); LiteralValue interpret_for_loop(ASTNode *node, Environment *env); InterpretResult interpret_switch(ASTNode *node, Environment *env); void interpret_function_declaration(ASTNode *node, Environment *env); -LiteralValue interpret_function_call(ASTNode *node, Environment *env); -LiteralValue interpret_unary_op(ASTNode *node, Environment *env); +InterpretResult interpret_function_call(ASTNode *node, Environment *env); +InterpretResult interpret_unary_op(ASTNode *node, Environment *env); LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand); InterpretResult interpret_ternary(ASTNode *node, Environment *env); LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, From 16b411cbd2aea742c2687ea6c6f5809fb963c651 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:00:27 +0000 Subject: [PATCH 10/62] In process of replacing `error_interpreter` with `raise_error` in `builtins` #179 --- src/interpreter/builtins.c | 92 ++++++++++++++++++++------------------ src/interpreter/builtins.h | 2 +- 2 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 3074ba4..bfc67a8 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -354,134 +354,140 @@ bool is_valid_float(const char *str, FLOAT_SIZE *out_value) { return true; } -LiteralValue builtin_cast(ASTNode *node, Environment *env) { +InterpretResult builtin_cast(ASTNode *node, Environment *env) { if (node->type != AST_FUNCTION_CALL) { - error_interpreter( + return raise_error( "`builtin_cast()` expects an `AST_FUNCTION_CALL` node.\n"); } char *cast_type = node->function_call.name; if (!cast_type) { - error_interpreter("No cast type provided to `builtin_cast()`.\n"); + return raise_error("No cast type provided to `builtin_cast()`.\n"); } ASTNode *arg_node = node->function_call.arguments; if (!arg_node) { - error_interpreter( + return raise_error( "No expression provided for cast in `builtin_cast()`.\n"); } // Ensure there's only one argument if (arg_node->next != NULL) { - error_interpreter("`%s` cast function takes exactly one argument.\n", - cast_type); + return raise_error("`%s` cast function takes exactly one argument.\n", + cast_type); } ASTNode *expr = arg_node; // Interpret the expression to be casted InterpretResult expr_result = interpret_node(expr, env); - LiteralValue original = expr_result.value; - - // If interpreting the expression resulted in an error, propagate it - if (original.type == TYPE_ERROR) { - return original; + if (expr_result.is_error) { + return expr_result; // Propagate the error } - LiteralValue result; - memset(&result, 0, sizeof(LiteralValue)); // initialize result + LiteralValue original = expr_result.value; + + // Initialize the result + InterpretResult result_res = {0}; + result_res.did_return = false; + result_res.did_break = false; + result_res.is_error = false; + // Perform the cast based on `cast_type` if (strcmp(cast_type, "string") == 0) { - result.type = TYPE_STRING; + LiteralValue cast_val; + cast_val.type = TYPE_STRING; char buffer[256] = {0}; switch (original.type) { case TYPE_INTEGER: snprintf(buffer, sizeof(buffer), INT_FORMAT_SPECIFIER, original.data.integer); - result.data.string = strdup(buffer); + cast_val.data.string = strdup(buffer); break; case TYPE_FLOAT: snprintf(buffer, sizeof(buffer), FLOAT_FORMAT_SPECIFIER, original.data.floating_point); - result.data.string = strdup(buffer); + cast_val.data.string = strdup(buffer); break; case TYPE_BOOLEAN: - result.data.string = + cast_val.data.string = strdup(original.data.boolean ? "True" : "False"); break; case TYPE_STRING: - result.data.string = strdup(original.data.string); + cast_val.data.string = strdup(original.data.string); break; default: - error_interpreter("Unsupported type for string cast.\n"); + return raise_error("Unsupported type for string cast.\n"); } - if (!result.data.string) { - error_interpreter("Memory allocation failed during string cast.\n"); + if (!cast_val.data.string) { + return raise_error( + "Memory allocation failed during string cast.\n"); } - debug_print_int("Casted value to string: `%s`\n", result.data.string); + result_res.value = cast_val; } else if (strcmp(cast_type, "int") == 0) { - result.type = TYPE_INTEGER; + LiteralValue cast_val; + cast_val.type = TYPE_INTEGER; switch (original.type) { case TYPE_STRING: { INT_SIZE temp; if (!is_valid_int(original.data.string, &temp)) { - error_interpreter("Cannot cast string \"%s\" to int.\n", - original.data.string); + return raise_error("Cannot cast string \"%s\" to int.\n", + original.data.string); } - result.data.integer = temp; + cast_val.data.integer = temp; break; } case TYPE_FLOAT: - result.data.integer = (INT_SIZE)original.data.floating_point; + cast_val.data.integer = (INT_SIZE)original.data.floating_point; break; case TYPE_BOOLEAN: - result.data.integer = original.data.boolean ? 1 : 0; + cast_val.data.integer = original.data.boolean ? 1 : 0; break; case TYPE_INTEGER: - result.data.integer = original.data.integer; + cast_val.data.integer = original.data.integer; break; default: - error_interpreter("Unsupported type for int cast.\n"); + return raise_error("Unsupported type for int cast.\n"); } - debug_print_int("Casted value to int: `%lld`\n", result.data.integer); + result_res.value = cast_val; } else if (strcmp(cast_type, "float") == 0) { - result.type = TYPE_FLOAT; + LiteralValue cast_val; + cast_val.type = TYPE_FLOAT; switch (original.type) { case TYPE_STRING: { FLOAT_SIZE temp; if (!is_valid_float(original.data.string, &temp)) { - error_interpreter("Cannot cast string \"%s\" to float.\n", - original.data.string); + return raise_error("Cannot cast string \"%s\" to float.\n", + original.data.string); } - result.data.floating_point = temp; + cast_val.data.floating_point = temp; break; } case TYPE_INTEGER: - result.data.floating_point = (FLOAT_SIZE)original.data.integer; + cast_val.data.floating_point = (FLOAT_SIZE)original.data.integer; break; case TYPE_BOOLEAN: - result.data.floating_point = original.data.boolean ? 1.0 : 0.0; + cast_val.data.floating_point = original.data.boolean ? 1.0 : 0.0; break; case TYPE_FLOAT: - result.data.floating_point = original.data.floating_point; + cast_val.data.floating_point = original.data.floating_point; break; default: - error_interpreter("Unsupported type for float cast.\n"); + return raise_error("Unsupported type for float cast.\n"); } - debug_print_int("Casted value to float: `%Lf`\n", - result.data.floating_point); + result_res.value = cast_val; } else { - error_interpreter("Unsupported cast type: `%s`\n", cast_type); + return raise_error("Unsupported cast type: `%s`\n", cast_type); } - return result; + return result_res; } LiteralValue builtin_time() { diff --git a/src/interpreter/builtins.h b/src/interpreter/builtins.h index 3a7ea60..c24c16e 100644 --- a/src/interpreter/builtins.h +++ b/src/interpreter/builtins.h @@ -25,7 +25,7 @@ LiteralValue builtin_input(ASTNode *node, Environment *env); LiteralValue builtin_random(ASTNode *node, Environment *env); LiteralValue builtin_output(ASTNode *node, Environment *env); InterpretResult builtin_error(ASTNode *node, Environment *env); -LiteralValue builtin_cast(ASTNode *node, Environment *env); +InterpretResult builtin_cast(ASTNode *node, Environment *env); LiteralValue builtin_time(); LiteralValue builtin_file_read(ASTNode *node, Environment *env); LiteralValue builtin_file_write(ASTNode *node, Environment *env); From ab5eb8912cb7155832011c62e83a40e884dd02ba Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:10:25 +0000 Subject: [PATCH 11/62] Switching to `InterpretResult` and `make_result` universally across interpreter #179 --- src/interpreter/builtins.c | 11 +- src/interpreter/builtins.h | 15 +- src/interpreter/interpreter.c | 264 +++++++++++++++------------------- src/interpreter/interpreter.h | 11 +- src/interpreter/utils.c | 10 ++ src/interpreter/utils.h | 2 + 6 files changed, 150 insertions(+), 163 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index bfc67a8..22b556e 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -490,16 +490,17 @@ InterpretResult builtin_cast(ASTNode *node, Environment *env) { return result_res; } -LiteralValue builtin_time() { +InterpretResult builtin_time() { time_t current_time = time(NULL); if (current_time == -1) { - error_interpreter("Failed to get the current time\n"); - exit(1); + return raise_error("Failed to get the current time\n"); } - return (LiteralValue){.type = TYPE_INTEGER, - .data.integer = (INT_SIZE)current_time}; + LiteralValue time_val = {.type = TYPE_INTEGER, + .data.integer = (INT_SIZE)current_time}; + + return make_result(time_val, false, false); } char *process_escape_sequences(const char *input) { diff --git a/src/interpreter/builtins.h b/src/interpreter/builtins.h index c24c16e..dfae2a8 100644 --- a/src/interpreter/builtins.h +++ b/src/interpreter/builtins.h @@ -4,6 +4,7 @@ #include "../debug/debug.h" #include "../shared/ast_types.h" #include "interpreter_types.h" +#include "utils.h" #include #include #include @@ -21,15 +22,15 @@ typedef struct { } ArgumentSpec; // Built-in functions for the standard library -LiteralValue builtin_input(ASTNode *node, Environment *env); -LiteralValue builtin_random(ASTNode *node, Environment *env); -LiteralValue builtin_output(ASTNode *node, Environment *env); +InterpretResult builtin_input(ASTNode *node, Environment *env); +InterpretResult builtin_random(ASTNode *node, Environment *env); +InterpretResult builtin_output(ASTNode *node, Environment *env); InterpretResult builtin_error(ASTNode *node, Environment *env); InterpretResult builtin_cast(ASTNode *node, Environment *env); -LiteralValue builtin_time(); -LiteralValue builtin_file_read(ASTNode *node, Environment *env); -LiteralValue builtin_file_write(ASTNode *node, Environment *env); -LiteralValue builtin_file_append(ASTNode *node, Environment *env); +InterpretResult builtin_time(); +InterpretResult builtin_file_read(ASTNode *node, Environment *env); +InterpretResult builtin_file_write(ASTNode *node, Environment *env); +InterpretResult builtin_file_append(ASTNode *node, Environment *env); // Helpers bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index fde7504..dd3be0c 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -12,16 +12,6 @@ LiteralValue create_default_value() { return value; } -// A helper to wrap `LiteralValue` in `InterpretResult` -static InterpretResult make_result(LiteralValue val, bool did_return, - bool did_break) { - InterpretResult r; - r.value = val; - r.did_return = did_return; - r.did_break = did_break; - return r; -} - InterpretResult interpret_node(ASTNode *node, Environment *env) { if (!node) { return make_result(create_default_value(), false, false); @@ -102,13 +92,9 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) { case AST_VARIABLE: { debug_print_int("\tMatched: `AST_VARIABLE`\n"); - LiteralValue var_val = interpret_variable(node, env); - if (var_val.type == TYPE_ERROR) { - result = make_result(var_val, false, false); - result.is_error = true; - } else { - result = make_result(var_val, false, false); - } + // Replace LiteralValue with InterpretResult + InterpretResult var_res = interpret_variable(node, env); + result = var_res; break; } @@ -140,9 +126,7 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) { break; default: - error_interpreter("Unsupported `ASTNode` type.\n"); - result = make_result(create_default_value(), false, false); - break; + return raise_error("Unsupported `ASTNode` type.\n"); } return result; @@ -191,8 +175,12 @@ LiteralValue interpret_literal(ASTNode *node) { value.data.boolean ? "True" : "False"); break; default: - error_interpreter("Unsupported literal type.\n"); + // Let `interpret_node` handle unsupported literals + value.type = TYPE_ERROR; + value.data.string = strdup("Unsupported literal type.\n"); + break; } + return value; } @@ -610,15 +598,14 @@ Variable *get_variable(Environment *env, const char *variable_name) { return NULL; } -void add_variable(Environment *env, Variable var) { +InterpretResult add_variable(Environment *env, Variable var) { // Check if the variable already exists for (size_t i = 0; i < env->variable_count; i++) { if (strcmp(env->variables[i].variable_name, var.variable_name) == 0) { // If existing variable is a constant, prevent re-assignment if (env->variables[i].is_constant) { - fprintf(stderr, "Error: Cannot reassign to constant `%s`.\n", - var.variable_name); - return; + return raise_error("Error: Cannot reassign to constant `%s`.\n", + var.variable_name); } // Update the value of the existing variable @@ -629,29 +616,31 @@ void add_variable(Environment *env, Variable var) { env->variables[i].value = var.value; env->variables[i].is_constant = var.is_constant; - return; + return make_result(var.value, false, false); } } // Add a new variable if (env->variable_count == env->capacity) { // Resize the variables array if necessary - env->capacity = env->capacity ? env->capacity * 2 : 4; - env->variables = - realloc(env->variables, env->capacity * sizeof(Variable)); - if (!env->variables) { - error_interpreter( + size_t new_capacity = env->capacity ? env->capacity * 2 : 4; + Variable *new_variables = + realloc(env->variables, new_capacity * sizeof(Variable)); + if (!new_variables) { + return raise_error( "Memory allocation failed while adding variable `%s`.\n", var.variable_name); } + env->variables = new_variables; + env->capacity = new_capacity; } // Copy variable name and value env->variables[env->variable_count].variable_name = strdup(var.variable_name); if (!env->variables[env->variable_count].variable_name) { - error_interpreter("Memory allocation failed for variable name `%s`.\n", - var.variable_name); + return raise_error("Memory allocation failed for variable name `%s`.\n", + var.variable_name); } // Deep copy based on type @@ -659,6 +648,11 @@ void add_variable(Environment *env, Variable var) { env->variables[env->variable_count].value.type = TYPE_STRING; env->variables[env->variable_count].value.data.string = strdup(var.value.data.string); + if (!env->variables[env->variable_count].value.data.string) { + return raise_error( + "Memory allocation failed for string variable `%s`.\n", + var.variable_name); + } } else if (var.value.type == TYPE_FUNCTION) { env->variables[env->variable_count].value.type = TYPE_FUNCTION; env->variables[env->variable_count].value.data.function_ptr = @@ -669,30 +663,43 @@ void add_variable(Environment *env, Variable var) { env->variables[env->variable_count].is_constant = var.is_constant; env->variable_count++; + + return make_result(var.value, false, false); } -Variable *allocate_variable(Environment *env, const char *name) { +InterpretResult allocate_variable(Environment *env, const char *name) { // Check if the variable already exists for (size_t i = 0; i < env->variable_count; i++) { if (strcmp(env->variables[i].variable_name, name) == 0) { - return &env->variables[i]; // return existing variable + return make_result(env->variables[i].value, false, false); } } // If the variable doesn't exist, allocate it in memory if (env->variable_count == env->capacity) { - env->capacity *= 2; - env->variables = - realloc(env->variables, env->capacity * sizeof(Variable)); - if (!env->variables) { - error_interpreter("Memory allocation failed.\n"); + size_t new_capacity = env->capacity ? env->capacity * 2 : 4; + Variable *new_variables = + realloc(env->variables, new_capacity * sizeof(Variable)); + if (!new_variables) { + return raise_error("Memory allocation failed.\n"); } + env->variables = new_variables; + env->capacity = new_capacity; } env->variables[env->variable_count].variable_name = strdup(name); + if (!env->variables[env->variable_count].variable_name) { + return raise_error("Memory allocation failed for variable name `%s`.\n", + name); + } + + // Initialize the variable with default value + env->variables[env->variable_count].value = create_default_value(); + env->variables[env->variable_count].is_constant = false; env->variable_count++; - return &env->variables[env->variable_count - 1]; + Variable *var = &env->variables[env->variable_count - 1]; + return make_result(var->value, false, false); } InterpretResult interpret_conditional(ASTNode *node, Environment *env) { @@ -818,9 +825,9 @@ InterpretResult interpret_while_loop(ASTNode *node, Environment *env) { return make_result(create_default_value(), false, false); } -LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { +InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { if (node->type != AST_FOR_LOOP) { - error_interpreter( + return raise_error( "`interpret_for_loop` called with non-`for`-loop `ASTNode`\n"); } @@ -834,13 +841,13 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { // Evaluate start and end expressions InterpretResult start_res = interpret_node(start_expr, env); - if (start_res.value.type == TYPE_ERROR) { - return start_res.value; + if (start_res.is_error) { + return start_res; } InterpretResult end_res = interpret_node(end_expr, env); - if (end_res.value.type == TYPE_ERROR) { - return end_res.value; + if (end_res.is_error) { + return end_res; } // Determine start & end as floats for flexibility @@ -850,7 +857,7 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { } else if (start_res.value.type == TYPE_INTEGER) { start_val = (FLOAT_SIZE)start_res.value.data.integer; } else { - error_interpreter("Start expression in `for` loop must be numeric\n"); + return raise_error("Start expression in `for` loop must be numeric\n"); } if (end_res.value.type == TYPE_FLOAT) { @@ -858,15 +865,15 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { } else if (end_res.value.type == TYPE_INTEGER) { end_val = (FLOAT_SIZE)end_res.value.data.integer; } else { - error_interpreter("End expression in `for` loop must be numeric\n"); + return raise_error("End expression in `for` loop must be numeric\n"); } // Evaluate step expression if present double step = 1.0; // default if (step_expr) { InterpretResult step_res = interpret_node(step_expr, env); - if (step_res.value.type == TYPE_ERROR) { - return step_res.value; + if (step_res.is_error) { + return step_res; } if (step_res.value.type == TYPE_FLOAT) { @@ -874,7 +881,7 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { } else if (step_res.value.type == TYPE_INTEGER) { step = (FLOAT_SIZE)step_res.value.data.integer; } else { - error_interpreter( + return raise_error( "Step expression in `for` loop must be numeric\n"); } } else { @@ -888,14 +895,14 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { // Validate step to prevent infinite loops if (step < 1e-9 && step > -1e-9) { - error_interpreter("Step value cannot be zero in `for` loop\n"); + return raise_error("Step value cannot be zero in `for` loop\n"); } // Validate step to check if step is in correct direction if ((start_val < end_val && step < 0) || (start_val > end_val && step > 0)) { - error_interpreter("Step value is in the wrong direction for the " - "specified range of the `for` loop\n"); + return raise_error("Step value is in the wrong direction for the " + "specified range of the `for` loop\n"); } // Assign or update loop variable in the environment @@ -933,31 +940,32 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { } else if (var->value.type == TYPE_INTEGER) { current_val = (FLOAT_SIZE)var->value.data.integer; } else { - error_interpreter("Loop variable `%s` must be numeric\n", loop_var); + return raise_error("Loop variable `%s` must be numeric\n", + loop_var); } // Check if condition is still valid - bool condition = is_ascending ? (inclusive ? (current_val <= end_val) - : (current_val < end_val)) - : (inclusive ? (current_val >= end_val) - : (current_val > end_val)); - if (!condition) { + bool condition_true = false; + if (is_ascending) { + condition_true = + inclusive ? (current_val <= end_val) : (current_val < end_val); + } else { + condition_true = + inclusive ? (current_val >= end_val) : (current_val > end_val); + } + + if (!condition_true) { break; } - // Execute loop body - ASTNode *current_stmt = body; - while (current_stmt) { - InterpretResult res = interpret_node(current_stmt, env); - if (res.did_return) { - return res.value; - } - if (res.did_break) { - // break out of the for loop - // just exit interpret_for_loop entirely - return create_default_value(); + // Interpret the loop body + ASTNode *current = body; + while (current) { + InterpretResult body_res = interpret_node(current, env); + if (body_res.did_return || body_res.did_break) { + return body_res; } - current_stmt = current_stmt->next; + current = current->next; } // Update loop variable @@ -969,7 +977,7 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) { } // Loops do not return values - return create_default_value(); + return make_result(create_default_value(), false, false); } InterpretResult interpret_switch(ASTNode *node, Environment *env) { @@ -1063,8 +1071,9 @@ InterpretResult interpret_switch(ASTNode *node, Environment *env) { return make_result(create_default_value(), false, false); } -LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, - Environment *env) { +InterpretResult call_user_defined_function(Function *func_ref, + ASTNode *call_node, + Environment *env) { debug_print_int("Calling user-defined function: `%s`\n", func_ref->name); // 1) Create a new local environment @@ -1072,7 +1081,6 @@ LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, init_environment(&local_env); // 2) Copy global functions into the local environment - // This ensures that nested function calls can access global functions for (size_t i = 0; i < env->function_count; i++) { Function *global_func = &env->functions[i]; @@ -1092,13 +1100,13 @@ LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, while (param && arg) { InterpretResult arg_res = interpret_node(arg, env); - LiteralValue arg_value = arg_res.value; - - if (arg_value.type == TYPE_ERROR) { + if (arg_res.is_error) { free_environment(&local_env); - return arg_value; + return arg_res; } + LiteralValue arg_value = arg_res.value; + // Bind the argument to the parameter in the local environment Variable param_var = {.variable_name = strdup(param->parameter_name), .value = arg_value, @@ -1111,27 +1119,31 @@ LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, // 4) Check for argument count mismatch if (param || arg) { - fprintf(stderr, - "Error: Argument count mismatch when calling function `%s`\n", - func_ref->name); free_environment(&local_env); - return (LiteralValue){.type = TYPE_ERROR}; + return raise_error( + "Error: Argument count mismatch when calling function `%s`\n", + func_ref->name); } // 5) Interpret the function body ASTNode *stmt = func_ref->body; - LiteralValue result = create_default_value(); + InterpretResult func_res = + make_result(create_default_value(), false, false); + while (stmt) { InterpretResult r = interpret_node(stmt, &local_env); if (r.did_return) { - result = r.value; + func_res = r; break; } if (r.did_break) { - fprintf(stderr, - "Error: 'break' statement outside of loop or switch.\n"); free_environment(&local_env); - return (LiteralValue){.type = TYPE_ERROR}; + return raise_error( + "Error: 'break' statement outside of loop or switch.\n"); + } + if (r.is_error) { + free_environment(&local_env); + return r; } stmt = stmt->next; } @@ -1139,7 +1151,7 @@ LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, free_environment(&local_env); // If no explicit return, return default value (e.g., 0) - return result; + return func_res; } void interpret_function_declaration(ASTNode *node, Environment *env) { @@ -1216,15 +1228,9 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { if (func_var && func_var->value.type == TYPE_FUNCTION) { Function *func_ref = func_var->value.data.function_ptr; // Call the user-defined function - LiteralValue func_result = + InterpretResult func_res = call_user_defined_function(func_ref, node, env); - // Wrap the LiteralValue in InterpretResult - InterpretResult res; - res.value = func_result; - res.did_return = false; - res.did_break = false; - res.is_error = (func_result.type == TYPE_ERROR); - return res; + return func_res; } // 2. Else, check if it's a built-in or globally defined function @@ -1238,56 +1244,25 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { if (func->is_builtin) { InterpretResult builtin_res = {0}; if (strcmp(func->name, "sample") == 0) { - builtin_res = make_result(builtin_input(node, env), false, false); + builtin_res = builtin_input(node, env); } else if (strcmp(func->name, "serve") == 0) { - builtin_res = make_result(builtin_output(node, env), false, false); + builtin_res = builtin_output(node, env); } else if (strcmp(func->name, "burn") == 0) { builtin_res = builtin_error(node, env); } else if (strcmp(func->name, "random") == 0) { - LiteralValue rand_val = builtin_random(node, env); - if (rand_val.type == TYPE_ERROR) { - builtin_res = make_result(rand_val, false, false); - builtin_res.is_error = true; - } else { - builtin_res = make_result(rand_val, false, false); - } + builtin_res = builtin_random(node, env); } else if (strcmp(func->name, "string") == 0 || strcmp(func->name, "int") == 0 || strcmp(func->name, "float") == 0) { - LiteralValue cast_val = builtin_cast(node, env); - if (cast_val.type == TYPE_ERROR) { - builtin_res = make_result(cast_val, false, false); - builtin_res.is_error = true; - } else { - builtin_res = make_result(cast_val, false, false); - } + builtin_res = builtin_cast(node, env); } else if (strcmp(func->name, "get_time") == 0) { - LiteralValue time_val = builtin_time(); - builtin_res = make_result(time_val, false, false); + builtin_res = builtin_time(); } else if (strcmp(func->name, "taste_file") == 0) { - LiteralValue read_val = builtin_file_read(node, env); - if (read_val.type == TYPE_ERROR) { - builtin_res = make_result(read_val, false, false); - builtin_res.is_error = true; - } else { - builtin_res = make_result(read_val, false, false); - } + builtin_res = builtin_file_read(node, env); } else if (strcmp(func->name, "plate_file") == 0) { - LiteralValue write_val = builtin_file_write(node, env); - if (write_val.type == TYPE_ERROR) { - builtin_res = make_result(write_val, false, false); - builtin_res.is_error = true; - } else { - builtin_res = make_result(write_val, false, false); - } + builtin_res = builtin_file_write(node, env); } else if (strcmp(func->name, "garnish_file") == 0) { - LiteralValue append_val = builtin_file_append(node, env); - if (append_val.type == TYPE_ERROR) { - builtin_res = make_result(append_val, false, false); - builtin_res.is_error = true; - } else { - builtin_res = make_result(append_val, false, false); - } + builtin_res = builtin_file_append(node, env); } else { // Unknown built-in return raise_error("Unknown built-in function `%s`\n", func->name); @@ -1350,19 +1325,16 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { free_environment(&local_env); // If no explicit return, return default value (i.e., `0`) - InterpretResult default_res = - make_result(create_default_value(), false, false); - default_res.is_error = false; - return default_res; + return make_result(create_default_value(), false, false); } InterpretResult interpret_ternary(ASTNode *node, Environment *env) { if (!node || node->type != AST_TERNARY) { - error_interpreter("Invalid ternary operation node.\n"); + return raise_error("Invalid ternary operation node.\n"); } InterpretResult cond_res = interpret_node(node->ternary.condition, env); - if (cond_res.did_return || cond_res.did_break) { + if (cond_res.is_error || cond_res.did_return || cond_res.did_break) { return cond_res; } @@ -1372,7 +1344,7 @@ InterpretResult interpret_ternary(ASTNode *node, Environment *env) { } else if (cond_res.value.type == TYPE_INTEGER) { is_true = (cond_res.value.data.integer != 0); } else { - error_interpreter("Ternary condition must be boolean or integer.\n"); + return raise_error("Ternary condition must be boolean or integer.\n"); } if (is_true) { @@ -1390,7 +1362,7 @@ InterpretResult interpret_try(ASTNode *node, Environment *env) { return raise_error("Invalid AST node for try block."); } - InterpretResult result = {0}; + InterpretResult result = make_result(create_default_value(), false, false); bool exception_occurred = false; LiteralValue exception_value = create_default_value(); diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index 569c090..a691a5b 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -14,22 +14,23 @@ #include InterpretResult interpret_node(ASTNode *node, Environment *env); -LiteralValue interpret_literal(ASTNode *node); +InterpretResult interpret_literal(ASTNode *node); InterpretResult interpret_variable(ASTNode *node, Environment *env); InterpretResult interpret_constant(ASTNode *node, Environment *env); InterpretResult interpret_assignment(ASTNode *node, Environment *env); InterpretResult interpret_binary_op(ASTNode *node, Environment *env); InterpretResult interpret_conditional(ASTNode *node, Environment *env); InterpretResult interpret_while_loop(ASTNode *node, Environment *env); -LiteralValue interpret_for_loop(ASTNode *node, Environment *env); +InterpretResult interpret_for_loop(ASTNode *node, Environment *env); InterpretResult interpret_switch(ASTNode *node, Environment *env); void interpret_function_declaration(ASTNode *node, Environment *env); InterpretResult interpret_function_call(ASTNode *node, Environment *env); InterpretResult interpret_unary_op(ASTNode *node, Environment *env); LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand); InterpretResult interpret_ternary(ASTNode *node, Environment *env); -LiteralValue call_user_defined_function(Function *func_ref, ASTNode *call_node, - Environment *env); +InterpretResult call_user_defined_function(Function *func_ref, + ASTNode *call_node, + Environment *env); // Interpret program void interpret_program(ASTNode *program, Environment *env); @@ -39,4 +40,4 @@ Variable *get_variable(Environment *env, const char *variable_name); void add_variable(Environment *env, Variable var); ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params); -#endif +#endif \ No newline at end of file diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 78ad73f..9b5151e 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -213,3 +213,13 @@ Function *get_function(Environment *env, const char *name) { } return NULL; } + +// A helper to wrap `LiteralValue` in `InterpretResult` +static InterpretResult make_result(LiteralValue val, bool did_return, + bool did_break) { + InterpretResult r; + r.value = val; + r.did_return = did_return; + r.did_break = did_break; + return r; +} diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h index 8de157a..6aa2454 100644 --- a/src/interpreter/utils.h +++ b/src/interpreter/utils.h @@ -23,4 +23,6 @@ ASTNode *copy_ast_node(ASTNode *node); void add_function(Environment *env, Function func); Function *get_function(Environment *env, const char *name); +InterpretResult make_result(LiteralValue val, bool did_return, bool did_break); + #endif From 9caac50cb5868174cf44b4af5a0fbbeb943be5e0 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:22:18 +0000 Subject: [PATCH 12/62] Update README.md #179 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a128a1a..450906b 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ $ cd src $ make ``` -> [!Note] +> [!Warning] > > Unless you move `flavor` to `/usr/local/bin/`, > you'll have to use `./flavor` for commands with relative file paths. @@ -160,7 +160,9 @@ $ flavor recipe.flv --debug # Debug mode $ flavor --about # About FlavorLang ``` -The `--debug` flag is really useful for understanding how FlavorLang is executing (tokenizing, parsing, and interpreting) your file. +> [!Note] +> +> The `--debug` flag is really useful for understanding how FlavorLang is executing (tokenizing, parsing, and interpreting) your file. --- From 295e0a61b8337e789508bb62a9a0a7a66234ce96 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:22:39 +0000 Subject: [PATCH 13/62] Update language_design.md #179 --- docs/language_design.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/language_design.md b/docs/language_design.md index 2b08ba8..c4150f9 100644 --- a/docs/language_design.md +++ b/docs/language_design.md @@ -32,16 +32,15 @@ This `docs/` page details the core design of FlavorLang's syntax, the various da | `break` | Exit control flow | Stops execution of further cases in `check` and exits the current flow. | ✅ | | `create` | Define a function | Creates a reusable block of logic. | ✅ | | `deliver` | Return statement | Returns a value and stops function execution. | ✅ | -| `try` | Try block | Executes code that might fail. | ❌ | -| `rescue` | Catch block | Handles errors during execution. | ❌ | -| `finish` | Finally block | Optional cleanup & always runs. | ❌ | +| `try` | Try block | Executes code that might fail. | ✅ | +| `rescue` | Catch block | Handles errors during execution. | ✅ | +| `finish` | Finally block | Optional cleanup & always runs. | ✅ | | `burn` | Force exit or raise an error | Stops execution immediately with a message. | ✅ | | `serve` | Print or output | Outputs a value or message immediately. | ✅ | | `sample` | Input from console | Reads user input. | ✅ | | `plate` | Write to file | Writes data to a file. | ✅ | | `garnish` | Append to file | Appends data to a file. | ✅ | | `taste` | Read from file | Reads data from a file. | ✅ | -| `recipe` | Import `.flv` file | Imports logic from another `.flv` file. | ❌ | --- From 4e2d87e5d04f1cf36bcfa7a4beeca77247e104dd Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:02:53 +0000 Subject: [PATCH 14/62] Fix: `interpreter.c`/`.h` now fully embraces `InterpretResult` usage #179 --- src/interpreter/interpreter.c | 128 ++++++++++++++++++---------------- src/interpreter/interpreter.h | 2 +- 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index dd3be0c..5753d02 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -24,7 +24,7 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) { switch (node->type) { case AST_LITERAL: debug_print_int("\tMatched: `AST_LITERAL`\n"); - result = make_result(interpret_literal(node), false, false); + result = interpret_literal(node); break; case AST_ASSIGNMENT: @@ -145,7 +145,7 @@ void interpret_program(ASTNode *program, Environment *env) { } } -LiteralValue interpret_literal(ASTNode *node) { +InterpretResult interpret_literal(ASTNode *node) { LiteralValue value; debug_print_int("Interpreting literal value...\n"); debug_print_int("Literal type: %d\n", node->literal.type); @@ -181,7 +181,7 @@ LiteralValue interpret_literal(ASTNode *node) { break; } - return value; + return make_result(value, false, false); } InterpretResult interpret_variable(ASTNode *node, Environment *env) { @@ -250,8 +250,19 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) { return make_result(new_value, false, false); } -LiteralValue handle_string_concatenation(LiteralValue left, - LiteralValue right) { +InterpretResult handle_string_concatenation(InterpretResult left_res, + InterpretResult right_res) { + // Check for errors in operands + if (left_res.is_error) { + return left_res; + } + if (right_res.is_error) { + return right_res; + } + + LiteralValue left = left_res.value; + LiteralValue right = right_res.value; + LiteralValue result; result.type = TYPE_STRING; @@ -276,7 +287,7 @@ LiteralValue handle_string_concatenation(LiteralValue left, char *new_string = malloc(new_size); if (!new_string) { - error_interpreter( + return raise_error( "Memory allocation failed for string concatenation.\n"); } @@ -285,7 +296,7 @@ LiteralValue handle_string_concatenation(LiteralValue left, right.type == TYPE_STRING ? right.data.string : num_str2); result.data.string = new_string; - return result; + return make_result(result, false, false); } LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { @@ -368,28 +379,36 @@ InterpretResult interpret_unary_op(ASTNode *node, Environment *env) { return make_result(result, false, false); } -LiteralValue evaluate_operator(const char *op, LiteralValue left, - LiteralValue right) { +InterpretResult evaluate_operator(const char *op, InterpretResult left_res, + InterpretResult right_res) { debug_print_int("Operator: `%s`\n", op); + // Check for errors in operands + if (left_res.is_error) { + return left_res; + } + if (right_res.is_error) { + return right_res; + } + + LiteralValue left = left_res.value; + LiteralValue right = right_res.value; + LiteralValue result; memset(&result, 0, sizeof(LiteralValue)); // Initialize result // Handle string concatenation with "+" operator if (strcmp(op, "+") == 0 && (left.type == TYPE_STRING || right.type == TYPE_STRING)) { - return handle_string_concatenation(left, right); + return handle_string_concatenation(left_res, right_res); } // Handle logical AND and OR if (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0) { // Ensure both operands are boolean if (left.type != TYPE_BOOLEAN || right.type != TYPE_BOOLEAN) { - LiteralValue error_val; - error_val.type = TYPE_ERROR; - error_val.data.string = strdup( + return raise_error( "Logical operators `&&` and `||` require boolean operands.\n"); - return error_val; } result.type = TYPE_BOOLEAN; @@ -400,20 +419,19 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.data.boolean = left.data.boolean || right.data.boolean; } - return result; + return make_result(result, false, false); } // Get numeric values for arithmetic and comparison - FLOAT_SIZE left_value = 0.0, right_value = 0.0; + double left_value = 0.0, right_value = 0.0; if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { left_value = (left.type == TYPE_FLOAT) ? left.data.floating_point - : (FLOAT_SIZE)left.data.integer; - right_value = (right.type == TYPE_FLOAT) - ? right.data.floating_point - : (FLOAT_SIZE)right.data.integer; + : (double)left.data.integer; + right_value = (right.type == TYPE_FLOAT) ? right.data.floating_point + : (double)right.data.integer; } else { - left_value = (FLOAT_SIZE)left.data.integer; - right_value = (FLOAT_SIZE)right.data.integer; + left_value = (double)left.data.integer; + right_value = (double)right.data.integer; } // Determine result type based on operands @@ -441,19 +459,13 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.data.integer = (INT_SIZE)(left_value - right_value); } else if (strcmp(op, "/") == 0) { if (right_value == 0) { - LiteralValue error_val; - error_val.type = TYPE_ERROR; - error_val.data.string = strdup("Division by zero\n"); - return error_val; + return raise_error("Division by zero\n"); } result.type = TYPE_FLOAT; result.data.floating_point = left_value / right_value; } else if (strcmp(op, "//") == 0) { if (right_value == 0) { - LiteralValue error_val; - error_val.type = TYPE_ERROR; - error_val.data.string = strdup("Floor division by zero\n"); - return error_val; + return raise_error("Floor division by zero\n"); } if (result.type == TYPE_FLOAT) result.data.floating_point = floor(left_value / right_value); @@ -461,10 +473,7 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.data.integer = (INT_SIZE)(left_value / right_value); } else if (strcmp(op, "%") == 0) { if (right_value == 0) { - LiteralValue error_val; - error_val.type = TYPE_ERROR; - error_val.data.string = strdup("Modulo by zero\n"); - return error_val; + return raise_error("Modulo by zero\n"); } if (result.type == TYPE_FLOAT) { result.data.floating_point = fmod(left_value, right_value); @@ -496,13 +505,10 @@ LiteralValue evaluate_operator(const char *op, LiteralValue left, result.type = TYPE_BOOLEAN; result.data.boolean = (left_value != right_value); } else { - LiteralValue error_val; - error_val.type = TYPE_ERROR; - error_val.data.string = strdup("Unknown operator"); - return error_val; + return raise_error("Unknown operator"); } - return result; + return make_result(result, false, false); } int get_operator_precedence(const char *op) { @@ -537,36 +543,22 @@ InterpretResult interpret_binary_op(ASTNode *node, Environment *env) { if (left_r.is_error) { return left_r; } - LiteralValue left = left_r.value; // Interpret right operand InterpretResult right_r = interpret_node(node->binary_op.right, env); if (right_r.is_error) { return right_r; } - LiteralValue right = right_r.value; // Evaluate based on operator const char *op = node->binary_op.operator; - LiteralValue op_val = evaluate_operator(op, left, right); - - if (op_val.type == TYPE_ERROR) { - InterpretResult res; - res.value = op_val; - res.did_return = false; - res.did_break = false; - res.is_error = true; - return res; + InterpretResult op_res = evaluate_operator(op, left_r, right_r); + if (op_res.is_error) { + return op_res; } - // Return the operation result - InterpretResult res; - res.value = op_val; - res.did_return = false; - res.did_break = false; - res.is_error = false; - return res; + return make_result(op_res.value, false, false); } Variable *get_variable(Environment *env, const char *variable_name) { @@ -908,8 +900,20 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { // Assign or update loop variable in the environment Variable *var = get_variable(env, loop_var); if (!var) { - // Variable does not exist; create it - var = allocate_variable(env, loop_var); + // Allocate variable and handle InterpretResult + InterpretResult var_res = allocate_variable(env, loop_var); + if (var_res.is_error) { + return var_res; // Propagate the error + } + + // Retrieve the newly allocated variable + var = get_variable(env, loop_var); + if (!var) { + return raise_error( + "Failed to allocate and retrieve variable `%s`.\n", loop_var); + } + + // Initialize the loop variable with the start value if (start_res.value.type == TYPE_FLOAT) { var->value.type = TYPE_FLOAT; var->value.data.floating_point = start_val; @@ -1297,7 +1301,9 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { LiteralValue arg_value = arg_res.value; if (arg_value.type == TYPE_ERROR) { free_environment(&local_env); - return arg_value; + InterpretResult err_res = make_result(arg_value, false, false); + err_res.is_error = true; + return err_res; } Variable param_var = {.variable_name = strdup(p->parameter_name), @@ -1316,7 +1322,7 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { if (r.did_return) { // Short-circuit free_environment(&local_env); - return r.value; + return r; } // Else keep going stmt = stmt->next; diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index a691a5b..d2d8910 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -23,7 +23,7 @@ InterpretResult interpret_conditional(ASTNode *node, Environment *env); InterpretResult interpret_while_loop(ASTNode *node, Environment *env); InterpretResult interpret_for_loop(ASTNode *node, Environment *env); InterpretResult interpret_switch(ASTNode *node, Environment *env); -void interpret_function_declaration(ASTNode *node, Environment *env); +InterpretResult interpret_function_declaration(ASTNode *node, Environment *env); InterpretResult interpret_function_call(ASTNode *node, Environment *env); InterpretResult interpret_unary_op(ASTNode *node, Environment *env); LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand); From 500ed20a3a401ed1a72df36171fe78757711311c Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:15:54 +0000 Subject: [PATCH 15/62] Fix: `builtins.c`/`.h` now embraces InterpretResult #179 --- src/interpreter/builtins.c | 119 +++++++++++++++++++++---------------- src/interpreter/builtins.h | 4 +- 2 files changed, 69 insertions(+), 54 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 22b556e..9ce42c1 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -13,19 +13,21 @@ } while (0) // Function to interpret a mix of argument types -bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, - ArgumentSpec *specs) { - // size_t arg_count = 0; +InterpretResult interpret_arguments(ASTNode *node, Environment *env, + size_t num_args, ArgumentSpec *specs) { ASTNode *arg_node = node; for (size_t i = 0; i < num_args; i++) { if (arg_node == NULL) { - error_interpreter("Too few arguments provided.\n"); - return false; + return raise_error("Too few arguments provided.\n"); } // Interpret the current argument InterpretResult arg_res = interpret_node(arg_node, env); + if (arg_res.is_error) { + // Propagate the error + return arg_res; + } LiteralValue lv = arg_res.value; // Reference to the current argument specification @@ -42,9 +44,8 @@ bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, } else if (lv.type == TYPE_BOOLEAN) { *((INT_SIZE *)output_ptr) = lv.data.boolean ? 1 : 0; } else { - error_interpreter("Expected integer for argument %zu.\n", - i + 1); - return false; + return raise_error("Expected integer for argument %zu.\n", + i + 1); } break; @@ -56,8 +57,7 @@ bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, } else if (lv.type == TYPE_BOOLEAN) { *((FLOAT_SIZE *)output_ptr) = lv.data.boolean ? 1.0 : 0.0; } else { - error_interpreter("Expected float for argument %zu.\n", i + 1); - return false; + return raise_error("Expected float for argument %zu.\n", i + 1); } break; @@ -65,8 +65,8 @@ bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, if (lv.type == TYPE_STRING) { *((char **)output_ptr) = lv.data.string; } else { - error_interpreter("Expected string for argument %zu.\n", i + 1); - return false; + return raise_error("Expected string for argument %zu.\n", + i + 1); } break; @@ -78,30 +78,26 @@ bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, } else if (lv.type == TYPE_FLOAT) { *((bool *)output_ptr) = (lv.data.floating_point != 0.0); } else { - error_interpreter("Expected boolean for argument %zu.\n", - i + 1); - return false; + return raise_error("Expected boolean for argument %zu.\n", + i + 1); } break; - // Handle additional types as needed - default: - error_interpreter("Unknown argument type for argument %zu.\n", - i + 1); - return false; + return raise_error("Unknown argument type for argument %zu.\n", + i + 1); } - // arg_count++; arg_node = arg_node->next; } if (arg_node != NULL) { - error_interpreter("Too many arguments provided.\n"); - return false; + return raise_error("Too many arguments provided.\n"); } - return true; + // Indicate success + LiteralValue success_val = {.type = TYPE_BOOLEAN, .data.boolean = true}; + return make_result(success_val, false, false); } void print_formatted_string(const char *str) { @@ -133,7 +129,7 @@ void print_formatted_string(const char *str) { } // Built-in `input()` function -LiteralValue builtin_input(ASTNode *node, Environment *env) { +InterpretResult builtin_input(ASTNode *node, Environment *env) { (void)node; // Unused parameter, suppress compiler warning (void)env; // Unused parameter @@ -142,7 +138,8 @@ LiteralValue builtin_input(ASTNode *node, Environment *env) { char *input_buffer = malloc(buffer_size); if (!input_buffer) { fprintf(stderr, "Error: Failed to allocate memory for input buffer.\n"); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } int c; @@ -155,7 +152,8 @@ LiteralValue builtin_input(ASTNode *node, Environment *env) { stderr, "Error: Failed to reallocate memory for input buffer.\n"); free(input_buffer); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } input_buffer = new_buffer; } @@ -170,16 +168,17 @@ LiteralValue builtin_input(ASTNode *node, Environment *env) { if (!result.data.string) { fprintf(stderr, "Error: Failed to duplicate input string.\n"); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } DEBUG_PRINT_FLOAT("Input received: `%s`\n", result.data.string); - return result; + return make_result(result, false, false); } // Built-in `random()` function with 0, 1, or 2 arguments -LiteralValue builtin_random(ASTNode *node, Environment *env) { +InterpretResult builtin_random(ASTNode *node, Environment *env) { FLOAT_SIZE min = 0.0L; FLOAT_SIZE max = 1.0L; @@ -189,9 +188,11 @@ LiteralValue builtin_random(ASTNode *node, Environment *env) { specs[1].type = ARG_TYPE_FLOAT; specs[1].out_ptr = &max; - if (!interpret_arguments(node->function_call.arguments, env, 2, specs)) { + if (!interpret_arguments(node->function_call.arguments, env, 2, specs) + .is_error) { // Return an error type on failure - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } // Seed the random number generator once @@ -218,11 +219,11 @@ LiteralValue builtin_random(ASTNode *node, Environment *env) { result.type = TYPE_FLOAT; result.data.floating_point = random_number; - return result; + return make_result(result, false, false); } // Built-in `serve()` function for printing -LiteralValue builtin_output(ASTNode *node, Environment *env) { +InterpretResult builtin_output(ASTNode *node, Environment *env) { ASTNode *arg_node = node->function_call.arguments; while (arg_node != NULL) { InterpretResult r = interpret_node(arg_node, env); @@ -257,8 +258,9 @@ LiteralValue builtin_output(ASTNode *node, Environment *env) { } printf("\n"); - return (LiteralValue){.type = TYPE_INTEGER, - .data.integer = 0}; // Return 0 as default + LiteralValue lv = {.type = TYPE_INTEGER, + .data.integer = 0}; // Return 0 as default + return make_result(lv, false, false); } // Built-in `burn()` function to raise errors @@ -550,21 +552,24 @@ char *process_escape_sequences(const char *input) { return processed; } -LiteralValue builtin_file_read(ASTNode *node, Environment *env) { +InterpretResult builtin_file_read(ASTNode *node, Environment *env) { char *filepath; ArgumentSpec specs[1]; specs[0].type = ARG_TYPE_STRING; specs[0].out_ptr = &filepath; - if (!interpret_arguments(node->function_call.arguments, env, 1, specs)) { - return (LiteralValue){.type = TYPE_ERROR}; + if (!interpret_arguments(node->function_call.arguments, env, 1, specs) + .is_error) { + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } FILE *file = fopen(filepath, "r"); if (file == NULL) { perror("Failed to open file"); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } size_t buffer_size = 1024; @@ -575,7 +580,8 @@ LiteralValue builtin_file_read(ASTNode *node, Environment *env) { if (file_contents == NULL) { perror("Failed to allocate memory for file contents"); fclose(file); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } // Initialize buffer for reading each line @@ -593,7 +599,8 @@ LiteralValue builtin_file_read(ASTNode *node, Environment *env) { free(buffer); free(file_contents); fclose(file); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } file_contents = temp; @@ -608,10 +615,12 @@ LiteralValue builtin_file_read(ASTNode *node, Environment *env) { free(buffer); fclose(file); - return (LiteralValue){.type = TYPE_STRING, .data.string = file_contents}; + LiteralValue lv = {.type = TYPE_STRING, .data.string = file_contents}; + return make_result(lv, false, false); } -LiteralValue helper_file_writer(ASTNode *node, Environment *env, bool append) { +InterpretResult helper_file_writer(ASTNode *node, Environment *env, + bool append) { char *filepath; char *content; @@ -622,40 +631,46 @@ LiteralValue helper_file_writer(ASTNode *node, Environment *env, bool append) { specs[1].type = ARG_TYPE_STRING; specs[1].out_ptr = &content; - if (!interpret_arguments(node->function_call.arguments, env, 2, specs)) { - return (LiteralValue){.type = TYPE_ERROR}; + if (!interpret_arguments(node->function_call.arguments, env, 2, specs) + .is_error) { + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } // Process the content to handle escape sequences char *processed_content = process_escape_sequences(content); if (processed_content == NULL) { - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } FILE *file = fopen(filepath, append ? "a" : "w"); if (file == NULL) { perror("Failed to open file for writing"); free(processed_content); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } if (fputs(processed_content, file) == EOF) { perror("Failed to write to file"); free(processed_content); fclose(file); - return (LiteralValue){.type = TYPE_ERROR}; + LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; + return make_result(lv, false, false); } free(processed_content); fclose(file); - return (LiteralValue){.type = TYPE_BOOLEAN, .data.boolean = true}; + LiteralValue lv = {.type = TYPE_BOOLEAN, .data.boolean = true}; + return make_result(lv, false, false); } -LiteralValue builtin_file_write(ASTNode *node, Environment *env) { +InterpretResult builtin_file_write(ASTNode *node, Environment *env) { return helper_file_writer(node, env, false); } -LiteralValue builtin_file_append(ASTNode *node, Environment *env) { +InterpretResult builtin_file_append(ASTNode *node, Environment *env) { return helper_file_writer(node, env, true); } diff --git a/src/interpreter/builtins.h b/src/interpreter/builtins.h index dfae2a8..580e00c 100644 --- a/src/interpreter/builtins.h +++ b/src/interpreter/builtins.h @@ -33,8 +33,8 @@ InterpretResult builtin_file_write(ASTNode *node, Environment *env); InterpretResult builtin_file_append(ASTNode *node, Environment *env); // Helpers -bool interpret_arguments(ASTNode *node, Environment *env, size_t num_args, - ArgumentSpec *specs); +InterpretResult interpret_arguments(ASTNode *node, Environment *env, + size_t num_args, ArgumentSpec *specs); void print_formatted_string(const char *str); bool is_valid_int(const char *str, INT_SIZE *out_value); bool is_valid_float(const char *str, FLOAT_SIZE *out_value); From 96cd87127e245b1c9d1862868ed7930dc240f774 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:23:25 +0000 Subject: [PATCH 16/62] Update builtins.c #179 --- src/interpreter/builtins.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 9ce42c1..e157c8f 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -559,8 +559,9 @@ InterpretResult builtin_file_read(ASTNode *node, Environment *env) { specs[0].type = ARG_TYPE_STRING; specs[0].out_ptr = &filepath; - if (!interpret_arguments(node->function_call.arguments, env, 1, specs) - .is_error) { + InterpretResult args_res = + interpret_arguments(node->function_call.arguments, env, 1, specs); + if (args_res.is_error) { LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; return make_result(lv, false, false); } @@ -631,8 +632,9 @@ InterpretResult helper_file_writer(ASTNode *node, Environment *env, specs[1].type = ARG_TYPE_STRING; specs[1].out_ptr = &content; - if (!interpret_arguments(node->function_call.arguments, env, 2, specs) - .is_error) { + InterpretResult args_res = + interpret_arguments(node->function_call.arguments, env, 2, specs); + if (args_res.is_error) { LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; return make_result(lv, false, false); } From feaae24c7f66c002fd3dd8381bd5a0107360ae0b Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:23:48 +0000 Subject: [PATCH 17/62] Fix: Incorrect types of some interpreter functions #179 --- src/interpreter/interpreter.c | 5 ++--- src/interpreter/interpreter.h | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 5753d02..71fb7fb 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -701,7 +701,6 @@ InterpretResult interpret_conditional(ASTNode *node, Environment *env) { return raise_error("Invalid conditional node."); } - InterpretResult result = {0}; bool condition_met = false; ASTNode *current_branch = node; @@ -1158,7 +1157,8 @@ InterpretResult call_user_defined_function(Function *func_ref, return func_res; } -void interpret_function_declaration(ASTNode *node, Environment *env) { +InterpretResult interpret_function_declaration(ASTNode *node, + Environment *env) { debug_print_int("`interpret_function_declaration()` called\n"); if (!node || !node->function_call.name) { @@ -1315,7 +1315,6 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { } // interpret function body - LiteralValue result = create_default_value(); ASTNode *stmt = func->body; while (stmt) { InterpretResult r = interpret_node(stmt, &local_env); diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index d2d8910..cfcf6a9 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -31,13 +31,14 @@ InterpretResult interpret_ternary(ASTNode *node, Environment *env); InterpretResult call_user_defined_function(Function *func_ref, ASTNode *call_node, Environment *env); +InterpretResult interpret_try(ASTNode *node, Environment *env); // Interpret program void interpret_program(ASTNode *program, Environment *env); // Helpers Variable *get_variable(Environment *env, const char *variable_name); -void add_variable(Environment *env, Variable var); +InterpretResult add_variable(Environment *env, Variable var); ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params); #endif \ No newline at end of file From 6292476772c0249280343d3d5d0c8fb0bfe563b6 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:26:28 +0000 Subject: [PATCH 18/62] Update interpreter.c #179 --- src/interpreter/interpreter.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 71fb7fb..5201741 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -310,8 +310,8 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { if (strcmp(op, "!") == 0) { // Ensure operand is boolean if (operand.type != TYPE_BOOLEAN) { - error_interpreter( - "Unary operator `%s` requires a boolean operand.\n", op); + raise_error("Unary operator `%s` requires a boolean operand.\n", + op); } result.type = TYPE_BOOLEAN; @@ -328,8 +328,8 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { result.type = TYPE_FLOAT; result.data.floating_point = -operand.data.floating_point; } else { - error_interpreter( - "Unary operator `%s` requires a numeric operand.\n", op); + raise_error("Unary operator `%s` requires a numeric operand.\n", + op); } return result; } @@ -343,7 +343,7 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { // Add more unary operators as needed - error_interpreter("Unknown unary operator `%s`\n", op); + raise_error("Unknown unary operator `%s`\n", op); return result; // unreachable } @@ -1162,7 +1162,7 @@ InterpretResult interpret_function_declaration(ASTNode *node, debug_print_int("`interpret_function_declaration()` called\n"); if (!node || !node->function_call.name) { - error_interpreter("Invalid function declaration\n"); + raise_error("Invalid function declaration\n"); } // Initialize param_list as NULL @@ -1174,14 +1174,14 @@ InterpretResult interpret_function_declaration(ASTNode *node, while (param) { ASTFunctionParameter *new_param = malloc(sizeof(ASTFunctionParameter)); if (!new_param) { - error_interpreter( + raise_error( "Error: Memory allocation failed for function parameter\n"); } new_param->parameter_name = strdup(param->parameter_name); if (!new_param->parameter_name) { free(new_param); - error_interpreter("Memory allocation failed for parameter name\n"); + raise_error("Memory allocation failed for parameter name\n"); } new_param->next = NULL; @@ -1216,6 +1216,8 @@ InterpretResult interpret_function_declaration(ASTNode *node, .is_constant = false}; add_variable(env, var); + + return make_result(create_default_value(), false, false); } InterpretResult interpret_function_call(ASTNode *node, Environment *env) { From 699f116739a8c5f68edf6b43dbefe56ba6fd90d2 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:29:04 +0000 Subject: [PATCH 19/62] Add: New `fatal_error` interpreter util #179 --- src/interpreter/interpreter.c | 12 ++++++------ src/interpreter/utils.c | 33 +++++++++++++++++++++------------ src/interpreter/utils.h | 4 ++++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 5201741..f5af720 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -310,7 +310,7 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { if (strcmp(op, "!") == 0) { // Ensure operand is boolean if (operand.type != TYPE_BOOLEAN) { - raise_error("Unary operator `%s` requires a boolean operand.\n", + fatal_error("Unary operator `%s` requires a boolean operand.\n", op); } @@ -328,7 +328,7 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { result.type = TYPE_FLOAT; result.data.floating_point = -operand.data.floating_point; } else { - raise_error("Unary operator `%s` requires a numeric operand.\n", + fatal_error("Unary operator `%s` requires a numeric operand.\n", op); } return result; @@ -343,7 +343,7 @@ LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { // Add more unary operators as needed - raise_error("Unknown unary operator `%s`\n", op); + fatal_error("Unknown unary operator `%s`\n", op); return result; // unreachable } @@ -1162,7 +1162,7 @@ InterpretResult interpret_function_declaration(ASTNode *node, debug_print_int("`interpret_function_declaration()` called\n"); if (!node || !node->function_call.name) { - raise_error("Invalid function declaration\n"); + fatal_error("Invalid function declaration\n"); } // Initialize param_list as NULL @@ -1174,14 +1174,14 @@ InterpretResult interpret_function_declaration(ASTNode *node, while (param) { ASTFunctionParameter *new_param = malloc(sizeof(ASTFunctionParameter)); if (!new_param) { - raise_error( + fatal_error( "Error: Memory allocation failed for function parameter\n"); } new_param->parameter_name = strdup(param->parameter_name); if (!new_param->parameter_name) { free(new_param); - raise_error("Memory allocation failed for parameter name\n"); + fatal_error("Memory allocation failed for parameter name\n"); } new_param->next = NULL; diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 9b5151e..0cfe993 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -28,6 +28,17 @@ InterpretResult raise_error(const char *format, ...) { return res; } +void fatal_error(const char *format, ...) { + char error_message[1024]; + va_list args; + va_start(args, format); + fprintf(stderr, "\033[31mError: "); + vsnprintf(error_message, sizeof(error_message), format, args); + fprintf(stderr, "%s\033[0m\n", error_message); + va_end(args); + exit(1); +} + void initialize_builtin_function(Environment *env, const char *name) { Function func; memset(&func, 0, sizeof(Function)); // zero out for safety @@ -55,7 +66,7 @@ void init_environment(Environment *env) { env->capacity = 10; env->variables = malloc(env->capacity * sizeof(Variable)); if (!env->variables) { - error_interpreter("Failed to allocate memory for variables.\n"); + fatal_error("Failed to allocate memory for variables.\n"); } // Add function initialization @@ -63,7 +74,7 @@ void init_environment(Environment *env) { env->function_capacity = 10; env->functions = malloc(env->function_capacity * sizeof(Function)); if (!env->functions) { - error_interpreter("Failed to allocate memory for functions.\n"); + fatal_error("Failed to allocate memory for functions.\n"); } initialize_all_builtin_functions(env); @@ -104,15 +115,14 @@ ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params) { while (params) { ASTFunctionParameter *new_param = malloc(sizeof(ASTFunctionParameter)); if (!new_param) { - error_interpreter( + fatal_error( "Memory allocation failed for function parameter copy.\n"); } new_param->parameter_name = strdup(params->parameter_name); if (!new_param->parameter_name) { free(new_param); - error_interpreter( - "Memory allocation failed for parameter name copy.\n"); + fatal_error("Memory allocation failed for parameter name copy.\n"); } new_param->next = NULL; @@ -136,7 +146,7 @@ ASTNode *copy_ast_node(ASTNode *node) { ASTNode *new_node = malloc(sizeof(ASTNode)); if (!new_node) { - error_interpreter("Memory allocation failed in `copy_ast_node`\n"); + fatal_error("Memory allocation failed in `copy_ast_node`\n"); } memcpy(new_node, node, sizeof(ASTNode)); @@ -169,7 +179,7 @@ void add_function(Environment *env, Function func) { if (!env->functions && env->function_capacity == 0) { env->functions = calloc(4, sizeof(Function)); if (!env->functions) { - error_interpreter("Initial allocation for functions failed.\n"); + fatal_error("Initial allocation for functions failed.\n"); } env->function_capacity = 4; } @@ -180,14 +190,14 @@ void add_function(Environment *env, Function func) { Function *new_functions = realloc(env->functions, new_capacity * sizeof(Function)); if (!new_functions) { - error_interpreter("Memory allocation failed for functions.\n"); + fatal_error("Memory allocation failed for functions.\n"); } env->functions = new_functions; env->function_capacity = new_capacity; } if (!func.name) { - error_interpreter("Function name is `NULL` or invalid.\n"); + fatal_error("Function name is `NULL` or invalid.\n"); } // Create a deep copy @@ -199,7 +209,7 @@ void add_function(Environment *env, Function func) { stored_func->name = strdup(func.name); if (!stored_func->name) { free(stored_func); - error_interpreter("Memory allocation failed for function name.\n"); + fatal_error("Memory allocation failed for function name.\n"); } debug_print_int("Function `%s` added successfully.\n", stored_func->name); @@ -215,8 +225,7 @@ Function *get_function(Environment *env, const char *name) { } // A helper to wrap `LiteralValue` in `InterpretResult` -static InterpretResult make_result(LiteralValue val, bool did_return, - bool did_break) { +InterpretResult make_result(LiteralValue val, bool did_return, bool did_break) { InterpretResult r; r.value = val; r.did_return = did_return; diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h index 6aa2454..6afd14d 100644 --- a/src/interpreter/utils.h +++ b/src/interpreter/utils.h @@ -14,8 +14,11 @@ void init_environment(Environment *env); // Free the environment void free_environment(Environment *env); +// Errors InterpretResult raise_error(const char *format, ...); +void fatal_error(const char *format, ...); +// Functions void free_parameter_list(ASTFunctionParameter *head); ASTFunctionParameter * copy_function_parameters(ASTFunctionParameter *param_list); @@ -23,6 +26,7 @@ ASTNode *copy_ast_node(ASTNode *node); void add_function(Environment *env, Function func); Function *get_function(Environment *env, const char *name); +// Helpers InterpretResult make_result(LiteralValue val, bool did_return, bool did_break); #endif From 6816e656de73275ac7c54385dd9301d86734edf1 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:35:29 +0000 Subject: [PATCH 20/62] Fix: `raise_error` now prints actual message #179 --- src/interpreter/utils.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 0cfe993..41b1c1d 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -4,12 +4,14 @@ InterpretResult raise_error(const char *format, ...) { char error_message[1024]; va_list args; va_start(args, format); - printf("\033[31m"); // red text color - printf("Error: "); + + // Format the error message into the buffer vsnprintf(error_message, sizeof(error_message), format, args); - printf("\033[0m\n"); // reset text color va_end(args); + // Print "Error:" in red, followed by the formatted error message + printf("\033[31mError: %s\033[0m\n", error_message); + // Create an error LiteralValue LiteralValue error_value; error_value.type = TYPE_ERROR; From a0fc63deb4b852f42d723af1b59be2f89fb9b8cd Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:36:51 +0000 Subject: [PATCH 21/62] Rename: `AST_RESCUE` to `AST_CATCH` & `AST_FINISH` to `AST_FINALLY` #179 --- src/parser/utils.c | 12 +++++++++++- src/shared/ast_types.h | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/parser/utils.c b/src/parser/utils.c index 610d639..837ad16 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -68,7 +68,7 @@ void free_ast(ASTNode *node) { free_ast(node->ternary.false_expr); break; - case AST_SWITCH: + case AST_SWITCH: { if (node->switch_case.expression) { free_ast(node->switch_case.expression); } @@ -95,6 +95,16 @@ void free_ast(ASTNode *node) { } break; + } + + case AST_TRY: + break; + + case AST_CATCH: + break; + + case AST_FINALLY: + break; default: fprintf(stderr, diff --git a/src/shared/ast_types.h b/src/shared/ast_types.h index 9b1344e..2f9080d 100644 --- a/src/shared/ast_types.h +++ b/src/shared/ast_types.h @@ -25,8 +25,8 @@ typedef enum { AST_VARIABLE, AST_CONSTANT, AST_TRY, - AST_RESCUE, - AST_FINISH + AST_CATCH, + AST_FINALLY } ASTNodeType; // Literal Node From 79745afb7e376de97d196c20932ba3a962aa0d6e Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:37:33 +0000 Subject: [PATCH 22/62] Update 7_try_catch.flv #179 --- src/tests/7_try_catch.flv | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/7_try_catch.flv b/src/tests/7_try_catch.flv index e5b85f8..6df0c2a 100644 --- a/src/tests/7_try_catch.flv +++ b/src/tests/7_try_catch.flv @@ -1,4 +1,5 @@ try { + serve("This will run!"); burn("This recipe failed!"); serve("This won't run!"); } rescue { From fd2f2e55f246a15e38cad1846c36d429725e258c Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:37:52 +0000 Subject: [PATCH 23/62] Update all.flv #179 --- src/tests/all.flv | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/tests/all.flv b/src/tests/all.flv index c02683c..7f89993 100644 --- a/src/tests/all.flv +++ b/src/tests/all.flv @@ -75,12 +75,22 @@ serve(result); # ================================================== # 7 # ================================================== -# try { -# burn("This recipe failed!"); -# serve("This won't run!"); -# } rescue { -# serve("Caught an error: Recipe needs improvement."); -# } +try { + serve("This will run!"); + burn("This recipe failed!"); + serve("This won't run!"); +} rescue { + serve("Caught an error: Recipe needs improvement."); +} + +try { + int("abc"); + serve("This won't run!"); +} rescue { + serve("Runtime error: Can't cast string `abc` to int."); +} finish { + serve("This always executes!"); +} # ================================================== From 7fa2991ad5cec01e0f6f046e4406a6528fd2da95 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:39:16 +0000 Subject: [PATCH 24/62] Update syntax_examples.md #179 --- docs/syntax_examples.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/syntax_examples.md b/docs/syntax_examples.md index 3c5653c..4b1ca1f 100644 --- a/docs/syntax_examples.md +++ b/docs/syntax_examples.md @@ -117,11 +117,21 @@ Use `try` and `rescue` to handle errors. ```py try { + serve("This will run!"); burn("This recipe failed!"); serve("This won't run!"); } rescue { serve("Caught an error: Recipe needs improvement."); } + +try { + int("abc"); + serve("This won't run!"); +} rescue { + serve("Runtime error: Can't cast string `abc` to int."); +} finish { + serve("This always executes!"); +} ``` ### 8. 🔎 Switch-Case Logic From 9f8b79ad7973dff9ab3047672d13e32940c06b55 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:39:58 +0000 Subject: [PATCH 25/62] Remove: Implemented column from Syntax Keywords in `docs/language_design.md` #179 --- docs/language_design.md | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/language_design.md b/docs/language_design.md index c4150f9..a8363e3 100644 --- a/docs/language_design.md +++ b/docs/language_design.md @@ -16,31 +16,31 @@ This `docs/` page details the core design of FlavorLang's syntax, the various da ## Syntax Keywords -| Keyword | Usage | Description | Implemented? | -| --------- | ---------------------------- | ------------------------------------------------------------------------------------------- | ------------ | -| `let` | Define variables | Declares and initializes variables. | ✅ | -| `const` | Define constants | Declares and initializes constants. | ✅ | -| `if` | Conditional logic | Executes code only if a condition is true. | ✅ | -| `elif` | Conditional logic fallback | Executes only if a prior `if` condition is false. | ✅ | -| `else` | Conditional fallback | Executes code if any prior `if`/`is` conditions are false. | ✅ | -| `for` | For-loop | Iterates over a range or sequence, executing a block of code for each step. | ✅ | -| `in` | Range declaration | Specifies the range or sequence to iterate over. | ✅ | -| `by` | Optional step specifier | Defines the step interval for iteration; defaults to `1`/`-1` (range dependent) if omitted. | ✅ | -| `while` | While-loop | Repeatedly runs code while a condition is true. | ✅ | -| `check` | Switch-case equivalent | Matches a value to multiple cases. | ✅ | -| `is` | Case clause | Defines a case inside `check`. | ✅ | -| `break` | Exit control flow | Stops execution of further cases in `check` and exits the current flow. | ✅ | -| `create` | Define a function | Creates a reusable block of logic. | ✅ | -| `deliver` | Return statement | Returns a value and stops function execution. | ✅ | -| `try` | Try block | Executes code that might fail. | ✅ | -| `rescue` | Catch block | Handles errors during execution. | ✅ | -| `finish` | Finally block | Optional cleanup & always runs. | ✅ | -| `burn` | Force exit or raise an error | Stops execution immediately with a message. | ✅ | -| `serve` | Print or output | Outputs a value or message immediately. | ✅ | -| `sample` | Input from console | Reads user input. | ✅ | -| `plate` | Write to file | Writes data to a file. | ✅ | -| `garnish` | Append to file | Appends data to a file. | ✅ | -| `taste` | Read from file | Reads data from a file. | ✅ | +| Keyword | Usage | Description | +| --------- | ---------------------------- | ------------------------------------------------------------------------------------------- | +| `let` | Define variables | Declares and initializes variables. | +| `const` | Define constants | Declares and initializes constants. | +| `if` | Conditional logic | Executes code only if a condition is true. | +| `elif` | Conditional logic fallback | Executes only if a prior `if` condition is false. | +| `else` | Conditional fallback | Executes code if any prior `if`/`is` conditions are false. | +| `for` | For-loop | Iterates over a range or sequence, executing a block of code for each step. | +| `in` | Range declaration | Specifies the range or sequence to iterate over. | +| `by` | Optional step specifier | Defines the step interval for iteration; defaults to `1`/`-1` (range dependent) if omitted. | +| `while` | While-loop | Repeatedly runs code while a condition is true. | +| `check` | Switch-case equivalent | Matches a value to multiple cases. | +| `is` | Case clause | Defines a case inside `check`. | +| `break` | Exit control flow | Stops execution of further cases in `check` and exits the current flow. | +| `create` | Define a function | Creates a reusable block of logic. | +| `deliver` | Return statement | Returns a value and stops function execution. | +| `try` | Try block | Executes code that might fail. | +| `rescue` | Catch block | Handles errors during execution. | +| `finish` | Finally block | Optional cleanup & always runs. | +| `burn` | Force exit or raise an error | Stops execution immediately with a message. | +| `serve` | Print or output | Outputs a value or message immediately. | +| `sample` | Input from console | Reads user input. | +| `plate` | Write to file | Writes data to a file. | +| `garnish` | Append to file | Appends data to a file. | +| `taste` | Read from file | Reads data from a file. | --- From 3a649bf5d174789f1721ac37771b0c62d00fca85 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:49:05 +0000 Subject: [PATCH 26/62] Fix: Incorrect token expected in `parse_ternary` #179 --- src/parser/operator_parser.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parser/operator_parser.c b/src/parser/operator_parser.c index 9608402..9442f32 100644 --- a/src/parser/operator_parser.c +++ b/src/parser/operator_parser.c @@ -24,8 +24,7 @@ ASTNode *parse_ternary(ParserState *state) { // Recursively parse expression for `True` branch (allows for nesting) ASTNode *true_expr = parse_ternary(state); - expect_token(state, TOKEN_OPERATOR, - "Expected `:` in ternary expression"); + expect_token(state, TOKEN_COLON, "Expected `:` in ternary expression"); // Recursively parse expression for `False` branch ASTNode *false_expr = parse_ternary(state); From 7e524e7b0e80ebfe96909d6394d959eb5e76f3f6 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:04:41 +0000 Subject: [PATCH 27/62] Update: `free_ast` for `AST_TRY`, `AST_CATCH`, and `AST_FINALLY` #179 --- src/parser/utils.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/parser/utils.c b/src/parser/utils.c index 837ad16..8846030 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -98,11 +98,41 @@ void free_ast(ASTNode *node) { } case AST_TRY: - break; + // Free try block + if (node->try_block.try_block) { + free_ast(node->try_block.try_block); + } - case AST_CATCH: + // Free all catch blocks + if (node->try_block.catch_blocks) { + ASTCatchNode *catch_node = node->try_block.catch_blocks; + + while (catch_node) { + ASTCatchNode *next_catch = catch_node->next; + + // Free the error variable (if allocated) + if (catch_node->error_variable) { + free(catch_node->error_variable); + } + + // Free the body of the catch block + if (catch_node->body) { + free_ast(catch_node->body); + } + + free(catch_node); + catch_node = next_catch; + } + } + + // Free finally block + if (node->try_block.finally_block) { + free_ast(node->try_block.finally_block); + } break; + // Already handled by `AST_TRY` + case AST_CATCH: case AST_FINALLY: break; From cf4850febc4b41c9bc61647c82f8e5b842fa3161 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:05:06 +0000 Subject: [PATCH 28/62] Fix: Incorrect field used for `AST_FUNCTION_RETURN` return value #179 --- src/interpreter/interpreter.c | 6 +----- src/parser/parser.c | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index f5af720..bea82bf 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -64,15 +64,11 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) { } case AST_FUNCTION_RETURN: { - debug_print_int("\tMatched: `AST_FUNCTION_RETURN`\n"); - // Extract the return value InterpretResult return_res = - interpret_node(node->assignment.value, env); + interpret_node(node->function_call.return_data, env); if (return_res.is_error) { - // Propagate the error return return_res; } - // Set the did_return flag return_res.did_return = true; return return_res; } diff --git a/src/parser/parser.c b/src/parser/parser.c index ace11ce..d99d71d 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -294,8 +294,7 @@ ASTNode *parse_function_return(ParserState *state) { } node->type = AST_FUNCTION_RETURN; - // node->function_call.return_data = parse_expression(state); - node->assignment.value = parse_expression(state); + node->function_call.return_data = parse_expression(state); node->next = NULL; expect_token(state, TOKEN_DELIMITER, From 59ee4d606e154d26bc52653d0b6afd895651b562 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:23:07 +0000 Subject: [PATCH 29/62] Update: Makefile to use Clang #179 --- src/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 4954a88..161a50c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ # Compiler and Flags -CC = gcc -CFLAGS = -Wall -Wextra -g -LDFLAGS = -lm +CC = clang +CFLAGS = -fsanitize=address -fsanitize=undefined -g -Wall -Wextra -pedantic +LDFLAGS = -fsanitize=address -fsanitize=undefined # Directories SRC_DIRS = . shared lexer parser interpreter debug From e6f87da8385d2df62be4065af2464983f400f534 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:23:40 +0000 Subject: [PATCH 30/62] Add: Newline at end of files that Clang raised a warning for #179 --- src/debug/debug.c | 2 +- src/interpreter/interpreter.h | 2 +- src/lexer/keywords.c | 2 +- src/lexer/keywords.h | 2 +- src/main.c | 2 +- src/parser/operator_parser.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/debug/debug.c b/src/debug/debug.c index aee93cb..6cc37c3 100644 --- a/src/debug/debug.c +++ b/src/debug/debug.c @@ -104,4 +104,4 @@ void debug_print_int(const char *format, ...) { debug_print(INTERPRETER, "%s", new_format); } -} \ No newline at end of file +} diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index cfcf6a9..ed6add1 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -41,4 +41,4 @@ Variable *get_variable(Environment *env, const char *variable_name); InterpretResult add_variable(Environment *env, Variable var); ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params); -#endif \ No newline at end of file +#endif diff --git a/src/lexer/keywords.c b/src/lexer/keywords.c index 684c275..d5c4fc7 100644 --- a/src/lexer/keywords.c +++ b/src/lexer/keywords.c @@ -77,4 +77,4 @@ int is_valid_identifier_char(char c) { return isalnum(c) || c == '_'; } int is_whitespace(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -} \ No newline at end of file +} diff --git a/src/lexer/keywords.h b/src/lexer/keywords.h index a63e1e6..435b3a7 100644 --- a/src/lexer/keywords.h +++ b/src/lexer/keywords.h @@ -103,4 +103,4 @@ int is_valid_identifier_char(char c); */ int is_whitespace(char c); -#endif \ No newline at end of file +#endif diff --git a/src/main.c b/src/main.c index 292ba87..1bc07f7 100644 --- a/src/main.c +++ b/src/main.c @@ -101,4 +101,4 @@ int main(int argc, char **argv) { free_ast(ast); return 0; -} \ No newline at end of file +} diff --git a/src/parser/operator_parser.h b/src/parser/operator_parser.h index 8f2d5f9..2243ef9 100644 --- a/src/parser/operator_parser.h +++ b/src/parser/operator_parser.h @@ -30,4 +30,4 @@ ASTNode *create_literal_node(Token *token); ASTNode *create_variable_node(char *name); ASTNode *create_function_call_node(char *name, ASTNode *args); -#endif \ No newline at end of file +#endif From aa6e659a3de748b91827589ed15742fd76d139b4 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:34:23 +0000 Subject: [PATCH 31/62] Fix: Clang warnings about not using `void` for functions with 0 args #179 --- src/interpreter/builtins.c | 2 +- src/interpreter/builtins.h | 2 +- src/interpreter/interpreter.c | 2 +- src/interpreter/interpreter.h | 1 + src/main.c | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index e157c8f..355f52c 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -492,7 +492,7 @@ InterpretResult builtin_cast(ASTNode *node, Environment *env) { return result_res; } -InterpretResult builtin_time() { +InterpretResult builtin_time(void) { time_t current_time = time(NULL); if (current_time == -1) { diff --git a/src/interpreter/builtins.h b/src/interpreter/builtins.h index 580e00c..ae85290 100644 --- a/src/interpreter/builtins.h +++ b/src/interpreter/builtins.h @@ -27,7 +27,7 @@ InterpretResult builtin_random(ASTNode *node, Environment *env); InterpretResult builtin_output(ASTNode *node, Environment *env); InterpretResult builtin_error(ASTNode *node, Environment *env); InterpretResult builtin_cast(ASTNode *node, Environment *env); -InterpretResult builtin_time(); +InterpretResult builtin_time(void); InterpretResult builtin_file_read(ASTNode *node, Environment *env); InterpretResult builtin_file_write(ASTNode *node, Environment *env); InterpretResult builtin_file_append(ASTNode *node, Environment *env); diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index bea82bf..4eddb76 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -1,7 +1,7 @@ #include "interpreter.h" // Helper function to create a default LiteralValue (zero number) -LiteralValue create_default_value() { +LiteralValue create_default_value(void) { LiteralValue value = { .type = TYPE_INTEGER, .data = { diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index ed6add1..3063a5d 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -37,6 +37,7 @@ InterpretResult interpret_try(ASTNode *node, Environment *env); void interpret_program(ASTNode *program, Environment *env); // Helpers +LiteralValue create_default_value(void); Variable *get_variable(Environment *env, const char *variable_name); InterpretResult add_variable(Environment *env, Variable var); ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params); diff --git a/src/main.c b/src/main.c index 1bc07f7..008bb5a 100644 --- a/src/main.c +++ b/src/main.c @@ -30,7 +30,7 @@ void print_logo_from_file(const char *filename) { fclose(file); } -void print_about() { +void print_about(void) { printf("\n"); print_logo_from_file("../logo/logo.txt"); printf("\n"); From ea5bf89f5cedac24d4343af47f7299986c64978a Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:34:29 +0000 Subject: [PATCH 32/62] Update builtins.c #179 --- src/interpreter/builtins.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 355f52c..2dd4af7 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -188,9 +188,9 @@ InterpretResult builtin_random(ASTNode *node, Environment *env) { specs[1].type = ARG_TYPE_FLOAT; specs[1].out_ptr = &max; - if (!interpret_arguments(node->function_call.arguments, env, 2, specs) - .is_error) { - // Return an error type on failure + InterpretResult args_res = + interpret_arguments(node->function_call.arguments, env, 2, specs); + if (args_res.is_error) { LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; return make_result(lv, false, false); } From 061052b3ffdb2509a7bf3edbaae564fa1a70dc07 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:36:41 +0000 Subject: [PATCH 33/62] Using switch-case in `copy_function_parameters` #179 --- src/interpreter/utils.c | 314 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 294 insertions(+), 20 deletions(-) diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 41b1c1d..08118c3 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -115,25 +115,26 @@ ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params) { ASTFunctionParameter *tail = NULL; while (params) { - ASTFunctionParameter *new_param = malloc(sizeof(ASTFunctionParameter)); - if (!new_param) { - fatal_error( - "Memory allocation failed for function parameter copy.\n"); + ASTFunctionParameter *copied_param = + malloc(sizeof(ASTFunctionParameter)); + if (!copied_param) { + fatal_error("Memory allocation failed for ASTFunctionParameter.\n"); } - new_param->parameter_name = strdup(params->parameter_name); - if (!new_param->parameter_name) { - free(new_param); - fatal_error("Memory allocation failed for parameter name copy.\n"); + // Duplicate the parameter name + copied_param->parameter_name = strdup(params->parameter_name); + if (!copied_param->parameter_name) { + free(copied_param); + fatal_error("Memory allocation failed for parameter name.\n"); } - new_param->next = NULL; + copied_param->next = NULL; if (!new_params) { - new_params = tail = new_param; + new_params = tail = copied_param; } else { - tail->next = new_param; - tail = new_param; + tail->next = copied_param; + tail = copied_param; } params = params->next; @@ -143,26 +144,299 @@ ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params) { } ASTNode *copy_ast_node(ASTNode *node) { - if (!node) + if (!node) { return NULL; + } + debug_print_int("Copying ASTNode of type %d at %p\n", node->type, + (void *)node); ASTNode *new_node = malloc(sizeof(ASTNode)); if (!new_node) { fatal_error("Memory allocation failed in `copy_ast_node`\n"); } - memcpy(new_node, node, sizeof(ASTNode)); + // Initialize new node to zero (prevents dangling pointers) + memset(new_node, 0, sizeof(ASTNode)); + + // Copy the node type and line number + new_node->type = node->type; + + // Deep copy based on node type + switch (node->type) { + case AST_ASSIGNMENT: + // Duplicate the variable name + new_node->assignment.variable_name = + strdup(node->assignment.variable_name); + if (!new_node->assignment.variable_name) { + fatal_error( + "Memory allocation failed for variable name in assignment.\n"); + } + // Recursively copy the value expression + new_node->assignment.value = copy_ast_node(node->assignment.value); + break; + + case AST_FUNCTION_DECLARATION: + // Duplicate the function name + new_node->function_call.name = strdup(node->function_call.name); + if (!new_node->function_call.name) { + fatal_error( + "Memory allocation failed for function name in declaration.\n"); + } + // Copy parameters + new_node->function_call.parameters = + copy_function_parameters(node->function_call.parameters); + // Recursively copy the function body + new_node->function_call.body = copy_ast_node(node->function_call.body); + // If there's return data, copy it + if (node->function_call.return_data) { + new_node->function_call.return_data = + copy_ast_node(node->function_call.return_data); + } + break; + + case AST_FUNCTION_CALL: + // Duplicate the function name + new_node->function_call.name = strdup(node->function_call.name); + if (!new_node->function_call.name) { + fatal_error( + "Memory allocation failed for function name in call.\n"); + } - // Deep copy for fields like `function_call`, `body`, or `arguments` - if (node->function_call.arguments) { + // Recursively copy arguments new_node->function_call.arguments = copy_ast_node(node->function_call.arguments); + if (node->function_call.body) { + new_node->function_call.body = + copy_ast_node(node->function_call.body); + } + + // If there's return data, copy it + if (node->function_call.return_data) { + new_node->function_call.return_data = + copy_ast_node(node->function_call.return_data); + } + break; + + case AST_FUNCTION_RETURN: + new_node->function_call.return_data = + copy_ast_node(node->function_call.return_data); + break; + + case AST_LITERAL: + // Deep copy the literal based on its type + new_node->literal.type = node->literal.type; + switch (node->literal.type) { + case LITERAL_STRING: + new_node->literal.value.string = strdup(node->literal.value.string); + if (!new_node->literal.value.string) { + fatal_error("Memory allocation failed for string literal.\n"); + } + break; + case LITERAL_FLOAT: + new_node->literal.value.floating_point = + node->literal.value.floating_point; + break; + case LITERAL_INTEGER: + new_node->literal.value.integer = node->literal.value.integer; + break; + case LITERAL_BOOLEAN: + new_node->literal.value.boolean = node->literal.value.boolean; + break; + default: + fatal_error("Unknown literal type during copy.\n"); + } + break; + + case AST_CONDITIONAL: + // Recursively copy the condition and body + new_node->conditional.condition = + copy_ast_node(node->conditional.condition); + new_node->conditional.body = copy_ast_node(node->conditional.body); + new_node->conditional.else_branch = + copy_ast_node(node->conditional.else_branch); + break; + + case AST_UNARY_OP: + // Duplicate the operator string + new_node->unary_op.operator= strdup(node->unary_op.operator); + if (!new_node->unary_op.operator) { + fatal_error("Memory allocation failed for unary operator.\n"); + } + + // Recursively copy the operand + new_node->unary_op.operand = copy_ast_node(node->unary_op.operand); + break; + + case AST_BINARY_OP: + // Duplicate the operator string + new_node->binary_op.operator= strdup(node->binary_op.operator); + if (!new_node->binary_op.operator) { + fatal_error("Memory allocation failed for binary operator.\n"); + } + + // Recursively copy left and right operands + new_node->binary_op.left = copy_ast_node(node->binary_op.left); + new_node->binary_op.right = copy_ast_node(node->binary_op.right); + break; + + case AST_WHILE_LOOP: + // Recursively copy the condition and body + new_node->while_loop.condition = + copy_ast_node(node->while_loop.condition); + new_node->while_loop.body = copy_ast_node(node->while_loop.body); + new_node->while_loop.re_evaluate_condition = + node->while_loop.re_evaluate_condition; + break; + + case AST_FOR_LOOP: + // Duplicate the loop variable name + new_node->for_loop.loop_variable = strdup(node->for_loop.loop_variable); + if (!new_node->for_loop.loop_variable) { + fatal_error( + "Memory allocation failed for loop variable in for-loop.\n"); + } + + // Recursively copy start and end expressions + new_node->for_loop.start_expr = + copy_ast_node(node->for_loop.start_expr); + new_node->for_loop.end_expr = copy_ast_node(node->for_loop.end_expr); + new_node->for_loop.inclusive = node->for_loop.inclusive; + + // Recursively copy step expression if it exists + if (node->for_loop.step_expr) { + new_node->for_loop.step_expr = + copy_ast_node(node->for_loop.step_expr); + } + // Recursively copy the loop body + new_node->for_loop.body = copy_ast_node(node->for_loop.body); + break; + + case AST_SWITCH: + // Recursively copy the switch expression + new_node->switch_case.expression = + copy_ast_node(node->switch_case.expression); + + // Recursively copy each case + new_node->switch_case.cases = NULL; + ASTCaseNode *current_case = node->switch_case.cases; + ASTCaseNode *new_case_head = NULL; + ASTCaseNode *new_case_tail = NULL; + + while (current_case) { + ASTCaseNode *copied_case = malloc(sizeof(ASTCaseNode)); + if (!copied_case) { + fatal_error("Memory allocation failed for ASTCaseNode.\n"); + } + + // Recursively copy the condition and body + copied_case->condition = copy_ast_node(current_case->condition); + copied_case->body = copy_ast_node(current_case->body); + copied_case->next = NULL; + + if (!new_case_head) { + new_case_head = new_case_tail = copied_case; + } else { + new_case_tail->next = copied_case; + new_case_tail = copied_case; + } + + current_case = current_case->next; + } + + new_node->switch_case.cases = new_case_head; + break; + + case AST_BREAK: + break; + + case AST_TERNARY: + // Recursively copy condition, true_expr, and false_expr + new_node->ternary.condition = copy_ast_node(node->ternary.condition); + new_node->ternary.true_expr = copy_ast_node(node->ternary.true_expr); + new_node->ternary.false_expr = copy_ast_node(node->ternary.false_expr); + break; + + case AST_VARIABLE: + // Duplicate the variable name + new_node->variable_name = strdup(node->variable_name); + if (!new_node->variable_name) { + fatal_error("Memory allocation failed for variable name.\n"); + } + break; + + case AST_CONSTANT: + // Handle constants similarly to assignments + new_node->assignment.variable_name = + strdup(node->assignment.variable_name); + if (!new_node->assignment.variable_name) { + fatal_error("Memory allocation failed for constant name.\n"); + } + new_node->assignment.value = copy_ast_node(node->assignment.value); + break; + + case AST_TRY: + // Recursively copy try block + new_node->try_block.try_block = + copy_ast_node(node->try_block.try_block); + + // Recursively copy catch blocks + new_node->try_block.catch_blocks = NULL; + ASTCatchNode *current_catch = node->try_block.catch_blocks; + ASTCatchNode *new_catch_head = NULL; + ASTCatchNode *new_catch_tail = NULL; + + while (current_catch) { + ASTCatchNode *copied_catch = malloc(sizeof(ASTCatchNode)); + if (!copied_catch) { + fatal_error("Memory allocation failed for ASTCatchNode.\n"); + } + + // Duplicate error variable name if it exists + if (current_catch->error_variable) { + copied_catch->error_variable = + strdup(current_catch->error_variable); + if (!copied_catch->error_variable) { + fatal_error( + "Memory allocation failed for catch error variable.\n"); + } + } else { + copied_catch->error_variable = NULL; + } + + // Recursively copy the catch body + copied_catch->body = copy_ast_node(current_catch->body); + copied_catch->next = NULL; + + if (!new_catch_head) { + new_catch_head = new_catch_tail = copied_catch; + } else { + new_catch_tail->next = copied_catch; + new_catch_tail = copied_catch; + } + + current_catch = current_catch->next; + } + + new_node->try_block.catch_blocks = new_catch_head; + + // Recursively copy finally block if it exists + if (node->try_block.finally_block) { + new_node->try_block.finally_block = + copy_ast_node(node->try_block.finally_block); + } + break; + + case AST_FINALLY: + // For now, managed within AST_TRY + break; + + default: + fatal_error("Unknown ASTNodeType encountered in `copy_ast_node`.\n"); } - if (node->function_call.body) { - new_node->function_call.body = copy_ast_node(node->function_call.body); - } - // Handle other types like linked lists or child nodes as needed + // Recursively copy the next node in the linked list + new_node->next = copy_ast_node(node->next); + return new_node; } From 36c56e9183f0384f9a248fade50e8fb1bffc5b6f Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:56:20 +0000 Subject: [PATCH 34/62] ASTNode now has separate structs for function declaration/call/return #179 --- src/interpreter/interpreter.c | 8 +- src/interpreter/utils.c | 230 ++++++++++++++++++---------------- src/interpreter/utils.h | 1 + src/parser/parser.c | 15 ++- src/parser/utils.c | 16 +-- src/shared/ast_types.h | 40 +++--- 6 files changed, 168 insertions(+), 142 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 4eddb76..5398365 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -65,7 +65,7 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) { case AST_FUNCTION_RETURN: { InterpretResult return_res = - interpret_node(node->function_call.return_data, env); + interpret_node(node->function_return.return_data, env); if (return_res.is_error) { return return_res; } @@ -1166,7 +1166,7 @@ InterpretResult interpret_function_declaration(ASTNode *node, ASTFunctionParameter *param_tail = NULL; // keep track of the last parameter // Copy parameters - ASTFunctionParameter *param = node->function_call.parameters; + ASTFunctionParameter *param = node->function_declaration.parameters; while (param) { ASTFunctionParameter *new_param = malloc(sizeof(ASTFunctionParameter)); if (!new_param) { @@ -1194,9 +1194,9 @@ InterpretResult interpret_function_declaration(ASTNode *node, param = param->next; } - Function func = {.name = strdup(node->function_call.name), + Function func = {.name = strdup(node->function_declaration.name), .parameters = param_list, - .body = node->function_call.body, + .body = node->function_declaration.body, .is_builtin = false}; add_function(env, func); diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 08118c3..55770c6 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -122,10 +122,14 @@ ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params) { } // Duplicate the parameter name - copied_param->parameter_name = strdup(params->parameter_name); - if (!copied_param->parameter_name) { - free(copied_param); - fatal_error("Memory allocation failed for parameter name.\n"); + if (params->parameter_name) { + copied_param->parameter_name = strdup(params->parameter_name); + if (!copied_param->parameter_name) { + free(copied_param); + fatal_error("Memory allocation failed for parameter name.\n"); + } + } else { + copied_param->parameter_name = NULL; } copied_param->next = NULL; @@ -147,6 +151,7 @@ ASTNode *copy_ast_node(ASTNode *node) { if (!node) { return NULL; } + debug_print_int("Copying ASTNode of type %d at %p\n", node->type, (void *)node); @@ -155,71 +160,74 @@ ASTNode *copy_ast_node(ASTNode *node) { fatal_error("Memory allocation failed in `copy_ast_node`\n"); } - // Initialize new node to zero (prevents dangling pointers) + // Initialize the new node to zero to prevent dangling pointers memset(new_node, 0, sizeof(ASTNode)); - // Copy the node type and line number + // Copy the node type new_node->type = node->type; // Deep copy based on node type switch (node->type) { case AST_ASSIGNMENT: // Duplicate the variable name - new_node->assignment.variable_name = - strdup(node->assignment.variable_name); - if (!new_node->assignment.variable_name) { - fatal_error( - "Memory allocation failed for variable name in assignment.\n"); + if (node->assignment.variable_name) { + new_node->assignment.variable_name = + strdup(node->assignment.variable_name); + if (!new_node->assignment.variable_name) { + fatal_error("Memory allocation failed for variable name in " + "assignment.\n"); + } + } else { + new_node->assignment.variable_name = NULL; } + // Recursively copy the value expression new_node->assignment.value = copy_ast_node(node->assignment.value); break; case AST_FUNCTION_DECLARATION: // Duplicate the function name - new_node->function_call.name = strdup(node->function_call.name); - if (!new_node->function_call.name) { - fatal_error( - "Memory allocation failed for function name in declaration.\n"); + if (node->function_declaration.name) { + new_node->function_declaration.name = + strdup(node->function_declaration.name); + if (!new_node->function_declaration.name) { + fatal_error("Memory allocation failed for function name in " + "declaration.\n"); + } + } else { + new_node->function_declaration.name = NULL; } + // Copy parameters - new_node->function_call.parameters = - copy_function_parameters(node->function_call.parameters); + new_node->function_declaration.parameters = + copy_function_parameters(node->function_declaration.parameters); + // Recursively copy the function body - new_node->function_call.body = copy_ast_node(node->function_call.body); - // If there's return data, copy it - if (node->function_call.return_data) { - new_node->function_call.return_data = - copy_ast_node(node->function_call.return_data); - } + new_node->function_declaration.body = + copy_ast_node(node->function_declaration.body); break; case AST_FUNCTION_CALL: // Duplicate the function name - new_node->function_call.name = strdup(node->function_call.name); - if (!new_node->function_call.name) { - fatal_error( - "Memory allocation failed for function name in call.\n"); + if (node->function_call.name) { + new_node->function_call.name = strdup(node->function_call.name); + if (!new_node->function_call.name) { + fatal_error( + "Memory allocation failed for function name in call.\n"); + } + } else { + new_node->function_call.name = NULL; } // Recursively copy arguments new_node->function_call.arguments = copy_ast_node(node->function_call.arguments); - if (node->function_call.body) { - new_node->function_call.body = - copy_ast_node(node->function_call.body); - } - - // If there's return data, copy it - if (node->function_call.return_data) { - new_node->function_call.return_data = - copy_ast_node(node->function_call.return_data); - } break; case AST_FUNCTION_RETURN: - new_node->function_call.return_data = - copy_ast_node(node->function_call.return_data); + // Recursively copy the return data expression + new_node->function_return.return_data = + copy_ast_node(node->function_return.return_data); break; case AST_LITERAL: @@ -227,9 +235,15 @@ ASTNode *copy_ast_node(ASTNode *node) { new_node->literal.type = node->literal.type; switch (node->literal.type) { case LITERAL_STRING: - new_node->literal.value.string = strdup(node->literal.value.string); - if (!new_node->literal.value.string) { - fatal_error("Memory allocation failed for string literal.\n"); + if (node->literal.value.string) { + new_node->literal.value.string = + strdup(node->literal.value.string); + if (!new_node->literal.value.string) { + fatal_error( + "Memory allocation failed for string literal.\n"); + } + } else { + new_node->literal.value.string = NULL; } break; case LITERAL_FLOAT: @@ -258,9 +272,13 @@ ASTNode *copy_ast_node(ASTNode *node) { case AST_UNARY_OP: // Duplicate the operator string - new_node->unary_op.operator= strdup(node->unary_op.operator); - if (!new_node->unary_op.operator) { - fatal_error("Memory allocation failed for unary operator.\n"); + if (node->unary_op.operator) { + new_node->unary_op.operator= strdup(node->unary_op.operator); + if (!new_node->unary_op.operator) { + fatal_error("Memory allocation failed for unary operator.\n"); + } + } else { + new_node->unary_op.operator= NULL; } // Recursively copy the operand @@ -269,9 +287,13 @@ ASTNode *copy_ast_node(ASTNode *node) { case AST_BINARY_OP: // Duplicate the operator string - new_node->binary_op.operator= strdup(node->binary_op.operator); - if (!new_node->binary_op.operator) { - fatal_error("Memory allocation failed for binary operator.\n"); + if (node->binary_op.operator) { + new_node->binary_op.operator= strdup(node->binary_op.operator); + if (!new_node->binary_op.operator) { + fatal_error("Memory allocation failed for binary operator.\n"); + } + } else { + new_node->binary_op.operator= NULL; } // Recursively copy left and right operands @@ -290,23 +312,33 @@ ASTNode *copy_ast_node(ASTNode *node) { case AST_FOR_LOOP: // Duplicate the loop variable name - new_node->for_loop.loop_variable = strdup(node->for_loop.loop_variable); - if (!new_node->for_loop.loop_variable) { - fatal_error( - "Memory allocation failed for loop variable in for-loop.\n"); + if (node->for_loop.loop_variable) { + new_node->for_loop.loop_variable = + strdup(node->for_loop.loop_variable); + if (!new_node->for_loop.loop_variable) { + fatal_error("Memory allocation failed for loop variable in " + "for-loop.\n"); + } + } else { + new_node->for_loop.loop_variable = NULL; } // Recursively copy start and end expressions new_node->for_loop.start_expr = copy_ast_node(node->for_loop.start_expr); new_node->for_loop.end_expr = copy_ast_node(node->for_loop.end_expr); + + // Copy inclusive flag new_node->for_loop.inclusive = node->for_loop.inclusive; // Recursively copy step expression if it exists if (node->for_loop.step_expr) { new_node->for_loop.step_expr = copy_ast_node(node->for_loop.step_expr); + } else { + new_node->for_loop.step_expr = NULL; } + // Recursively copy the loop body new_node->for_loop.body = copy_ast_node(node->for_loop.body); break; @@ -347,6 +379,7 @@ ASTNode *copy_ast_node(ASTNode *node) { break; case AST_BREAK: + // No additional fields to copy for break break; case AST_TERNARY: @@ -356,23 +389,8 @@ ASTNode *copy_ast_node(ASTNode *node) { new_node->ternary.false_expr = copy_ast_node(node->ternary.false_expr); break; - case AST_VARIABLE: - // Duplicate the variable name - new_node->variable_name = strdup(node->variable_name); - if (!new_node->variable_name) { - fatal_error("Memory allocation failed for variable name.\n"); - } - break; - - case AST_CONSTANT: - // Handle constants similarly to assignments - new_node->assignment.variable_name = - strdup(node->assignment.variable_name); - if (!new_node->assignment.variable_name) { - fatal_error("Memory allocation failed for constant name.\n"); - } - new_node->assignment.value = copy_ast_node(node->assignment.value); - break; + // Exclude AST_VARIABLE and AST_CONSTANT as they're not used yet + // If needed in the future, similar handling can be added here case AST_TRY: // Recursively copy try block @@ -380,54 +398,21 @@ ASTNode *copy_ast_node(ASTNode *node) { copy_ast_node(node->try_block.try_block); // Recursively copy catch blocks - new_node->try_block.catch_blocks = NULL; - ASTCatchNode *current_catch = node->try_block.catch_blocks; - ASTCatchNode *new_catch_head = NULL; - ASTCatchNode *new_catch_tail = NULL; - - while (current_catch) { - ASTCatchNode *copied_catch = malloc(sizeof(ASTCatchNode)); - if (!copied_catch) { - fatal_error("Memory allocation failed for ASTCatchNode.\n"); - } - - // Duplicate error variable name if it exists - if (current_catch->error_variable) { - copied_catch->error_variable = - strdup(current_catch->error_variable); - if (!copied_catch->error_variable) { - fatal_error( - "Memory allocation failed for catch error variable.\n"); - } - } else { - copied_catch->error_variable = NULL; - } - - // Recursively copy the catch body - copied_catch->body = copy_ast_node(current_catch->body); - copied_catch->next = NULL; - - if (!new_catch_head) { - new_catch_head = new_catch_tail = copied_catch; - } else { - new_catch_tail->next = copied_catch; - new_catch_tail = copied_catch; - } - - current_catch = current_catch->next; - } - - new_node->try_block.catch_blocks = new_catch_head; + new_node->try_block.catch_blocks = + copy_catch_node(node->try_block.catch_blocks); // Recursively copy finally block if it exists if (node->try_block.finally_block) { new_node->try_block.finally_block = copy_ast_node(node->try_block.finally_block); + } else { + new_node->try_block.finally_block = NULL; } break; + case AST_CATCH: case AST_FINALLY: - // For now, managed within AST_TRY + // These are handled within AST_TRY, so no action needed here break; default: @@ -440,6 +425,35 @@ ASTNode *copy_ast_node(ASTNode *node) { return new_node; } +ASTCatchNode *copy_catch_node(ASTCatchNode *catch_node) { + if (!catch_node) + return NULL; + + ASTCatchNode *new_catch = malloc(sizeof(ASTCatchNode)); + if (!new_catch) { + fatal_error("Memory allocation failed for ASTCatchNode.\n"); + } + + // Duplicate error variable name if it exists + if (catch_node->error_variable) { + new_catch->error_variable = strdup(catch_node->error_variable); + if (!new_catch->error_variable) { + free(new_catch); + fatal_error("Memory allocation failed for catch error variable.\n"); + } + } else { + new_catch->error_variable = NULL; + } + + // Recursively copy the catch body + new_catch->body = copy_ast_node(catch_node->body); + + // Recursively copy the next catch node + new_catch->next = copy_catch_node(catch_node->next); + + return new_catch; +} + void add_function(Environment *env, Function func) { // Check if this function name already exists in env for (size_t i = 0; i < env->function_count; i++) { diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h index 6afd14d..9c50cde 100644 --- a/src/interpreter/utils.h +++ b/src/interpreter/utils.h @@ -28,5 +28,6 @@ Function *get_function(Environment *env, const char *name); // Helpers InterpretResult make_result(LiteralValue val, bool did_return, bool did_break); +ASTCatchNode *copy_catch_node(ASTCatchNode *catch_node); #endif diff --git a/src/parser/parser.c b/src/parser/parser.c index d99d71d..9e94950 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -294,7 +294,7 @@ ASTNode *parse_function_return(ParserState *state) { } node->type = AST_FUNCTION_RETURN; - node->function_call.return_data = parse_expression(state); + node->function_return.return_data = parse_expression(state); node->next = NULL; expect_token(state, TOKEN_DELIMITER, @@ -750,10 +750,9 @@ ASTNode *parse_function_declaration(ParserState *state) { } node->type = AST_FUNCTION_DECLARATION; - node->function_call.name = strdup(name->lexeme); - node->function_call.parameters = NULL; - node->function_call.body = NULL; - node->function_call.return_data = NULL; + node->function_declaration.name = strdup(name->lexeme); + node->function_declaration.parameters = NULL; + node->function_declaration.body = NULL; node->next = NULL; advance_token(state); // Move past the function name @@ -764,9 +763,9 @@ ASTNode *parse_function_declaration(ParserState *state) { // Check if the parameter list is empty if (get_current_token(state)->type == TOKEN_PAREN_CLOSE) { - node->function_call.parameters = NULL; // No parameters + node->function_declaration.parameters = NULL; // No parameters } else { - node->function_call.parameters = parse_parameter_list(state); + node->function_declaration.parameters = parse_parameter_list(state); } expect_token(state, TOKEN_PAREN_CLOSE, @@ -780,7 +779,7 @@ ASTNode *parse_function_declaration(ParserState *state) { if (get_current_token(state)->type == TOKEN_BRACE_OPEN) { advance_token(state); // Consume `(` state->in_function_body = true; - node->function_call.body = + node->function_declaration.body = parse_function_body(state); // Parse function body state->in_function_body = false; diff --git a/src/parser/utils.c b/src/parser/utils.c index 8846030..3a10f30 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -175,12 +175,14 @@ void print_ast(ASTNode *node, int depth) { break; case AST_FUNCTION_DECLARATION: - printf("Function Declaration: `%s`\n", node->function_call.name); + printf("Function Declaration: `%s`\n", + node->function_declaration.name); // Print Parameters - if (node->function_call.parameters != NULL) { + if (node->function_declaration.parameters != NULL) { print_indentation(depth + 1); printf("Parameters:\n"); - ASTFunctionParameter *param = node->function_call.parameters; + ASTFunctionParameter *param = + node->function_declaration.parameters; while (param != NULL) { print_indentation(depth + 2); printf("- `%s`\n", param->parameter_name); @@ -191,10 +193,10 @@ void print_ast(ASTNode *node, int depth) { printf("Parameters: None\n"); } // Print Body - if (node->function_call.body != NULL) { + if (node->function_declaration.body != NULL) { print_indentation(depth + 1); printf("Body:\n"); - print_ast(node->function_call.body, depth + 2); + print_ast(node->function_declaration.body, depth + 2); } else { print_indentation(depth + 1); printf("Body: None\n"); @@ -216,8 +218,8 @@ void print_ast(ASTNode *node, int depth) { case AST_FUNCTION_RETURN: printf("Function Return:\n"); - if (node->function_call.return_data != NULL) { - print_ast(node->function_call.return_data, depth + 1); + if (node->function_return.return_data != NULL) { + print_ast(node->function_return.return_data, depth + 1); } else { print_indentation(depth + 1); printf("Return Data: None\n"); diff --git a/src/shared/ast_types.h b/src/shared/ast_types.h index 2f9080d..10f616d 100644 --- a/src/shared/ast_types.h +++ b/src/shared/ast_types.h @@ -103,16 +103,24 @@ typedef struct ASTFunctionParameter { struct ASTFunctionParameter *next; // Linked list for multiple parameters } ASTFunctionParameter; -// AST Function Call +// AST Function Declaration Node typedef struct { char *name; - ASTFunctionParameter - *parameters; // For function declarations, parameter names - struct ASTNode *arguments; // For function calls, argument values - struct ASTNode *body; - struct ASTNode *return_data; + ASTFunctionParameter *parameters; // Function parameters + struct ASTNode *body; // Function body +} ASTFunctionDeclaration; + +// AST Function Call Node +typedef struct { + char *name; + struct ASTNode *arguments; // Function call arguments } ASTFunctionCall; +// AST Function Return Node +typedef struct { + struct ASTNode *return_data; // Expression to return +} ASTFunctionReturn; + // AST Ternary typedef struct { struct ASTNode *condition; @@ -144,15 +152,17 @@ typedef struct ASTNode { struct ASTNode *value; } assignment; - LiteralNode literal; // Literal - ASTUnaryOp unary_op; // Unary operation - ASTBinaryOp binary_op; // Binary operation - ASTConditional conditional; // Conditional - ASTSwitch switch_case; // Switch - ASTWhileLoop while_loop; // While loop - ASTForLoop for_loop; // For loop - ASTFunctionCall function_call; // Function - ASTTernary ternary; // Ternary + LiteralNode literal; + ASTUnaryOp unary_op; + ASTBinaryOp binary_op; + ASTConditional conditional; + ASTSwitch switch_case; + ASTWhileLoop while_loop; + ASTForLoop for_loop; + ASTTernary ternary; + ASTFunctionDeclaration function_declaration; + ASTFunctionCall function_call; + ASTFunctionReturn function_return; ASTTry try_block; // Try Block // ASTCatchNode *catch_block; // Rescue Block From 2512c0ba322a076871d6f94c9b5af4b75ba9a63d Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:44:19 +0000 Subject: [PATCH 35/62] Closer to fixing memory corruption issue #179 Debugging output (example): [DEBUG] Execution complete! ================================================================= ==57484==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000d08 at pc 0x0001046edb80 bp 0x00016b735710 sp 0x00016b735708 READ of size 8 at 0x602000000d08 thread T0 #0 0x1046edb7c in free_ast utils.c:5 #1 0x1046efafc in free_ast utils.c:58 #2 0x1046cb7cc in main main.c:101 #3 0x18a0fc270 () 0x602000000d08 is located 8 bytes before 4-byte region [0x602000000d10,0x602000000d14) allocated by thread T0 here: #0 0x104c65cb8 in strdup+0x11c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x4dcb8) #1 0x1046dd9a4 in create_variable_node operator_parser.c:352 #2 0x1046db998 in parse_primary operator_parser.c:224 #3 0x1046da9a8 in parse_unary operator_parser.c:166 #4 0x1046da3b0 in parse_power operator_parser.c:138 #5 0x1046da03c in parse_factor operator_parser.c:120 #6 0x1046d9d08 in parse_term operator_parser.c:103 #7 0x1046d9994 in parse_comparison operator_parser.c:85 #8 0x1046d8da0 in parse_equality operator_parser.c:68 #9 0x1046d8a6c in parse_logical operator_parser.c:51 #10 0x1046d81c0 in parse_ternary operator_parser.c:18 #11 0x1046d8188 in parse_operator_expression operator_parser.c:12 #12 0x1046e67f4 in parse_expression parser.c:285 #13 0x1046e4524 in parse_function_return parser.c:297 #14 0x1046ead04 in parse_block parser.c:341 #15 0x1046ebcb0 in parse_function_body parser.c:731 #16 0x1046e3ed0 in parse_function_declaration parser.c:783 #17 0x1046de208 in parse_statement parser.c:48 #18 0x1046ddd28 in parse_program parser.c:11 #19 0x1046cb750 in main main.c:84 #20 0x18a0fc270 () SUMMARY: AddressSanitizer: heap-buffer-overflow utils.c:5 in free_ast Shadow bytes around the buggy address: 0x602000000a80: fa fa 06 fa fa fa 02 fa fa fa 02 fa fa fa fd fd 0x602000000b00: fa fa 00 07 fa fa 02 fa fa fa 02 fa fa fa fd fa 0x602000000b80: fa fa 06 fa fa fa 02 fa fa fa 02 fa fa fa 02 fa 0x602000000c00: fa fa 02 fa fa fa 02 fa fa fa 02 fa fa fa 02 fa 0x602000000c80: fa fa 02 fa fa fa fd fd fa fa 00 00 fa fa 04 fa =>0x602000000d00: fa[fa]04 fa fa fa 02 fa fa fa 03 fa fa fa 05 fa 0x602000000d80: fa fa 04 fa fa fa 02 fa fa fa 06 fa fa fa 00 00 0x602000000e00: fa fa 06 fa fa fa 04 fa fa fa 07 fa fa fa 06 fa 0x602000000e80: fa fa 00 07 fa fa 06 fa fa fa 07 fa fa fa fd fa 0x602000000f00: fa fa 06 fa fa fa fd fa fa fa 04 fa fa fa fd fa 0x602000000f80: fa fa 07 fa fa fa fd fa fa fa 06 fa fa fa fd fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==57484==ABORTING [1] 57484 abort ./flavor tests/16_ternary.flv --debug --- src/interpreter/builtins.c | 2 +- src/interpreter/interpreter.c | 23 +++++----- src/interpreter/utils.c | 85 +++++++---------------------------- 3 files changed, 28 insertions(+), 82 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 2dd4af7..5b00dd1 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -362,7 +362,7 @@ InterpretResult builtin_cast(ASTNode *node, Environment *env) { "`builtin_cast()` expects an `AST_FUNCTION_CALL` node.\n"); } - char *cast_type = node->function_call.name; + char *cast_type = strdup(node->function_call.name); if (!cast_type) { return raise_error("No cast type provided to `builtin_cast()`.\n"); } diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 5398365..a3e5206 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -2,13 +2,10 @@ // Helper function to create a default LiteralValue (zero number) LiteralValue create_default_value(void) { - LiteralValue value = { - .type = TYPE_INTEGER, - .data = { - // .floating_point = 0.0, - .integer = 0, // main value used for comparisons, etc - // .string = "", - }}; + LiteralValue value; + memset(&value, 0, sizeof(value)); + value.type = TYPE_INTEGER; + value.data.integer = 0; return value; } @@ -132,11 +129,11 @@ void interpret_program(ASTNode *program, Environment *env) { ASTNode *current = program; while (current) { debug_print_int("Executing top-level statement\n"); - InterpretResult res = interpret_node(current, env); - if (res.is_error) { - fprintf(stderr, "Unhandled exception: %s\n", res.value.data.string); - exit(1); - } + interpret_node(current, env); + // if (res.is_error) { + // fprintf(stderr, "Unhandled exception: %s\n", + // res.value.data.string); exit(1); + // } current = current->next; } } @@ -1223,7 +1220,7 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { return raise_error("Invalid function call"); } - const char *func_name = node->function_call.name; + const char *func_name = strdup(node->function_call.name); // 1. Check if the function name is a variable holding a function reference Variable *func_var = get_variable(env, func_name); diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 55770c6..a5d06be 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -169,7 +169,6 @@ ASTNode *copy_ast_node(ASTNode *node) { // Deep copy based on node type switch (node->type) { case AST_ASSIGNMENT: - // Duplicate the variable name if (node->assignment.variable_name) { new_node->assignment.variable_name = strdup(node->assignment.variable_name); @@ -180,13 +179,10 @@ ASTNode *copy_ast_node(ASTNode *node) { } else { new_node->assignment.variable_name = NULL; } - - // Recursively copy the value expression new_node->assignment.value = copy_ast_node(node->assignment.value); break; case AST_FUNCTION_DECLARATION: - // Duplicate the function name if (node->function_declaration.name) { new_node->function_declaration.name = strdup(node->function_declaration.name); @@ -197,18 +193,13 @@ ASTNode *copy_ast_node(ASTNode *node) { } else { new_node->function_declaration.name = NULL; } - - // Copy parameters new_node->function_declaration.parameters = copy_function_parameters(node->function_declaration.parameters); - - // Recursively copy the function body new_node->function_declaration.body = copy_ast_node(node->function_declaration.body); break; case AST_FUNCTION_CALL: - // Duplicate the function name if (node->function_call.name) { new_node->function_call.name = strdup(node->function_call.name); if (!new_node->function_call.name) { @@ -218,20 +209,16 @@ ASTNode *copy_ast_node(ASTNode *node) { } else { new_node->function_call.name = NULL; } - - // Recursively copy arguments new_node->function_call.arguments = copy_ast_node(node->function_call.arguments); break; case AST_FUNCTION_RETURN: - // Recursively copy the return data expression new_node->function_return.return_data = copy_ast_node(node->function_return.return_data); break; case AST_LITERAL: - // Deep copy the literal based on its type new_node->literal.type = node->literal.type; switch (node->literal.type) { case LITERAL_STRING: @@ -262,7 +249,6 @@ ASTNode *copy_ast_node(ASTNode *node) { break; case AST_CONDITIONAL: - // Recursively copy the condition and body new_node->conditional.condition = copy_ast_node(node->conditional.condition); new_node->conditional.body = copy_ast_node(node->conditional.body); @@ -271,7 +257,6 @@ ASTNode *copy_ast_node(ASTNode *node) { break; case AST_UNARY_OP: - // Duplicate the operator string if (node->unary_op.operator) { new_node->unary_op.operator= strdup(node->unary_op.operator); if (!new_node->unary_op.operator) { @@ -280,13 +265,10 @@ ASTNode *copy_ast_node(ASTNode *node) { } else { new_node->unary_op.operator= NULL; } - - // Recursively copy the operand new_node->unary_op.operand = copy_ast_node(node->unary_op.operand); break; case AST_BINARY_OP: - // Duplicate the operator string if (node->binary_op.operator) { new_node->binary_op.operator= strdup(node->binary_op.operator); if (!new_node->binary_op.operator) { @@ -295,14 +277,11 @@ ASTNode *copy_ast_node(ASTNode *node) { } else { new_node->binary_op.operator= NULL; } - - // Recursively copy left and right operands new_node->binary_op.left = copy_ast_node(node->binary_op.left); new_node->binary_op.right = copy_ast_node(node->binary_op.right); break; case AST_WHILE_LOOP: - // Recursively copy the condition and body new_node->while_loop.condition = copy_ast_node(node->while_loop.condition); new_node->while_loop.body = copy_ast_node(node->while_loop.body); @@ -311,7 +290,6 @@ ASTNode *copy_ast_node(ASTNode *node) { break; case AST_FOR_LOOP: - // Duplicate the loop variable name if (node->for_loop.loop_variable) { new_node->for_loop.loop_variable = strdup(node->for_loop.loop_variable); @@ -322,104 +300,75 @@ ASTNode *copy_ast_node(ASTNode *node) { } else { new_node->for_loop.loop_variable = NULL; } - - // Recursively copy start and end expressions new_node->for_loop.start_expr = copy_ast_node(node->for_loop.start_expr); new_node->for_loop.end_expr = copy_ast_node(node->for_loop.end_expr); - - // Copy inclusive flag new_node->for_loop.inclusive = node->for_loop.inclusive; - - // Recursively copy step expression if it exists - if (node->for_loop.step_expr) { - new_node->for_loop.step_expr = - copy_ast_node(node->for_loop.step_expr); - } else { - new_node->for_loop.step_expr = NULL; - } - - // Recursively copy the loop body + new_node->for_loop.step_expr = copy_ast_node(node->for_loop.step_expr); new_node->for_loop.body = copy_ast_node(node->for_loop.body); break; case AST_SWITCH: - // Recursively copy the switch expression new_node->switch_case.expression = copy_ast_node(node->switch_case.expression); - - // Recursively copy each case - new_node->switch_case.cases = NULL; ASTCaseNode *current_case = node->switch_case.cases; ASTCaseNode *new_case_head = NULL; ASTCaseNode *new_case_tail = NULL; - while (current_case) { ASTCaseNode *copied_case = malloc(sizeof(ASTCaseNode)); if (!copied_case) { fatal_error("Memory allocation failed for ASTCaseNode.\n"); } - - // Recursively copy the condition and body copied_case->condition = copy_ast_node(current_case->condition); copied_case->body = copy_ast_node(current_case->body); copied_case->next = NULL; - if (!new_case_head) { new_case_head = new_case_tail = copied_case; } else { new_case_tail->next = copied_case; new_case_tail = copied_case; } - current_case = current_case->next; } - new_node->switch_case.cases = new_case_head; break; + case AST_VARIABLE: + case AST_CONSTANT: + if (node->variable_name) { + new_node->variable_name = strdup(node->variable_name); + if (!new_node->variable_name) { + fatal_error( + "Memory allocation failed for variable/constant name.\n"); + } + } else { + new_node->variable_name = NULL; + } + break; + case AST_BREAK: - // No additional fields to copy for break + // No fields to copy break; case AST_TERNARY: - // Recursively copy condition, true_expr, and false_expr new_node->ternary.condition = copy_ast_node(node->ternary.condition); new_node->ternary.true_expr = copy_ast_node(node->ternary.true_expr); new_node->ternary.false_expr = copy_ast_node(node->ternary.false_expr); break; - // Exclude AST_VARIABLE and AST_CONSTANT as they're not used yet - // If needed in the future, similar handling can be added here - case AST_TRY: - // Recursively copy try block new_node->try_block.try_block = copy_ast_node(node->try_block.try_block); - - // Recursively copy catch blocks new_node->try_block.catch_blocks = copy_catch_node(node->try_block.catch_blocks); - - // Recursively copy finally block if it exists - if (node->try_block.finally_block) { - new_node->try_block.finally_block = - copy_ast_node(node->try_block.finally_block); - } else { - new_node->try_block.finally_block = NULL; - } - break; - - case AST_CATCH: - case AST_FINALLY: - // These are handled within AST_TRY, so no action needed here + new_node->try_block.finally_block = + copy_ast_node(node->try_block.finally_block); break; default: fatal_error("Unknown ASTNodeType encountered in `copy_ast_node`.\n"); } - // Recursively copy the next node in the linked list new_node->next = copy_ast_node(node->next); return new_node; From f05fa9c47e16230c2cd4d861cb6e57a0a4997e26 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:59:32 +0000 Subject: [PATCH 36/62] Ensure use of `strdup` everywhere strings are copied #179 --- src/interpreter/interpreter.c | 2 +- src/parser/operator_parser.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index a3e5206..9ed9dbc 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -192,7 +192,7 @@ InterpretResult interpret_constant(ASTNode *node, Environment *env) { } // Extract the constant name and value - char *const_name = node->assignment.variable_name; + char *const_name = strdup(node->assignment.variable_name); InterpretResult value_res = interpret_node(node->assignment.value, env); LiteralValue const_value = value_res.value; diff --git a/src/parser/operator_parser.c b/src/parser/operator_parser.c index 9442f32..bebc3d2 100644 --- a/src/parser/operator_parser.c +++ b/src/parser/operator_parser.c @@ -361,7 +361,7 @@ ASTNode *create_function_call_node(char *name, ASTNode *args) { parser_error("Memory allocation failed for function call node", NULL); } node->type = AST_FUNCTION_CALL; - node->function_call.name = name; + node->function_call.name = strdup(name); node->function_call.arguments = args; node->next = NULL; return node; From 7981a6462b56d6835351404b25dbbcaf53c50c6f Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:25:04 +0000 Subject: [PATCH 37/62] Update interpreter.c #179 --- src/interpreter/interpreter.c | 193 ++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 79 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 9ed9dbc..dfbc4ed 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -372,6 +372,14 @@ InterpretResult interpret_unary_op(ASTNode *node, Environment *env) { return make_result(result, false, false); } +// Helper function to check if a type is numeric +bool is_numeric_type(LiteralType type) { + return type == TYPE_INTEGER || type == TYPE_FLOAT; +} + +// Helper function to check if a type is boolean +bool is_boolean_type(LiteralType type) { return type == TYPE_BOOLEAN; } + InterpretResult evaluate_operator(const char *op, InterpretResult left_res, InterpretResult right_res) { debug_print_int("Operator: `%s`\n", op); @@ -399,7 +407,7 @@ InterpretResult evaluate_operator(const char *op, InterpretResult left_res, // Handle logical AND and OR if (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0) { // Ensure both operands are boolean - if (left.type != TYPE_BOOLEAN || right.type != TYPE_BOOLEAN) { + if (!is_boolean_type(left.type) || !is_boolean_type(right.type)) { return raise_error( "Logical operators `&&` and `||` require boolean operands.\n"); } @@ -415,93 +423,120 @@ InterpretResult evaluate_operator(const char *op, InterpretResult left_res, return make_result(result, false, false); } - // Get numeric values for arithmetic and comparison - double left_value = 0.0, right_value = 0.0; - if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { - left_value = (left.type == TYPE_FLOAT) ? left.data.floating_point - : (double)left.data.integer; - right_value = (right.type == TYPE_FLOAT) ? right.data.floating_point - : (double)right.data.integer; - } else { - left_value = (double)left.data.integer; - right_value = (double)right.data.integer; + // **New Type Validation for Numeric Operators** + // List of operators that require numeric operands + const char *numeric_operators[] = {"*", "+", "-", "/", "//", "%", "**", + "<", ">", "<=", ">=", "==", "!="}; + size_t num_numeric_ops = + sizeof(numeric_operators) / sizeof(numeric_operators[0]); + + bool is_numeric_op = false; + for (size_t i = 0; i < num_numeric_ops; ++i) { + if (strcmp(op, numeric_operators[i]) == 0) { + is_numeric_op = true; + break; + } } - // Determine result type based on operands - if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { - result.type = TYPE_FLOAT; - } else { - result.type = TYPE_INTEGER; - } - - // Handle operators - if (strcmp(op, "*") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = left_value * right_value; - else - result.data.integer = (INT_SIZE)(left_value * right_value); - } else if (strcmp(op, "+") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = left_value + right_value; - else - result.data.integer = (INT_SIZE)(left_value + right_value); - } else if (strcmp(op, "-") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = left_value - right_value; - else - result.data.integer = (INT_SIZE)(left_value - right_value); - } else if (strcmp(op, "/") == 0) { - if (right_value == 0) { - return raise_error("Division by zero\n"); + if (is_numeric_op) { + // Ensure both operands are numeric + if (!is_numeric_type(left.type) || !is_numeric_type(right.type)) { + return raise_error("Operator `%s` requires numeric operands.\n", + op); } - result.type = TYPE_FLOAT; - result.data.floating_point = left_value / right_value; - } else if (strcmp(op, "//") == 0) { - if (right_value == 0) { - return raise_error("Floor division by zero\n"); + + // Proceed to get numeric values + double left_value = 0.0, right_value = 0.0; + if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { + left_value = (left.type == TYPE_FLOAT) ? left.data.floating_point + : (double)left.data.integer; + right_value = (right.type == TYPE_FLOAT) + ? right.data.floating_point + : (double)right.data.integer; + } else { + left_value = (double)left.data.integer; + right_value = (double)right.data.integer; } - if (result.type == TYPE_FLOAT) - result.data.floating_point = floor(left_value / right_value); - else - result.data.integer = (INT_SIZE)(left_value / right_value); - } else if (strcmp(op, "%") == 0) { - if (right_value == 0) { - return raise_error("Modulo by zero\n"); + + // Determine result type based on operands + if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { + result.type = TYPE_FLOAT; + } else { + result.type = TYPE_INTEGER; } - if (result.type == TYPE_FLOAT) { - result.data.floating_point = fmod(left_value, right_value); + + // Handle operators + if (strcmp(op, "*") == 0) { + if (result.type == TYPE_FLOAT) + result.data.floating_point = left_value * right_value; + else + result.data.integer = (INT_SIZE)(left_value * right_value); + } else if (strcmp(op, "+") == 0) { + if (result.type == TYPE_FLOAT) + result.data.floating_point = left_value + right_value; + else + result.data.integer = (INT_SIZE)(left_value + right_value); + } else if (strcmp(op, "-") == 0) { + if (result.type == TYPE_FLOAT) + result.data.floating_point = left_value - right_value; + else + result.data.integer = (INT_SIZE)(left_value - right_value); + } else if (strcmp(op, "/") == 0) { + if (right_value == 0) { + return raise_error("Division by zero\n"); + } + result.type = TYPE_FLOAT; + result.data.floating_point = left_value / right_value; + } else if (strcmp(op, "//") == 0) { + if (right_value == 0) { + return raise_error("Floor division by zero\n"); + } + if (result.type == TYPE_FLOAT) + result.data.floating_point = floor(left_value / right_value); + else + result.data.integer = (INT_SIZE)(left_value / right_value); + } else if (strcmp(op, "%") == 0) { + if (right_value == 0) { + return raise_error("Modulo by zero\n"); + } + if (result.type == TYPE_FLOAT) { + result.data.floating_point = fmod(left_value, right_value); + } else { + result.data.integer = + (INT_SIZE)((INT_SIZE)left_value % (INT_SIZE)right_value); + } + } else if (strcmp(op, "**") == 0) { + if (result.type == TYPE_FLOAT) + result.data.floating_point = pow(left_value, right_value); + else + result.data.integer = (INT_SIZE)pow(left_value, right_value); + } else if (strcmp(op, "<") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_value < right_value) ? 1 : 0; + } else if (strcmp(op, ">") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_value > right_value) ? 1 : 0; + } else if (strcmp(op, "<=") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_value <= right_value) ? 1 : 0; + } else if (strcmp(op, ">=") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_value >= right_value) ? 1 : 0; + } else if (strcmp(op, "==") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_value == right_value) ? 1 : 0; + } else if (strcmp(op, "!=") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_value != right_value) ? 1 : 0; } else { - result.data.integer = - (INT_SIZE)((INT_SIZE)left_value % (INT_SIZE)right_value); + return raise_error("Unknown operator"); } - } else if (strcmp(op, "**") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = pow(left_value, right_value); - else - result.data.integer = (INT_SIZE)pow(left_value, right_value); - } else if (strcmp(op, "<") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value < right_value); - } else if (strcmp(op, ">") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value > right_value); - } else if (strcmp(op, "<=") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value <= right_value); - } else if (strcmp(op, ">=") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value >= right_value); - } else if (strcmp(op, "==") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value == right_value); - } else if (strcmp(op, "!=") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value != right_value); - } else { - return raise_error("Unknown operator"); + + return make_result(result, false, false); } - return make_result(result, false, false); + // If operator is not recognized + return raise_error("Unknown operator"); } int get_operator_precedence(const char *op) { From e32341b5a05628a12af439356de4d16a35994d88 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:25:20 +0000 Subject: [PATCH 38/62] Update utils.c #179 --- src/parser/utils.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parser/utils.c b/src/parser/utils.c index 3a10f30..6f0aa13 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -5,6 +5,9 @@ void free_ast(ASTNode *node) { ASTNode *next = node->next; switch (node->type) { + printf("free_ast: freeing AST var_name at %p -> '%s'\n", + (void *)node->assignment.variable_name, + node->assignment.variable_name); case AST_ASSIGNMENT: free(node->assignment.variable_name); From 1d466c8d20fd5521d0c22d4cc352491f26f20c4d Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:25:43 +0000 Subject: [PATCH 39/62] Partial fix: Memory issue fixed for functions #179 --- src/interpreter/utils.c | 13 ++++++++++++- src/tests/16_ternary.flv | 10 +--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index a5d06be..1907027 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -86,11 +86,22 @@ void free_environment(Environment *env) { // Free variables for (size_t i = 0; i < env->variable_count; i++) { free(env->variables[i].variable_name); + printf("free_env: freeing env var_name at %p -> '%s'\n", + (void *)env->variables[i].variable_name, + env->variables[i].variable_name); } free(env->variables); - // Free functions + // Free functions with complete cleanup for (size_t i = 0; i < env->function_count; i++) { + // Free the parameters linked list + free_parameter_list(env->functions[i].parameters); + + // Free the function body AST (only if they're NOT builtin) + if (!env->functions[i].is_builtin && env->functions[i].body) { + free(env->functions[i].body); + } + free(env->functions[i].name); } free(env->functions); diff --git a/src/tests/16_ternary.flv b/src/tests/16_ternary.flv index b9ace4a..31eb6d2 100644 --- a/src/tests/16_ternary.flv +++ b/src/tests/16_ternary.flv @@ -1,9 +1 @@ -create check_even_odd(num) { - deliver num % 2 == 0 ? "Even" : "Odd"; -} - -for _ in 1..=5 { - serve("Enter a number:"); - let input = int(sample()); - serve(check_even_odd(input)); -} +serve(5); \ No newline at end of file From a6686f7bcdc974c1a29040963d56a051babdb931 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:28:13 +0000 Subject: [PATCH 40/62] Fix: Trying to access variable after free #179 --- src/interpreter/utils.c | 3 --- src/tests/16_ternary.flv | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 1907027..7e2a595 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -86,9 +86,6 @@ void free_environment(Environment *env) { // Free variables for (size_t i = 0; i < env->variable_count; i++) { free(env->variables[i].variable_name); - printf("free_env: freeing env var_name at %p -> '%s'\n", - (void *)env->variables[i].variable_name, - env->variables[i].variable_name); } free(env->variables); diff --git a/src/tests/16_ternary.flv b/src/tests/16_ternary.flv index 31eb6d2..d9ef9e9 100644 --- a/src/tests/16_ternary.flv +++ b/src/tests/16_ternary.flv @@ -1 +1 @@ -serve(5); \ No newline at end of file +let a = 5; \ No newline at end of file From dfc616c599bb078b7763efb8fb57dba700b23665 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:41:01 +0000 Subject: [PATCH 41/62] Finally fixed `.is_error` issue by initializing in `make_result` as `false` to overwrite garbage memory #179 --- src/interpreter/utils.c | 1 + src/parser/utils.c | 9 ++++++--- src/tests/16_ternary.flv | 10 +++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 7e2a595..cf9c844 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -477,5 +477,6 @@ InterpretResult make_result(LiteralValue val, bool did_return, bool did_break) { r.value = val; r.did_return = did_return; r.did_break = did_break; + r.is_error = false; return r; } diff --git a/src/parser/utils.c b/src/parser/utils.c index 6f0aa13..8782902 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -5,11 +5,14 @@ void free_ast(ASTNode *node) { ASTNode *next = node->next; switch (node->type) { - printf("free_ast: freeing AST var_name at %p -> '%s'\n", - (void *)node->assignment.variable_name, - node->assignment.variable_name); case AST_ASSIGNMENT: + // Only print debug info if the node's actually an assignment + if (debug_flag) { + printf("free_ast: freeing AST var_name at %p -> '%s'\n", + (void *)node->assignment.variable_name, + node->assignment.variable_name); + } free(node->assignment.variable_name); free_ast(node->assignment.value); break; diff --git a/src/tests/16_ternary.flv b/src/tests/16_ternary.flv index d9ef9e9..5d8f4ac 100644 --- a/src/tests/16_ternary.flv +++ b/src/tests/16_ternary.flv @@ -1 +1,9 @@ -let a = 5; \ No newline at end of file +create check_even_odd(num) { + deliver num % 2 == 0 ? "Even" : "Odd"; +} + +for _ in 1..=1 { + serve("Enter a number:"); + let input = int(sample()); + serve(check_even_odd(input)); +} From b1aff65dd91a6bf29736b4988047f197d2bc3a99 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:00:34 +0000 Subject: [PATCH 42/62] Fix: All these memory errors were caused by this - All because I had `case AST_FUNCTION_DECLARATION` & `case AST_FUNCTION_CALL` share the same body in `free_ast` even though now they're different `struct` structures in `ast_types.h` #179 --- src/parser/utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/parser/utils.c b/src/parser/utils.c index 8782902..a643444 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -59,6 +59,11 @@ void free_ast(ASTNode *node) { break; case AST_FUNCTION_DECLARATION: + free(node->function_declaration.name); + free_ast(node->function_declaration.body); + free_ast(node->function_declaration.parameters); + break; + case AST_FUNCTION_CALL: free(node->function_call.name); free_ast(node->function_call.arguments); From 4839094cbda8804680d16caf5292d7bdd0dcb1df Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:02:30 +0000 Subject: [PATCH 43/62] Fix: `free_ast` now handles `AST_FUNCTION_RETURN` #179 --- src/parser/utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser/utils.c b/src/parser/utils.c index a643444..54527af 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -69,6 +69,10 @@ void free_ast(ASTNode *node) { free_ast(node->function_call.arguments); break; + case AST_FUNCTION_RETURN: + free_ast(node->function_return.return_data); + break; + case AST_BREAK: // No cleanup needed! break; From 485a50426952926ae4479dd892f77185071b0150 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:09:00 +0000 Subject: [PATCH 44/62] Fix: Memory issue in regards to freeing `function_declaration.parameters` #179 --- src/parser/utils.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/parser/utils.c b/src/parser/utils.c index 54527af..041f694 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -59,9 +59,34 @@ void free_ast(ASTNode *node) { break; case AST_FUNCTION_DECLARATION: + if (debug_flag) { + printf("free_ast: freeing function declaration `%s`\n", + node->function_declaration.name); + } + free(node->function_declaration.name); + + // Free function body recursively free_ast(node->function_declaration.body); - free_ast(node->function_declaration.parameters); + + // Free the function parameters + ASTFunctionParameter *param = node->function_declaration.parameters; + while (param) { + ASTFunctionParameter *next = param->next; + + if (debug_flag) { + printf("Freeing function_declaration.parameters: freeing " + "parameter `%s` " + "at %p\n", + param->parameter_name, + (void *)param->parameter_name); + } + + free(param->parameter_name); + free(param); + param = next; + } + break; case AST_FUNCTION_CALL: From d950c113735229b701f053633ae2801fdc062c94 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:10:07 +0000 Subject: [PATCH 45/62] Remove: Debugging messages in `case AST_FUNCTION_DECLARATION` in `free_ast` #179 --- src/parser/utils.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/parser/utils.c b/src/parser/utils.c index 041f694..e5795d1 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -59,29 +59,14 @@ void free_ast(ASTNode *node) { break; case AST_FUNCTION_DECLARATION: - if (debug_flag) { - printf("free_ast: freeing function declaration `%s`\n", - node->function_declaration.name); - } - free(node->function_declaration.name); - - // Free function body recursively - free_ast(node->function_declaration.body); + free_ast(node->function_declaration + .body); // free function body recursively // Free the function parameters ASTFunctionParameter *param = node->function_declaration.parameters; while (param) { ASTFunctionParameter *next = param->next; - - if (debug_flag) { - printf("Freeing function_declaration.parameters: freeing " - "parameter `%s` " - "at %p\n", - param->parameter_name, - (void *)param->parameter_name); - } - free(param->parameter_name); free(param); param = next; From 5dc2e53d78e45152ff0f0a62a5113635f2c43ddb Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:10:41 +0000 Subject: [PATCH 46/62] Remove: Debugging message in ` case AST_ASSIGNMENT` in `free_ast` #179 --- src/parser/utils.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/parser/utils.c b/src/parser/utils.c index e5795d1..183e1e5 100644 --- a/src/parser/utils.c +++ b/src/parser/utils.c @@ -7,12 +7,6 @@ void free_ast(ASTNode *node) { switch (node->type) { case AST_ASSIGNMENT: - // Only print debug info if the node's actually an assignment - if (debug_flag) { - printf("free_ast: freeing AST var_name at %p -> '%s'\n", - (void *)node->assignment.variable_name, - node->assignment.variable_name); - } free(node->assignment.variable_name); free_ast(node->assignment.value); break; From 0fc8ed0d80e7dc36cbd2b2122371dc58d04a23c0 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:11:04 +0000 Subject: [PATCH 47/62] Update 16_ternary.flv #179 --- src/tests/16_ternary.flv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/16_ternary.flv b/src/tests/16_ternary.flv index 5d8f4ac..b9ace4a 100644 --- a/src/tests/16_ternary.flv +++ b/src/tests/16_ternary.flv @@ -2,7 +2,7 @@ create check_even_odd(num) { deliver num % 2 == 0 ? "Even" : "Odd"; } -for _ in 1..=1 { +for _ in 1..=5 { serve("Enter a number:"); let input = int(sample()); serve(check_even_odd(input)); From 5cc509a363c34d90c849bc6bdabfa6c0ef19e101 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:43:55 +0000 Subject: [PATCH 48/62] Fix: Memory issue in `interpret_for_loop` - Error message was: ==38671==ERROR: AddressSanitizer: heap-use-after-free on address 0x6160000011c8 at pc 0x0001041d7960 bp 0x00016bc6d950 sp 0x00016bc6d948 READ of size 4 at 0x6160000011c8 thread T0 #0 0x1041d795c in interpret_for_loop interpreter.c:1003 #1 0x1041ca318 in interpret_node interpreter.c:81 #2 0x1041dd924 in interpret_program interpreter.c:132 #3 0x104191f80 in main main.c:94 #4 0x18a0fc270 () - By tracing through `interpret_for_loop` since it was brought up by Clang, this heap-use-after-free memory issue got solved with `loop_var` just needing to be freed #179 --- src/interpreter/interpreter.c | 97 +++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index dfbc4ed..b1b1100 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -851,7 +851,10 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { } // Extract loop components - char *loop_var = node->for_loop.loop_variable; + char *loop_var = strdup(node->for_loop.loop_variable); + if (!loop_var) { + return raise_error("Memory allocation failed for loop variable\n"); + } ASTNode *start_expr = node->for_loop.start_expr; ASTNode *end_expr = node->for_loop.end_expr; bool inclusive = node->for_loop.inclusive; @@ -861,29 +864,33 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { // Evaluate start and end expressions InterpretResult start_res = interpret_node(start_expr, env); if (start_res.is_error) { + free(loop_var); return start_res; } InterpretResult end_res = interpret_node(end_expr, env); if (end_res.is_error) { + free(loop_var); return end_res; } - // Determine start & end as floats for flexibility + // Determine start & end as doubles for flexibility double start_val, end_val; if (start_res.value.type == TYPE_FLOAT) { start_val = start_res.value.data.floating_point; } else if (start_res.value.type == TYPE_INTEGER) { - start_val = (FLOAT_SIZE)start_res.value.data.integer; + start_val = (double)start_res.value.data.integer; } else { + free(loop_var); return raise_error("Start expression in `for` loop must be numeric\n"); } if (end_res.value.type == TYPE_FLOAT) { end_val = end_res.value.data.floating_point; } else if (end_res.value.type == TYPE_INTEGER) { - end_val = (FLOAT_SIZE)end_res.value.data.integer; + end_val = (double)end_res.value.data.integer; } else { + free(loop_var); return raise_error("End expression in `for` loop must be numeric\n"); } @@ -892,14 +899,16 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { if (step_expr) { InterpretResult step_res = interpret_node(step_expr, env); if (step_res.is_error) { + free(loop_var); return step_res; } if (step_res.value.type == TYPE_FLOAT) { step = step_res.value.data.floating_point; } else if (step_res.value.type == TYPE_INTEGER) { - step = (FLOAT_SIZE)step_res.value.data.integer; + step = (double)step_res.value.data.integer; } else { + free(loop_var); return raise_error( "Step expression in `for` loop must be numeric\n"); } @@ -914,68 +923,57 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { // Validate step to prevent infinite loops if (step < 1e-9 && step > -1e-9) { + free(loop_var); return raise_error("Step value cannot be zero in `for` loop\n"); } - // Validate step to check if step is in correct direction - if ((start_val < end_val && step < 0) || - (start_val > end_val && step > 0)) { - return raise_error("Step value is in the wrong direction for the " - "specified range of the `for` loop\n"); + // Assign or update loop variable in the environment + InterpretResult var_res = allocate_variable(env, loop_var); + if (var_res.is_error) { + free(loop_var); + return var_res; // Propagate the error } - // Assign or update loop variable in the environment + // Initialize the loop variable with the start value Variable *var = get_variable(env, loop_var); if (!var) { - // Allocate variable and handle InterpretResult - InterpretResult var_res = allocate_variable(env, loop_var); - if (var_res.is_error) { - return var_res; // Propagate the error - } - - // Retrieve the newly allocated variable - var = get_variable(env, loop_var); - if (!var) { - return raise_error( - "Failed to allocate and retrieve variable `%s`.\n", loop_var); - } + free(loop_var); + return raise_error("Failed to allocate and retrieve variable `%s`.\n", + loop_var); + } - // Initialize the loop variable with the start value - if (start_res.value.type == TYPE_FLOAT) { - var->value.type = TYPE_FLOAT; - var->value.data.floating_point = start_val; - } else { - var->value.type = TYPE_INTEGER; - var->value.data.integer = (INT_SIZE)start_val; - } + if (start_res.value.type == TYPE_FLOAT) { + var->value.type = TYPE_FLOAT; + var->value.data.floating_point = start_val; } else { - // Update existing variable - if (start_res.value.type == TYPE_FLOAT || - var->value.type == TYPE_FLOAT) { - var->value.type = TYPE_FLOAT; - var->value.data.floating_point = start_val; - } else { - var->value.type = TYPE_INTEGER; - var->value.data.integer = (INT_SIZE)start_val; - } + var->value.type = TYPE_INTEGER; + var->value.data.integer = (INT_SIZE)start_val; } // Determine loop direction bool is_ascending = step > 0.0; while (1) { - // Fetch current value + // Re-fetch the variable pointer at the start of each iteration + var = get_variable(env, loop_var); + if (!var) { + free(loop_var); + return raise_error("Loop variable `%s` not found in environment\n", + loop_var); + } + double current_val; if (var->value.type == TYPE_FLOAT) { current_val = var->value.data.floating_point; } else if (var->value.type == TYPE_INTEGER) { - current_val = (FLOAT_SIZE)var->value.data.integer; + current_val = (double)var->value.data.integer; } else { + free(loop_var); return raise_error("Loop variable `%s` must be numeric\n", loop_var); } - // Check if condition is still valid + // Check if the loop should continue bool condition_true = false; if (is_ascending) { condition_true = @@ -994,11 +992,21 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { while (current) { InterpretResult body_res = interpret_node(current, env); if (body_res.did_return || body_res.did_break) { + free(loop_var); return body_res; } current = current->next; } + // Re-fetch the variable pointer after interpreting the loop body + var = get_variable(env, loop_var); + if (!var) { + free(loop_var); + return raise_error( + "Loop variable `%s` not found in environment after loop body\n", + loop_var); + } + // Update loop variable if (var->value.type == TYPE_FLOAT) { var->value.data.floating_point += step; @@ -1007,6 +1015,9 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) { } } + // Clean up + free(loop_var); + // Loops do not return values return make_result(create_default_value(), false, false); } From 684be4218a35c87f3426b6631cb79b1272861092 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:14:07 +0000 Subject: [PATCH 49/62] Fixing issues with operators post-migration to `InterpretResult` #179 --- src/interpreter/builtins.c | 39 --- src/interpreter/interpreter.c | 531 ++++++++++++++++++---------------- src/interpreter/interpreter.h | 3 +- src/interpreter/utils.c | 50 ++++ src/interpreter/utils.h | 9 + 5 files changed, 345 insertions(+), 287 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 5b00dd1..14d73a0 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -317,45 +317,6 @@ InterpretResult builtin_error(ASTNode *node, Environment *env) { return raise_error("%s", error_message); } -bool is_valid_int(const char *str, INT_SIZE *out_value) { - char *endptr; - errno = 0; // reset errno before conversion - long long temp = strtoll(str, &endptr, 10); - - // Check for conversion errors - if (errno != 0 || endptr == str || *endptr != '\0') { - return false; - } - - // Optionally, check for overflow - if (temp < LLONG_MIN || temp > LLONG_MAX) { - return false; - } - - if (out_value) { - *out_value = (INT_SIZE)temp; - } - - return true; -} - -bool is_valid_float(const char *str, FLOAT_SIZE *out_value) { - char *endptr; - errno = 0; // reset errno before conversion - long double temp = strtold(str, &endptr); - - // Check for conversion errors - if (errno != 0 || endptr == str || *endptr != '\0') { - return false; - } - - if (out_value) { - *out_value = (FLOAT_SIZE)temp; - } - - return true; -} - InterpretResult builtin_cast(ASTNode *node, Environment *env) { if (node->type != AST_FUNCTION_CALL) { return raise_error( diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index b1b1100..2f44569 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -243,40 +243,32 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) { return make_result(new_value, false, false); } -InterpretResult handle_string_concatenation(InterpretResult left_res, - InterpretResult right_res) { - // Check for errors in operands - if (left_res.is_error) { - return left_res; - } - if (right_res.is_error) { - return right_res; - } - - LiteralValue left = left_res.value; - LiteralValue right = right_res.value; +InterpretResult handle_string_concatenation(InterpretResult left, + InterpretResult right) { + LiteralValue lv_result; + lv_result.type = TYPE_STRING; - LiteralValue result; - result.type = TYPE_STRING; + LiteralValue left_val = left.value; + LiteralValue right_val = right.value; // Convert numbers to strings char num_str1[50] = {0}; char num_str2[50] = {0}; - if (left.type == TYPE_FLOAT) { + if (left_val.type == TYPE_FLOAT) { snprintf(num_str1, sizeof(num_str1), FLOAT_FORMAT_SPECIFIER, - left.data.floating_point); + left_val.data.floating_point); } - if (right.type == TYPE_FLOAT) { + if (right_val.type == TYPE_FLOAT) { snprintf(num_str2, sizeof(num_str2), FLOAT_FORMAT_SPECIFIER, - right.data.floating_point); + right_val.data.floating_point); } // Allocate memory for the concatenated string size_t new_size = strlen(num_str1) + strlen(num_str2) + - strlen(left.type == TYPE_STRING ? left.data.string : "") + - strlen(right.type == TYPE_STRING ? right.data.string : "") + 1; + strlen(left_val.type == TYPE_STRING ? left_val.data.string : "") + + strlen(right_val.type == TYPE_STRING ? right_val.data.string : "") + 1; char *new_string = malloc(new_size); if (!new_string) { @@ -284,149 +276,243 @@ InterpretResult handle_string_concatenation(InterpretResult left_res, "Memory allocation failed for string concatenation.\n"); } - strcpy(new_string, left.type == TYPE_STRING ? left.data.string : num_str1); + strcpy(new_string, + left_val.type == TYPE_STRING ? left_val.data.string : num_str1); strcat(new_string, - right.type == TYPE_STRING ? right.data.string : num_str2); - result.data.string = new_string; + right_val.type == TYPE_STRING ? right_val.data.string : num_str2); + lv_result.data.string = new_string; - return make_result(result, false, false); + return make_result(lv_result, false, false); } -LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand) { - // Debugging output - debug_print_int("Unary Operator: `%s`\n", op); - - LiteralValue result; - memset(&result, 0, sizeof(LiteralValue)); // initialize result - - // Handle logical NOT - if (strcmp(op, "!") == 0) { - // Ensure operand is boolean - if (operand.type != TYPE_BOOLEAN) { - fatal_error("Unary operator `%s` requires a boolean operand.\n", - op); - } - - result.type = TYPE_BOOLEAN; - result.data.boolean = !operand.data.boolean; - return result; - } - - // Handle unary minus - if (strcmp(op, "-") == 0) { - if (operand.type == TYPE_INTEGER) { - result.type = TYPE_INTEGER; - result.data.integer = -operand.data.integer; - } else if (operand.type == TYPE_FLOAT) { - result.type = TYPE_FLOAT; - result.data.floating_point = -operand.data.floating_point; - } else { - fatal_error("Unary operator `%s` requires a numeric operand.\n", - op); - } - return result; +// Helper function to handle numeric operations and comparisons +InterpretResult handle_numeric_operator(const char *op, + InterpretResult left_res, + InterpretResult right_res) { + // Check for errors in operands + if (left_res.is_error) { + return left_res; } - - // Handle unary plus (no effect) - if (strcmp(op, "+") == 0) { - // Unary plus doesn't change the operand - result = operand; - return result; + if (right_res.is_error) { + return right_res; } - // Add more unary operators as needed - - fatal_error("Unknown unary operator `%s`\n", op); - return result; // unreachable -} + LiteralValue left = left_res.value; + LiteralValue right = right_res.value; -InterpretResult interpret_unary_op(ASTNode *node, Environment *env) { - if (!node || node->type != AST_UNARY_OP) { - return raise_error("Invalid unary operation node."); + // Ensure both operands are numeric + if (!is_numeric_type(left.type) || !is_numeric_type(right.type)) { + return raise_error("Operator `%s` requires numeric operands.\n", op); } - // Extract the operator and operand - const char *op = node->unary_op.operator; - ASTNode *operand_node = node->unary_op.operand; + // Determine if the result should be a float + bool result_is_float = + (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT); - // Interpret the operand - InterpretResult operand_r = interpret_node(operand_node, env); - if (operand_r.is_error) { - return operand_r; - } - - LiteralValue operand = operand_r.value; + // Fetch numeric values with coercion + double left_val = (left.type == TYPE_FLOAT) ? left.data.floating_point + : (double)left.data.integer; + double right_val = (right.type == TYPE_FLOAT) ? right.data.floating_point + : (double)right.data.integer; - // Evaluate the unary operator - LiteralValue result = evaluate_unary_operator(op, operand); + LiteralValue result; + memset(&result, 0, sizeof(LiteralValue)); - if (result.type == TYPE_ERROR) { - InterpretResult res; - res.value = result; - res.did_return = false; - res.did_break = false; - res.is_error = true; - return res; + // Handle operators + if (strcmp(op, "*") == 0) { + if (result_is_float) { + result.type = TYPE_FLOAT; + result.data.floating_point = left_val * right_val; + } else { + result.type = TYPE_INTEGER; + result.data.integer = (INT_SIZE)(left_val * right_val); + } + } else if (strcmp(op, "-") == 0) { + if (result_is_float) { + result.type = TYPE_FLOAT; + result.data.floating_point = left_val - right_val; + } else { + result.type = TYPE_INTEGER; + result.data.integer = (INT_SIZE)(left_val - right_val); + } + } else if (strcmp(op, "/") == 0) { + if (right_val == 0.0) { + return raise_error("Division by zero.\n"); + } + result.type = TYPE_FLOAT; + result.data.floating_point = left_val / right_val; + } else if (strcmp(op, "//") == 0) { // floor Division + if (right_val == 0.0) { + return raise_error("Floor division by zero.\n"); + } + double div_result = left_val / right_val; + if (result_is_float) { + result.type = TYPE_FLOAT; + result.data.floating_point = floor(div_result); + } else { + result.type = TYPE_INTEGER; + result.data.integer = (INT_SIZE)floor(div_result); + } + } else if (strcmp(op, "%") == 0) { // modulo + if (right_val == 0.0) { + return raise_error("Modulo by zero.\n"); + } + if (result_is_float) { + result.type = TYPE_FLOAT; + result.data.floating_point = fmod(left_val, right_val); + } else { + result.type = TYPE_INTEGER; + result.data.integer = + (INT_SIZE)((INT_SIZE)left_val % (INT_SIZE)right_val); + } + } else if (strcmp(op, "**") == 0) { // exponentiation + if (result_is_float) { + result.type = TYPE_FLOAT; + result.data.floating_point = pow(left_val, right_val); + } else { + result.type = TYPE_INTEGER; + result.data.integer = (INT_SIZE)pow(left_val, right_val); + } + } else if (strcmp(op, "<") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_val < right_val); + } else if (strcmp(op, ">") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_val > right_val); + } else if (strcmp(op, "<=") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_val <= right_val); + } else if (strcmp(op, ">=") == 0) { + result.type = TYPE_BOOLEAN; + result.data.boolean = (left_val >= right_val); + } else { + return raise_error("Unknown operator `%s`.\n", op); } return make_result(result, false, false); } -// Helper function to check if a type is numeric -bool is_numeric_type(LiteralType type) { - return type == TYPE_INTEGER || type == TYPE_FLOAT; -} - -// Helper function to check if a type is boolean -bool is_boolean_type(LiteralType type) { return type == TYPE_BOOLEAN; } - +// Function to evaluate binary operators InterpretResult evaluate_operator(const char *op, InterpretResult left_res, InterpretResult right_res) { debug_print_int("Operator: `%s`\n", op); - // Check for errors in operands - if (left_res.is_error) { - return left_res; - } - if (right_res.is_error) { - return right_res; - } - - LiteralValue left = left_res.value; - LiteralValue right = right_res.value; - - LiteralValue result; - memset(&result, 0, sizeof(LiteralValue)); // Initialize result - // Handle string concatenation with "+" operator - if (strcmp(op, "+") == 0 && - (left.type == TYPE_STRING || right.type == TYPE_STRING)) { + if (strcmp(op, "+") == 0 && (left_res.value.type == TYPE_STRING || + right_res.value.type == TYPE_STRING)) { return handle_string_concatenation(left_res, right_res); } // Handle logical AND and OR if (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0) { // Ensure both operands are boolean - if (!is_boolean_type(left.type) || !is_boolean_type(right.type)) { + if (!is_boolean_type(left_res.value.type) || + !is_boolean_type(right_res.value.type)) { return raise_error( "Logical operators `&&` and `||` require boolean operands.\n"); } + LiteralValue result; result.type = TYPE_BOOLEAN; if (strcmp(op, "&&") == 0) { - result.data.boolean = left.data.boolean && right.data.boolean; + result.data.boolean = + left_res.value.data.boolean && right_res.value.data.boolean; } else { // op == "||" - result.data.boolean = left.data.boolean || right.data.boolean; + result.data.boolean = + left_res.value.data.boolean || right_res.value.data.boolean; + } + + return make_result(result, false, false); + } + + // Handle Equality Operators `==` and `!=` Across All Types + if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0) { + bool comparison_result = false; + + // If types are the same, perform direct comparison + if (left_res.value.type == right_res.value.type) { + switch (left_res.value.type) { + case TYPE_INTEGER: + comparison_result = (left_res.value.data.integer == + right_res.value.data.integer); + break; + case TYPE_FLOAT: + comparison_result = (left_res.value.data.floating_point == + right_res.value.data.floating_point); + break; + case TYPE_BOOLEAN: + comparison_result = (left_res.value.data.boolean == + right_res.value.data.boolean); + break; + case TYPE_STRING: + if (left_res.value.data.string == NULL || + right_res.value.data.string == NULL) { + return raise_error("Cannot compare NULL strings.\n"); + } + comparison_result = (strcmp(left_res.value.data.string, + right_res.value.data.string) == 0); + break; + default: + return raise_error("Equality operators `==` and `!=` are not " + "supported for this type.\n"); + } + } + // Handle cross-type comparisons + else { + // For simplicity, handle numeric comparisons by coercing to float + if (is_numeric_type(left_res.value.type) && + is_numeric_type(right_res.value.type)) { + return handle_numeric_operator(op, left_res, right_res); + } + // Handle boolean and integer comparisons + else if ((left_res.value.type == TYPE_BOOLEAN && + is_numeric_type(right_res.value.type)) || + (right_res.value.type == TYPE_BOOLEAN && + is_numeric_type(left_res.value.type))) { + // Coerce boolean to integer (false=0, true=1) + LiteralValue coerced_left = left_res.value; + LiteralValue coerced_right = right_res.value; + + if (left_res.value.type == TYPE_BOOLEAN) { + coerced_left.type = TYPE_INTEGER; + coerced_left.data.integer = + left_res.value.data.boolean ? 1 : 0; + } + if (right_res.value.type == TYPE_BOOLEAN) { + coerced_right.type = TYPE_INTEGER; + coerced_right.data.integer = + right_res.value.data.boolean ? 1 : 0; + } + + return handle_numeric_operator( + op, make_result(coerced_left, false, false), + make_result(coerced_right, false, false)); + } + // Handle string and other type comparisons if necessary + else { + return raise_error("Cannot compare different types.\n"); + } } + // Apply `!=` logic if operator is "!=" + if (strcmp(op, "!=") == 0) { + comparison_result = !comparison_result; + } + + // Set the result + LiteralValue result; + result.type = TYPE_BOOLEAN; + result.data.boolean = comparison_result; + return make_result(result, false, false); } - // **New Type Validation for Numeric Operators** + // Handle Arithmetic and Comparison Operators // List of operators that require numeric operands - const char *numeric_operators[] = {"*", "+", "-", "/", "//", "%", "**", - "<", ">", "<=", ">=", "==", "!="}; + const char *numeric_operators[] = { + "*", "-", "/", "//", "%", "**", "<", ">", "<=", ">=", + }; size_t num_numeric_ops = sizeof(numeric_operators) / sizeof(numeric_operators[0]); @@ -439,149 +525,55 @@ InterpretResult evaluate_operator(const char *op, InterpretResult left_res, } if (is_numeric_op) { - // Ensure both operands are numeric - if (!is_numeric_type(left.type) || !is_numeric_type(right.type)) { - return raise_error("Operator `%s` requires numeric operands.\n", - op); - } - - // Proceed to get numeric values - double left_value = 0.0, right_value = 0.0; - if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { - left_value = (left.type == TYPE_FLOAT) ? left.data.floating_point - : (double)left.data.integer; - right_value = (right.type == TYPE_FLOAT) - ? right.data.floating_point - : (double)right.data.integer; - } else { - left_value = (double)left.data.integer; - right_value = (double)right.data.integer; - } - - // Determine result type based on operands - if (left.type == TYPE_FLOAT || right.type == TYPE_FLOAT) { - result.type = TYPE_FLOAT; - } else { - result.type = TYPE_INTEGER; - } - - // Handle operators - if (strcmp(op, "*") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = left_value * right_value; - else - result.data.integer = (INT_SIZE)(left_value * right_value); - } else if (strcmp(op, "+") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = left_value + right_value; - else - result.data.integer = (INT_SIZE)(left_value + right_value); - } else if (strcmp(op, "-") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = left_value - right_value; - else - result.data.integer = (INT_SIZE)(left_value - right_value); - } else if (strcmp(op, "/") == 0) { - if (right_value == 0) { - return raise_error("Division by zero\n"); - } - result.type = TYPE_FLOAT; - result.data.floating_point = left_value / right_value; - } else if (strcmp(op, "//") == 0) { - if (right_value == 0) { - return raise_error("Floor division by zero\n"); - } - if (result.type == TYPE_FLOAT) - result.data.floating_point = floor(left_value / right_value); - else - result.data.integer = (INT_SIZE)(left_value / right_value); - } else if (strcmp(op, "%") == 0) { - if (right_value == 0) { - return raise_error("Modulo by zero\n"); - } - if (result.type == TYPE_FLOAT) { - result.data.floating_point = fmod(left_value, right_value); - } else { - result.data.integer = - (INT_SIZE)((INT_SIZE)left_value % (INT_SIZE)right_value); - } - } else if (strcmp(op, "**") == 0) { - if (result.type == TYPE_FLOAT) - result.data.floating_point = pow(left_value, right_value); - else - result.data.integer = (INT_SIZE)pow(left_value, right_value); - } else if (strcmp(op, "<") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value < right_value) ? 1 : 0; - } else if (strcmp(op, ">") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value > right_value) ? 1 : 0; - } else if (strcmp(op, "<=") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value <= right_value) ? 1 : 0; - } else if (strcmp(op, ">=") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value >= right_value) ? 1 : 0; - } else if (strcmp(op, "==") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value == right_value) ? 1 : 0; - } else if (strcmp(op, "!=") == 0) { - result.type = TYPE_BOOLEAN; - result.data.boolean = (left_value != right_value) ? 1 : 0; - } else { - return raise_error("Unknown operator"); - } - - return make_result(result, false, false); + return handle_numeric_operator(op, left_res, right_res); } // If operator is not recognized - return raise_error("Unknown operator"); -} - -int get_operator_precedence(const char *op) { - if (strcmp(op, "**") == 0) - return 4; // highest precedence - if (strcmp(op, "*") == 0 || strcmp(op, "/") == 0 || strcmp(op, "//") == 0 || - strcmp(op, "%") == 0) - return 3; - if (strcmp(op, "+") == 0 || strcmp(op, "-") == 0) - return 2; - if (strcmp(op, "<") == 0 || strcmp(op, ">") == 0 || strcmp(op, "<=") == 0 || - strcmp(op, ">=") == 0) - return 1; - if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0) - return 1; - - // Return `0` for unknown operators - return 0; -} - -int is_right_associative(const char *op) { - return (strcmp(op, "**") == 0); // exponentiation is right-associative + return raise_error("Unknown operator `%s`.\n", op); } InterpretResult interpret_binary_op(ASTNode *node, Environment *env) { - if (!node || node->type != AST_BINARY_OP) { - return raise_error("Invalid binary operation node."); + if (node->type != AST_BINARY_OP) { + return raise_error("Invalid node type for binary operation.\n"); } // Interpret left operand - InterpretResult left_r = interpret_node(node->binary_op.left, env); - if (left_r.is_error) { - return left_r; + InterpretResult left_res = interpret_node(node->binary_op.left, env); + if (left_res.is_error) { + return left_res; } // Interpret right operand - InterpretResult right_r = interpret_node(node->binary_op.right, env); - if (right_r.is_error) { - return right_r; + InterpretResult right_res = interpret_node(node->binary_op.right, env); + if (right_res.is_error) { + return right_res; } - // Evaluate based on operator + // Evaluate the operator const char *op = node->binary_op.operator; + InterpretResult op_res = evaluate_operator(op, left_res, right_res); + if (op_res.is_error) { + return op_res; + } + + return make_result(op_res.value, false, false); +} - InterpretResult op_res = evaluate_operator(op, left_r, right_r); +// Implementation of interpret_unary_op +InterpretResult interpret_unary_op(ASTNode *node, Environment *env) { + if (node->type != AST_UNARY_OP) { + return raise_error("Invalid node type for unary operation.\n"); + } + + // Interpret the operand + InterpretResult operand_res = interpret_node(node->unary_op.operand, env); + if (operand_res.is_error) { + return operand_res; + } + + // Evaluate the unary operator + const char *op = node->unary_op.operator; + InterpretResult op_res = evaluate_unary_operator(op, operand_res); if (op_res.is_error) { return op_res; } @@ -589,6 +581,51 @@ InterpretResult interpret_binary_op(ASTNode *node, Environment *env) { return make_result(op_res.value, false, false); } +// Helper function to handle unary operators +InterpretResult evaluate_unary_operator(const char *op, + InterpretResult operand_res) { + debug_print_int("Unary Operator: `%s`\n", op); + + // Check for errors in operand + if (operand_res.is_error) { + return operand_res; + } + + LiteralValue operand = operand_res.value; + LiteralValue result; + + if (strcmp(op, "-") == 0) { + // Arithmetic negation + if (operand.type == TYPE_INTEGER) { + result.type = TYPE_INTEGER; + result.data.integer = -operand.data.integer; + } else if (operand.type == TYPE_FLOAT) { + result.type = TYPE_FLOAT; + result.data.floating_point = -operand.data.floating_point; + } else { + return raise_error( + "Unary `-` operator requires numeric operand.\n"); + } + } else if (strcmp(op, "!") == 0) { + // Logical NOT + if (operand.type == TYPE_BOOLEAN) { + result.type = TYPE_BOOLEAN; + result.data.boolean = !operand.data.boolean; + } else if (operand.type == TYPE_INTEGER) { + // Treat 0 as false, non-zero as true + result.type = TYPE_BOOLEAN; + result.data.boolean = (operand.data.integer == 0) ? true : false; + } else { + return raise_error( + "Unary `!` operator requires boolean or integer operand.\n"); + } + } else { + return raise_error("Unsupported unary operator `%s`.\n", op); + } + + return make_result(result, false, false); +} + Variable *get_variable(Environment *env, const char *variable_name) { debug_print_int("Looking up variable: `%s`\n", variable_name); for (size_t i = 0; i < env->variable_count; i++) { diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index 3063a5d..f11e16d 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -26,7 +26,8 @@ InterpretResult interpret_switch(ASTNode *node, Environment *env); InterpretResult interpret_function_declaration(ASTNode *node, Environment *env); InterpretResult interpret_function_call(ASTNode *node, Environment *env); InterpretResult interpret_unary_op(ASTNode *node, Environment *env); -LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand); +InterpretResult evaluate_unary_operator(const char *op, + InterpretResult operand_res); InterpretResult interpret_ternary(ASTNode *node, Environment *env); InterpretResult call_user_defined_function(Function *func_ref, ASTNode *call_node, diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index cf9c844..b0a7b25 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -1,4 +1,5 @@ #include "utils.h" +#include InterpretResult raise_error(const char *format, ...) { char error_message[1024]; @@ -480,3 +481,52 @@ InterpretResult make_result(LiteralValue val, bool did_return, bool did_break) { r.is_error = false; return r; } + +bool is_valid_int(const char *str, INT_SIZE *out) { + char *endptr; + errno = 0; + long long val = strtoll(str, &endptr, 10); + if (errno != 0 || *endptr != '\0') { + return false; + } + *out = (INT_SIZE)val; + return true; +} + +bool is_valid_float(const char *str, FLOAT_SIZE *out) { + char *endptr; + errno = 0; + long double val = strtold(str, &endptr); + if (errno != 0 || *endptr != '\0') { + return false; + } + *out = (FLOAT_SIZE)val; + return true; +} + +// Type checking helpers +bool is_numeric_type(LiteralType type) { + return type == TYPE_INTEGER || type == TYPE_FLOAT; +} + +bool is_boolean_type(LiteralType type) { return type == TYPE_BOOLEAN; } + +// Convert LiteralType to string for error messages +const char *literal_type_to_string(LiteralType type) { + switch (type) { + case TYPE_INTEGER: + return "integer"; + case TYPE_FLOAT: + return "float"; + case TYPE_BOOLEAN: + return "boolean"; + case TYPE_STRING: + return "string"; + case TYPE_FUNCTION: + return "function"; + case TYPE_ERROR: + return "error"; + default: + return "unknown"; + } +} diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h index 9c50cde..2c23344 100644 --- a/src/interpreter/utils.h +++ b/src/interpreter/utils.h @@ -30,4 +30,13 @@ Function *get_function(Environment *env, const char *name); InterpretResult make_result(LiteralValue val, bool did_return, bool did_break); ASTCatchNode *copy_catch_node(ASTCatchNode *catch_node); +// Type Helpers +bool is_numeric_type(LiteralType type); +bool is_boolean_type(LiteralType type); +const char *literal_type_to_string(LiteralType type); + +// Casting Helpers +bool is_valid_int(const char *str, INT_SIZE *out); +bool is_valid_float(const char *str, FLOAT_SIZE *out); + #endif From db0bec5a8af6086652b459175a4ea9bdd94a732d Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:31:59 +0000 Subject: [PATCH 50/62] Fix: String concatenation error #179 --- src/interpreter/interpreter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 2f44569..45bb434 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -528,6 +528,10 @@ InterpretResult evaluate_operator(const char *op, InterpretResult left_res, return handle_numeric_operator(op, left_res, right_res); } + if (strcmp(op, "+") == 0) { + return handle_string_concatenation(left_res, right_res); + } + // If operator is not recognized return raise_error("Unknown operator `%s`.\n", op); } From 7cf4f1ca0ac4692283228e874e56edf6b902c4eb Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:32:48 +0000 Subject: [PATCH 51/62] Fix: `builtin_random` to handle 0/1/2 args - Example/test 14 #179 --- src/interpreter/builtins.c | 60 ++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 14d73a0..7406b46 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -179,20 +179,45 @@ InterpretResult builtin_input(ASTNode *node, Environment *env) { // Built-in `random()` function with 0, 1, or 2 arguments InterpretResult builtin_random(ASTNode *node, Environment *env) { - FLOAT_SIZE min = 0.0L; - FLOAT_SIZE max = 1.0L; + FLOAT_SIZE min = 0.0L; // default min + FLOAT_SIZE max = 1.0L; // default max - ArgumentSpec specs[2]; - specs[0].type = ARG_TYPE_FLOAT; - specs[0].out_ptr = &min; - specs[1].type = ARG_TYPE_FLOAT; - specs[1].out_ptr = &max; + ASTNode *arg_node = node->function_call.arguments; - InterpretResult args_res = - interpret_arguments(node->function_call.arguments, env, 2, specs); - if (args_res.is_error) { - LiteralValue lv = (LiteralValue){.type = TYPE_ERROR}; - return make_result(lv, false, false); + size_t num_args = 0; + ASTNode *temp = arg_node; + while (temp) { + num_args++; + temp = temp->next; + } + if (num_args > 2) { + return raise_error( + "`random()` takes at most 2 arguments, but %zu provided.\n", + num_args); + } + + if (num_args == 1) { + // One argument provided: set max, min remains 0.0 + ArgumentSpec specs[1]; + specs[0].type = ARG_TYPE_FLOAT; + specs[0].out_ptr = &max; + + InterpretResult args_res = interpret_arguments(arg_node, env, 1, specs); + if (args_res.is_error) { + return args_res; + } + } else if (num_args == 2) { + // Two arguments provided: set min and max + ArgumentSpec specs[2]; + specs[0].type = ARG_TYPE_FLOAT; + specs[0].out_ptr = &min; + specs[1].type = ARG_TYPE_FLOAT; + specs[1].out_ptr = &max; + + InterpretResult args_res = interpret_arguments(arg_node, env, 2, specs); + if (args_res.is_error) { + return args_res; + } } // Seed the random number generator once @@ -202,13 +227,14 @@ InterpretResult builtin_random(ASTNode *node, Environment *env) { seeded = true; } - // Swap min and max if min > max + // Swap min & max if min > max to ensure correct range if (min > max) { - FLOAT_SIZE temp = min; + FLOAT_SIZE temp_val = min; min = max; - max = temp; + max = temp_val; } + // Generate random number FLOAT_SIZE random_number = min + ((FLOAT_SIZE)rand() / (FLOAT_SIZE)RAND_MAX) * (max - min); @@ -247,7 +273,9 @@ InterpretResult builtin_output(ASTNode *node, Environment *env) { printf("%s", lv.data.boolean ? "True" : "False"); break; case TYPE_ERROR: - fprintf(stderr, "Error: Invalid literal type in `serve()`.\n"); + fprintf( + stderr, + "Error: Invalid literal type in `serve()` (`TYPE_ERROR`).\n"); break; default: fprintf(stderr, "Error: Unknown literal type in s`erve()`.\n"); From fbb1af8694a5c3c07a014b1f5670f94361efc1cf Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:47:01 +0000 Subject: [PATCH 52/62] Fix: Redefining constant error in `tests/all.flv` - Error message was: "Constant favorite is already defined." #179 --- src/tests/all.flv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/all.flv b/src/tests/all.flv index 7f89993..a0fd854 100644 --- a/src/tests/all.flv +++ b/src/tests/all.flv @@ -198,8 +198,8 @@ serve(float(False)); # ================================================== # Using `sample()` to get user input serve("Enter your favorite number:"); -const favorite = sample(); -serve("Your favorite is:", favorite); +const favorite2 = sample(); +serve("Your favorite is:", favorite2); # Using `random()` with different argument counts const num1 = random(); # Generates between 0.0 and 1.0 From 51706a8f1912480d88b793f2f3b8d9cb9d7b2865 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:49:23 +0000 Subject: [PATCH 53/62] Fix: `+` operator only working for strings - E.g., example/test 13: ``` const a = 1; serve(a + a); serve(string(a) + string(a)); ``` #179 --- src/interpreter/interpreter.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 45bb434..0f52ba7 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -319,7 +319,15 @@ InterpretResult handle_numeric_operator(const char *op, memset(&result, 0, sizeof(LiteralValue)); // Handle operators - if (strcmp(op, "*") == 0) { + if (strcmp(op, "+") == 0) { + if (result_is_float) { + result.type = TYPE_FLOAT; + result.data.floating_point = left_val + right_val; + } else { + result.type = TYPE_INTEGER; + result.data.integer = (INT_SIZE)(left_val + right_val); + } + } else if (strcmp(op, "*") == 0) { if (result_is_float) { result.type = TYPE_FLOAT; result.data.floating_point = left_val * right_val; @@ -511,7 +519,7 @@ InterpretResult evaluate_operator(const char *op, InterpretResult left_res, // Handle Arithmetic and Comparison Operators // List of operators that require numeric operands const char *numeric_operators[] = { - "*", "-", "/", "//", "%", "**", "<", ">", "<=", ">=", + "+", "*", "-", "/", "//", "%", "**", "<", ">", "<=", ">=", }; size_t num_numeric_ops = sizeof(numeric_operators) / sizeof(numeric_operators[0]); From a7e54d8c204490f258e65adf3fc54c6eb92149f8 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:56:38 +0000 Subject: [PATCH 54/62] Update all.flv #179 --- src/tests/all.flv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/all.flv b/src/tests/all.flv index a0fd854..6e20ffb 100644 --- a/src/tests/all.flv +++ b/src/tests/all.flv @@ -87,7 +87,7 @@ try { int("abc"); serve("This won't run!"); } rescue { - serve("Runtime error: Can't cast string `abc` to int."); + serve("(Expect a) Runtime error: Can't cast string `abc` to int."); } finish { serve("This always executes!"); } From c1797e8f424c124dde2ba9c703dc12c73ade9ff1 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:56:50 +0000 Subject: [PATCH 55/62] Update 7_try_catch.flv #179 --- src/tests/7_try_catch.flv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/7_try_catch.flv b/src/tests/7_try_catch.flv index 6df0c2a..705cd5b 100644 --- a/src/tests/7_try_catch.flv +++ b/src/tests/7_try_catch.flv @@ -10,7 +10,7 @@ try { int("abc"); serve("This won't run!"); } rescue { - serve("Runtime error: Can't cast string `abc` to int."); + serve("(Expect a) Runtime error: Can't cast string `abc` to int."); } finish { serve("This always executes!"); } From 59762cbae57217a268e19cb0b53812c257d3662f Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:56:55 +0000 Subject: [PATCH 56/62] Update builtins.c #179 --- src/interpreter/builtins.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 7406b46..35b04d8 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -5,13 +5,6 @@ #include #include -// Helper function to handle debug printing for floats -#define DEBUG_PRINT_FLOAT(format, ...) \ - do { \ - if (debug_flag) \ - printf(format, __VA_ARGS__); \ - } while (0) - // Function to interpret a mix of argument types InterpretResult interpret_arguments(ASTNode *node, Environment *env, size_t num_args, ArgumentSpec *specs) { @@ -172,7 +165,7 @@ InterpretResult builtin_input(ASTNode *node, Environment *env) { return make_result(lv, false, false); } - DEBUG_PRINT_FLOAT("Input received: `%s`\n", result.data.string); + debug_print_int("Input received: `%s`\n", result.data.string); return make_result(result, false, false); } @@ -238,8 +231,8 @@ InterpretResult builtin_random(ASTNode *node, Environment *env) { FLOAT_SIZE random_number = min + ((FLOAT_SIZE)rand() / (FLOAT_SIZE)RAND_MAX) * (max - min); - DEBUG_PRINT_FLOAT("Random number generated (min: %Lf, max: %Lf): `%Lf`\n", - min, max, random_number); + debug_print_int("Random number generated (min: %Lf, max: %Lf): `%Lf`\n", + min, max, random_number); LiteralValue result; result.type = TYPE_FLOAT; From 00e5b3ca2c7772d33d06f8e1b477d0364f823046 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:26:40 +0000 Subject: [PATCH 57/62] Fix: Environment type mismatch warnings in interpreter - Fixed type mismatches between `Environment *` & `struct Environment *` by ensuring proper `typedef` usage in `interpreter_types.h`. - Updated all `.c` files (`interpreter.c`, `utils.c`) to consistently use the `Environment` `typedef`. - Added include guards & ensured proper inclusion of `interpreter_types.h` in all relevant files. - Implemented consistent initialization of environments, preventing redundancy in built-in function setups. - Verified changes with clean recompilation & removed all compiler warnings. #179 --- src/interpreter/interpreter.c | 99 +++++++++++++++-------------- src/interpreter/interpreter_types.h | 9 ++- src/interpreter/utils.c | 46 ++++++++++++-- src/interpreter/utils.h | 5 +- 4 files changed, 103 insertions(+), 56 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 0f52ba7..20a959b 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -129,11 +129,15 @@ void interpret_program(ASTNode *program, Environment *env) { ASTNode *current = program; while (current) { debug_print_int("Executing top-level statement\n"); - interpret_node(current, env); - // if (res.is_error) { - // fprintf(stderr, "Unhandled exception: %s\n", - // res.value.data.string); exit(1); - // } + InterpretResult res = interpret_node(current, env); + if (res.is_error) { + fprintf(stderr, "Unhandled error: %s\n", res.value.data.string); + break; // (or handle as needed in future) + } + if (res.did_return) { + // Handle unexpected return at top-level, if applicable + break; + } current = current->next; } } @@ -640,28 +644,45 @@ InterpretResult evaluate_unary_operator(const char *op, Variable *get_variable(Environment *env, const char *variable_name) { debug_print_int("Looking up variable: `%s`\n", variable_name); - for (size_t i = 0; i < env->variable_count; i++) { - if (strcmp(env->variables[i].variable_name, variable_name) == 0) { - if (env->variables[i].value.type == TYPE_FLOAT) { - debug_print_int("Variable found: `%s` with value `%Lf`\n", - variable_name, - env->variables[i].value.data.floating_point); - } else if (env->variables[i].value.type == TYPE_INTEGER) { - debug_print_int("Variable found: `%s` with value `%lld`\n", - variable_name, - env->variables[i].value.data.integer); - } else if (env->variables[i].value.type == TYPE_STRING) { - debug_print_int("Variable found: `%s` with value `%s`\n", - variable_name, - env->variables[i].value.data.string); - } else if (env->variables[i].value.type == TYPE_FUNCTION) { - debug_print_int( - "Variable found: `%s` with function reference `%s`\n", - variable_name, - env->variables[i].value.data.function_ptr->name); + Environment *current_env = env; + while (current_env) { + for (size_t i = 0; i < current_env->variable_count; i++) { + if (strcmp(current_env->variables[i].variable_name, + variable_name) == 0) { + // Debugging information based on type + switch (current_env->variables[i].value.type) { + case TYPE_FLOAT: + debug_print_int( + "Variable found: `%s` with value `%Lf`\n", + variable_name, + current_env->variables[i].value.data.floating_point); + break; + case TYPE_INTEGER: + debug_print_int( + "Variable found: `%s` with value `%lld`\n", + variable_name, + current_env->variables[i].value.data.integer); + break; + case TYPE_STRING: + debug_print_int( + "Variable found: `%s` with value `%s`\n", variable_name, + current_env->variables[i].value.data.string); + break; + case TYPE_FUNCTION: + debug_print_int( + "Variable found: `%s` with function reference `%s`\n", + variable_name, + current_env->variables[i] + .value.data.function_ptr->name); + break; + default: + debug_print_int("Variable found: `%s` with unknown type.\n", + variable_name); + } + return ¤t_env->variables[i]; } - return &env->variables[i]; } + current_env = current_env->parent; } debug_print_int("Variable not found: `%s`\n", variable_name); return NULL; @@ -1167,25 +1188,11 @@ InterpretResult call_user_defined_function(Function *func_ref, Environment *env) { debug_print_int("Calling user-defined function: `%s`\n", func_ref->name); - // 1) Create a new local environment + // Create a new local environment with 'env' as its parent Environment local_env; - init_environment(&local_env); - - // 2) Copy global functions into the local environment - for (size_t i = 0; i < env->function_count; i++) { - Function *global_func = &env->functions[i]; - - // Deep copy the function - Function func_copy = { - .name = strdup(global_func->name), - .parameters = copy_function_parameters(global_func->parameters), - .body = copy_ast_node(global_func->body), - .is_builtin = global_func->is_builtin}; - - add_function(&local_env, func_copy); - } + init_environment_with_parent(&local_env, env); - // 3) Bind function parameters with arguments + // Bind function parameters with arguments ASTFunctionParameter *param = func_ref->parameters; ASTNode *arg = call_node->function_call.arguments; @@ -1193,7 +1200,7 @@ InterpretResult call_user_defined_function(Function *func_ref, InterpretResult arg_res = interpret_node(arg, env); if (arg_res.is_error) { free_environment(&local_env); - return arg_res; + return arg_res; // Propagate the error } LiteralValue arg_value = arg_res.value; @@ -1208,7 +1215,7 @@ InterpretResult call_user_defined_function(Function *func_ref, arg = arg->next; } - // 4) Check for argument count mismatch + // Check for argument count mismatch if (param || arg) { free_environment(&local_env); return raise_error( @@ -1216,7 +1223,7 @@ InterpretResult call_user_defined_function(Function *func_ref, func_ref->name); } - // 5) Interpret the function body + // Interpret the function body ASTNode *stmt = func_ref->body; InterpretResult func_res = make_result(create_default_value(), false, false); @@ -1241,7 +1248,7 @@ InterpretResult call_user_defined_function(Function *func_ref, free_environment(&local_env); - // If no explicit return, return default value (e.g., 0) + // If no explicit return, return default value (e.g., `0`) return func_res; } diff --git a/src/interpreter/interpreter_types.h b/src/interpreter/interpreter_types.h index 66b5ba9..d0ffa97 100644 --- a/src/interpreter/interpreter_types.h +++ b/src/interpreter/interpreter_types.h @@ -6,6 +6,9 @@ struct ASTFunctionParameter; struct ASTNode; struct Function; +// Forward declaration and type alias for Environment +typedef struct Environment Environment; + typedef enum { TYPE_BOOLEAN, TYPE_FLOAT, @@ -48,7 +51,7 @@ typedef struct Function { bool is_builtin; } Function; -typedef struct { +struct Environment { Variable *variables; size_t variable_count; size_t capacity; // to handle dynamic resizing @@ -56,7 +59,9 @@ typedef struct { Function *functions; // Array of functions size_t function_count; size_t function_capacity; -} Environment; + + Environment *parent; // Parent environment +}; typedef struct { LiteralValue value; // Result of interpreting a node diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index b0a7b25..93b96b4 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -63,8 +63,11 @@ void initialize_all_builtin_functions(Environment *env) { } } +// Function to initialize the environment without a parent (global) void init_environment(Environment *env) { - // Existing variable initialization + env->parent = NULL; + + // Initialize variables env->variable_count = 0; env->capacity = 10; env->variables = malloc(env->capacity * sizeof(Variable)); @@ -72,7 +75,7 @@ void init_environment(Environment *env) { fatal_error("Failed to allocate memory for variables.\n"); } - // Add function initialization + // Initialize functions env->function_count = 0; env->function_capacity = 10; env->functions = malloc(env->function_capacity * sizeof(Function)); @@ -80,27 +83,56 @@ void init_environment(Environment *env) { fatal_error("Failed to allocate memory for functions.\n"); } + // Initialize built-in functions ONLY for the GLOBAL environment initialize_all_builtin_functions(env); } +// Function to initialize the environment with a parent (local) +void init_environment_with_parent(Environment *env, Environment *parent) { + env->parent = parent; + + // Initialize variables + env->variable_count = 0; + env->capacity = 10; + env->variables = malloc(env->capacity * sizeof(Variable)); + if (!env->variables) { + fatal_error("Failed to allocate memory for variables.\n"); + } + + // Initialize functions + env->function_count = 0; + env->function_capacity = 10; + env->functions = malloc(env->function_capacity * sizeof(Function)); + if (!env->functions) { + fatal_error("Failed to allocate memory for functions.\n"); + } + + // Do NOT initialize built-in functions in local environments +} + +// Free the environment and its resources void free_environment(Environment *env) { // Free variables for (size_t i = 0; i < env->variable_count; i++) { free(env->variables[i].variable_name); + if (env->variables[i].value.type == TYPE_STRING) { + free(env->variables[i].value.data.string); + } } free(env->variables); - // Free functions with complete cleanup + // Free functions for (size_t i = 0; i < env->function_count; i++) { - // Free the parameters linked list + // Free function name + free(env->functions[i].name); + + // Free function parameters free_parameter_list(env->functions[i].parameters); - // Free the function body AST (only if they're NOT builtin) + // Free function body if user-defined if (!env->functions[i].is_builtin && env->functions[i].body) { free(env->functions[i].body); } - - free(env->functions[i].name); } free(env->functions); } diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h index 2c23344..29e4653 100644 --- a/src/interpreter/utils.h +++ b/src/interpreter/utils.h @@ -8,9 +8,12 @@ #include #include -// Initialize the environment +// Initialize the environment without a parent (global) void init_environment(Environment *env); +// Initialize the environment with a parent (local) +void init_environment_with_parent(Environment *env, Environment *parent); + // Free the environment void free_environment(Environment *env); From 01d15f0f265f2c4708c38a18258a866220c69833 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:39:53 +0000 Subject: [PATCH 58/62] Refactor: Function call handling for nested application support - Removed redundant fallback logic in `interpret_function_call()` to avoid environment conflicts during nested function calls. - Unified all user-defined function calls through `call_user_defined_function()`. - Ensured consistent environment chaining and parameter binding in `call_user_defined_function()`. - Improved error handling for argument mismatches and function calls. - Verified and streamlined function declarations and variable bindings to ensure accurate references in `interpret_function_declaration()`. #179 --- src/interpreter/interpreter.c | 104 ++++++++-------------------------- 1 file changed, 25 insertions(+), 79 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 20a959b..04e4f21 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -1209,7 +1209,11 @@ InterpretResult call_user_defined_function(Function *func_ref, Variable param_var = {.variable_name = strdup(param->parameter_name), .value = arg_value, .is_constant = false}; - add_variable(&local_env, param_var); + InterpretResult add_res = add_variable(&local_env, param_var); + if (add_res.is_error) { + free_environment(&local_env); + return add_res; + } param = param->next; arg = arg->next; @@ -1322,112 +1326,54 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { return raise_error("Invalid function call"); } - const char *func_name = strdup(node->function_call.name); + const char *func_name = node->function_call.name; - // 1. Check if the function name is a variable holding a function reference + // 1) Try looking up the function as a variable (user-defined function + // reference) Variable *func_var = get_variable(env, func_name); if (func_var && func_var->value.type == TYPE_FUNCTION) { Function *func_ref = func_var->value.data.function_ptr; - // Call the user-defined function - InterpretResult func_res = - call_user_defined_function(func_ref, node, env); - return func_res; + // Delegate to call_user_defined_function + return call_user_defined_function(func_ref, node, env); } - // 2. Else, check if it's a built-in or globally defined function + // 2) If not found as a variable, check if it's a built-in or in global + // env->functions Function *func = get_function(env, func_name); if (!func) { - // Function not found return raise_error("Undefined function `%s`\n", func_name); } - // If it’s a built-in => call the built-in logic + // 2a) If built-in, call the corresponding built-in function if (func->is_builtin) { - InterpretResult builtin_res = {0}; if (strcmp(func->name, "sample") == 0) { - builtin_res = builtin_input(node, env); + return builtin_input(node, env); } else if (strcmp(func->name, "serve") == 0) { - builtin_res = builtin_output(node, env); + return builtin_output(node, env); } else if (strcmp(func->name, "burn") == 0) { - builtin_res = builtin_error(node, env); + return builtin_error(node, env); } else if (strcmp(func->name, "random") == 0) { - builtin_res = builtin_random(node, env); + return builtin_random(node, env); } else if (strcmp(func->name, "string") == 0 || strcmp(func->name, "int") == 0 || strcmp(func->name, "float") == 0) { - builtin_res = builtin_cast(node, env); + return builtin_cast(node, env); } else if (strcmp(func->name, "get_time") == 0) { - builtin_res = builtin_time(); + return builtin_time(); } else if (strcmp(func->name, "taste_file") == 0) { - builtin_res = builtin_file_read(node, env); + return builtin_file_read(node, env); } else if (strcmp(func->name, "plate_file") == 0) { - builtin_res = builtin_file_write(node, env); + return builtin_file_write(node, env); } else if (strcmp(func->name, "garnish_file") == 0) { - builtin_res = builtin_file_append(node, env); + return builtin_file_append(node, env); } else { - // Unknown built-in return raise_error("Unknown built-in function `%s`\n", func->name); } - - return builtin_res; - } - - // Otherwise, user-defined function. - // Create local environment, bind parameters, interpret AST body, etc. - - Environment local_env; - init_environment(&local_env); - - // Copy parent's functions so calls to other user-defined functions work - for (size_t i = 0; i < env->function_count; i++) { - // NOTE: If the function is built-in or user-defined, - // copy them all anyway. - Function func_copy = {.name = strdup(env->functions[i].name), - .parameters = copy_function_parameters( - env->functions[i].parameters), - .body = copy_ast_node(env->functions[i].body), - .is_builtin = env->functions[i].is_builtin}; - add_function(&local_env, func_copy); - } - - // Now interpret arguments & bind them - ASTFunctionParameter *p = func->parameters; - ASTNode *arg = node->function_call.arguments; - while (p && arg) { - InterpretResult arg_res = interpret_node(arg, env); - LiteralValue arg_value = arg_res.value; - if (arg_value.type == TYPE_ERROR) { - free_environment(&local_env); - InterpretResult err_res = make_result(arg_value, false, false); - err_res.is_error = true; - return err_res; - } - - Variable param_var = {.variable_name = strdup(p->parameter_name), - .value = arg_value}; - add_variable(&local_env, param_var); - - p = p->next; - arg = arg->next; - } - - // interpret function body - ASTNode *stmt = func->body; - while (stmt) { - InterpretResult r = interpret_node(stmt, &local_env); - if (r.did_return) { - // Short-circuit - free_environment(&local_env); - return r; - } - // Else keep going - stmt = stmt->next; } - free_environment(&local_env); - - // If no explicit return, return default value (i.e., `0`) - return make_result(create_default_value(), false, false); + // 2b) If not built-in, it's a user-defined function in the global function + // array + return call_user_defined_function(func, node, env); } InterpretResult interpret_ternary(ASTNode *node, Environment *env) { From 3645b3edbfa3083b98942a971102c5b23edfd952 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:03:15 +0000 Subject: [PATCH 59/62] Fix: Variable scope handling in nested function assignments - Resolved an issue where variables were incorrectly scoped to local environments during nested function calls, causing global variables (e.g., `a`) to be undefined after assignment. - Updated `interpret_assignment` to traverse the parent environment chain for variable updates, ensuring global variables are properly stored. - Enhanced `add_variable` logic to distinguish between updating existing variables and creating new ones, addressing scope resolution issues in the interpreter. - Debugged and validated `serve()` function execution after fixing the variable assignment issue. - Test: `tests/17_nested_function_application.flv` #179 --- src/interpreter/builtins.c | 6 +- src/interpreter/interpreter.c | 126 ++++++++++++++++++++-------------- src/interpreter/utils.c | 13 +++- 3 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c index 35b04d8..a771a69 100644 --- a/src/interpreter/builtins.c +++ b/src/interpreter/builtins.c @@ -243,6 +243,8 @@ InterpretResult builtin_random(ASTNode *node, Environment *env) { // Built-in `serve()` function for printing InterpretResult builtin_output(ASTNode *node, Environment *env) { + debug_print_int("builtin_output() called\n"); + ASTNode *arg_node = node->function_call.arguments; while (arg_node != NULL) { InterpretResult r = interpret_node(arg_node, env); @@ -271,7 +273,7 @@ InterpretResult builtin_output(ASTNode *node, Environment *env) { "Error: Invalid literal type in `serve()` (`TYPE_ERROR`).\n"); break; default: - fprintf(stderr, "Error: Unknown literal type in s`erve()`.\n"); + fprintf(stderr, "Error: Unknown literal type in `serve()`.\n"); break; } printf(" "); // Space padding @@ -280,7 +282,7 @@ InterpretResult builtin_output(ASTNode *node, Environment *env) { printf("\n"); LiteralValue lv = {.type = TYPE_INTEGER, - .data.integer = 0}; // Return 0 as default + .data.integer = 0}; // return 0 as default return make_result(lv, false, false); } diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 04e4f21..7689f67 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -61,8 +61,15 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) { } case AST_FUNCTION_RETURN: { + // After assignment + debug_print_int("===Value stored in 'a': %lld\n", + get_variable(env, "a")); + InterpretResult return_res = interpret_node(node->function_return.return_data, env); + + // Before serve call + debug_print_int("===About to execute serve(a)\n"); if (return_res.is_error) { return return_res; } @@ -228,23 +235,35 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) { // Evaluate the right-hand side InterpretResult assign_r = interpret_node(node->assignment.value, env); - - // If the RHS triggered a return or break, propagate it if (assign_r.did_return || assign_r.did_break) { return assign_r; } - LiteralValue new_value = assign_r.value; + // Find the environment where the variable exists + Environment *target_env = env; + Variable *existing_var = NULL; + 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) { + existing_var = &target_env->variables[i]; + break; + } + } + if (existing_var) + break; + target_env = target_env->parent; + } + + // If variable doesn't exist anywhere, add to current scope + // If it exists, update in the scope where it was found + Environment *scope_to_modify = existing_var ? target_env : env; - // Add or update variable - Variable new_var = { - .variable_name = strdup(node->assignment.variable_name), - .value = new_value, - .is_constant = false // 'let' declarations are not constants - }; - add_variable(env, new_var); + Variable new_var = {.variable_name = strdup(node->assignment.variable_name), + .value = assign_r.value, + .is_constant = false}; - return make_result(new_value, false, false); + return add_variable(scope_to_modify, new_var); } InterpretResult handle_string_concatenation(InterpretResult left, @@ -1183,6 +1202,9 @@ InterpretResult interpret_switch(ASTNode *node, Environment *env) { return make_result(create_default_value(), false, false); } +/** + * Function to call a user-defined function + */ InterpretResult call_user_defined_function(Function *func_ref, ASTNode *call_node, Environment *env) { @@ -1260,7 +1282,7 @@ InterpretResult interpret_function_declaration(ASTNode *node, Environment *env) { debug_print_int("`interpret_function_declaration()` called\n"); - if (!node || !node->function_call.name) { + if (!node || !node->function_declaration.name) { fatal_error("Invalid function declaration\n"); } @@ -1306,7 +1328,6 @@ InterpretResult interpret_function_declaration(ASTNode *node, // Also add the function as a variable holding its reference LiteralValue func_ref = {.type = TYPE_FUNCTION, - // Point to the last added function .data.function_ptr = &env->functions[env->function_count - 1]}; @@ -1328,8 +1349,44 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { const char *func_name = node->function_call.name; - // 1) Try looking up the function as a variable (user-defined function - // reference) + // 1) Try looking up the function in the functions array (built-in or global + // user-defined) + Function *func = get_function(env, func_name); + if (func) { + if (func->is_builtin) { + // Handle built-in functions + if (strcmp(func->name, "sample") == 0) { + return builtin_input(node, env); + } else if (strcmp(func->name, "serve") == 0) { + return builtin_output(node, env); + } else if (strcmp(func->name, "burn") == 0) { + return builtin_error(node, env); + } else if (strcmp(func->name, "random") == 0) { + return builtin_random(node, env); + } else if (strcmp(func->name, "string") == 0 || + strcmp(func->name, "int") == 0 || + strcmp(func->name, "float") == 0) { + return builtin_cast(node, env); + } else if (strcmp(func->name, "get_time") == 0) { + return builtin_time(); + } else if (strcmp(func->name, "taste_file") == 0) { + return builtin_file_read(node, env); + } else if (strcmp(func->name, "plate_file") == 0) { + return builtin_file_write(node, env); + } else if (strcmp(func->name, "garnish_file") == 0) { + return builtin_file_append(node, env); + } else { + return raise_error("Unknown built-in function `%s`\n", + func->name); + } + } else { + // Handle user-defined functions in the functions array + return call_user_defined_function(func, node, env); + } + } + + // 2) If not found in functions array, check if it's a variable holding a + // function reference Variable *func_var = get_variable(env, func_name); if (func_var && func_var->value.type == TYPE_FUNCTION) { Function *func_ref = func_var->value.data.function_ptr; @@ -1337,43 +1394,8 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { return call_user_defined_function(func_ref, node, env); } - // 2) If not found as a variable, check if it's a built-in or in global - // env->functions - Function *func = get_function(env, func_name); - if (!func) { - return raise_error("Undefined function `%s`\n", func_name); - } - - // 2a) If built-in, call the corresponding built-in function - if (func->is_builtin) { - if (strcmp(func->name, "sample") == 0) { - return builtin_input(node, env); - } else if (strcmp(func->name, "serve") == 0) { - return builtin_output(node, env); - } else if (strcmp(func->name, "burn") == 0) { - return builtin_error(node, env); - } else if (strcmp(func->name, "random") == 0) { - return builtin_random(node, env); - } else if (strcmp(func->name, "string") == 0 || - strcmp(func->name, "int") == 0 || - strcmp(func->name, "float") == 0) { - return builtin_cast(node, env); - } else if (strcmp(func->name, "get_time") == 0) { - return builtin_time(); - } else if (strcmp(func->name, "taste_file") == 0) { - return builtin_file_read(node, env); - } else if (strcmp(func->name, "plate_file") == 0) { - return builtin_file_write(node, env); - } else if (strcmp(func->name, "garnish_file") == 0) { - return builtin_file_append(node, env); - } else { - return raise_error("Unknown built-in function `%s`\n", func->name); - } - } - - // 2b) If not built-in, it's a user-defined function in the global function - // array - return call_user_defined_function(func, node, env); + // 3) If not found in either, raise an error + return raise_error("Undefined function `%s`\n", func_name); } InterpretResult interpret_ternary(ASTNode *node, Environment *env) { diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c index 93b96b4..883ea4a 100644 --- a/src/interpreter/utils.c +++ b/src/interpreter/utils.c @@ -495,11 +495,18 @@ void add_function(Environment *env, Function func) { debug_print_int("Function `%s` added successfully.\n", stored_func->name); } +/** + * Function to retrieve a function by name, traversing the environment chain. + */ Function *get_function(Environment *env, const char *name) { - for (size_t i = 0; i < env->function_count; i++) { - if (strcmp(env->functions[i].name, name) == 0) { - return &env->functions[i]; + Environment *current_env = env; + while (current_env) { + for (size_t i = 0; i < current_env->function_count; i++) { + if (strcmp(current_env->functions[i].name, name) == 0) { + return ¤t_env->functions[i]; + } } + current_env = current_env->parent; } return NULL; } From 0cae3b386db46c0a68e2155e3819aa2395a3c8a2 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:10:37 +0000 Subject: [PATCH 60/62] Update interpreter.c - `tests/17_nested_function_application.flv` is still not function correctly due to `a` not being defined #179 #179 --- src/interpreter/interpreter.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 7689f67..444ea3e 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -235,10 +235,13 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) { // Evaluate the right-hand side InterpretResult assign_r = interpret_node(node->assignment.value, env); + // If the RHS triggered a return or break, propagate it if (assign_r.did_return || assign_r.did_break) { return assign_r; } + LiteralValue new_value = assign_r.value; + // Find the environment where the variable exists Environment *target_env = env; Variable *existing_var = NULL; @@ -250,8 +253,9 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) { break; } } - if (existing_var) + if (existing_var) { break; + } target_env = target_env->parent; } @@ -260,7 +264,7 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) { Environment *scope_to_modify = existing_var ? target_env : env; Variable new_var = {.variable_name = strdup(node->assignment.variable_name), - .value = assign_r.value, + .value = new_value, .is_constant = false}; return add_variable(scope_to_modify, new_var); @@ -1385,8 +1389,8 @@ InterpretResult interpret_function_call(ASTNode *node, Environment *env) { } } - // 2) If not found in functions array, check if it's a variable holding a - // function reference + // 2) If not found in functions array, check if it's a variable holding + // a function reference Variable *func_var = get_variable(env, func_name); if (func_var && func_var->value.type == TYPE_FUNCTION) { Function *func_ref = func_var->value.data.function_ptr; From 7fcd11b552cfcd76e2ce901c8ccfea2391cce1e7 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:11:02 +0000 Subject: [PATCH 61/62] Temp Fix: Using `const` works! #179 --- src/tests/17_nested_function_application.flv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/17_nested_function_application.flv b/src/tests/17_nested_function_application.flv index 171ac95..19fb74a 100644 --- a/src/tests/17_nested_function_application.flv +++ b/src/tests/17_nested_function_application.flv @@ -10,5 +10,5 @@ create test(num, func_a, func_b) { deliver func_b(func_a(func_b(num))); } -let a = test(10, times_2, times_3); # 180 +const a = test(10, times_2, times_3); # 180 serve(a); From 17204d92ee34c9f699fb99d03af35deca85a0b57 Mon Sep 17 00:00:00 2001 From: Kenny <70860732+KennyOliver@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:21:40 +0000 Subject: [PATCH 62/62] Improve: Function highlighting of extension & error handling #179 --- vscode-extension/package.json | 2 +- .../syntaxes/flavorlang.tmLanguage.json | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 8a7e50a..f768179 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -2,7 +2,7 @@ "name": "flavorlang-vscode", "displayName": "FlavorLang Support", "description": "Syntax highlighting for FlavorLang programming language.", - "version": "1.2.3", + "version": "1.3.0", "publisher": "KennyOliver", "repository": { "type": "git", diff --git a/vscode-extension/syntaxes/flavorlang.tmLanguage.json b/vscode-extension/syntaxes/flavorlang.tmLanguage.json index 5fb112e..9859a4a 100644 --- a/vscode-extension/syntaxes/flavorlang.tmLanguage.json +++ b/vscode-extension/syntaxes/flavorlang.tmLanguage.json @@ -8,7 +8,9 @@ { "include": "#functions" }, { "include": "#numbers" }, { "include": "#operators" }, - { "include": "#variables" } + { "include": "#variables" }, + { "include": "#ranges" }, + { "include": "#error-handling" } ], "repository": { "comments": { @@ -38,7 +40,7 @@ "patterns": [ { "name": "keyword.control.flavorlang", - "match": "\\b(let|const|if|elif|else|for|in|while|create|burn|deliver|check|is|rescue|try|break|continue)\\b" + "match": "\\b(let|const|if|elif|else|for|in|while|create|burn|deliver|check|is|rescue|try|rescue|finish|break|continue)\\b" }, { "name": "keyword.other.flavorlang", @@ -49,8 +51,12 @@ "functions": { "patterns": [ { - "name": "entity.name.function.flavorlang", - "match": "\\b(serve|burn|deliver|sample|plate_file|garnish_file|taste_file|create|check|rescue|string|int|float|get_time)\\b(?=\\()" + "name": "entity.name.function.create.flavorlang", + "match": "\\bcreate\\b\\s+[a-zA-Z_][a-zA-Z0-9_]*\\b" + }, + { + "name": "entity.name.function.call.flavorlang", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b(?=\\()" } ] }, @@ -58,11 +64,11 @@ "patterns": [ { "name": "constant.numeric.integer.flavorlang", - "match": "\\b\\d+\\b" + "match": "\\b-?\\d+\\b" }, { "name": "constant.numeric.float.flavorlang", - "match": "\\b\\d+\\.\\d+\\b" + "match": "\\b-?\\d+\\.\\d+\\b" } ] }, @@ -85,6 +91,22 @@ "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b" } ] + }, + "ranges": { + "patterns": [ + { + "name": "meta.range.flavorlang", + "match": "\\b\\d+\\.\\.=?\\d+\\b" + } + ] + }, + "error-handling": { + "patterns": [ + { + "name": "keyword.control.error-handling.flavorlang", + "match": "\\b(burn|rescue|try|deliver)\\b" + } + ] } }, "scopeName": "source.flavorlang"