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.
---
diff --git a/docs/language_design.md b/docs/language_design.md
index 9b8c10e..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. | ❌ |
-| `crumbs` | Catch block | Handles errors during execution. | ❌ |
-| `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. | ❌ |
+| 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. |
---
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
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
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/builtins.c b/src/interpreter/builtins.c
index fa26198..a771a69 100644
--- a/src/interpreter/builtins.c
+++ b/src/interpreter/builtins.c
@@ -5,27 +5,22 @@
#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
-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 +37,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 +50,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 +58,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 +71,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 +122,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 +131,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 +145,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,28 +161,56 @@ 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);
+ debug_print_int("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) {
- FLOAT_SIZE min = 0.0L;
- FLOAT_SIZE max = 1.0L;
+InterpretResult builtin_random(ASTNode *node, Environment *env) {
+ 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;
-
- if (!interpret_arguments(node->function_call.arguments, env, 2, specs)) {
- // Return an error type on failure
- return (LiteralValue){.type = TYPE_ERROR};
+ ASTNode *arg_node = node->function_call.arguments;
+
+ 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
@@ -201,28 +220,31 @@ LiteralValue 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);
- 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;
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) {
+ 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);
@@ -246,10 +268,12 @@ LiteralValue 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");
+ fprintf(stderr, "Error: Unknown literal type in `serve()`.\n");
break;
}
printf(" "); // Space padding
@@ -257,12 +281,13 @@ 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
-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 +323,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,190 +336,157 @@ LiteralValue builtin_error(ASTNode *node, Environment *env) {
arg_node = arg_node->next;
}
- error_interpreter(error_message);
-
- return (LiteralValue){.type = TYPE_ERROR}; // keep compiler happy
-}
-
-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;
+ // Propagate the exception
+ return raise_error("%s", error_message);
}
-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;
+ char *cast_type = strdup(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() {
+InterpretResult builtin_time(void) {
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) {
@@ -544,21 +536,25 @@ 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};
+ 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);
}
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;
@@ -569,7 +565,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
@@ -587,7 +584,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;
@@ -602,10 +600,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;
@@ -616,40 +616,47 @@ 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};
+ 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);
}
// 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 84bbd06..ae85290 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,19 +22,19 @@ 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);
-LiteralValue builtin_error(ASTNode *node, Environment *env);
-LiteralValue 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_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);
+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);
// 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);
diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c
index 3580d25..444ea3e 100644
--- a/src/interpreter/interpreter.c
+++ b/src/interpreter/interpreter.c
@@ -1,149 +1,155 @@
#include "interpreter.h"
// Helper function to create a default LiteralValue (zero number)
-LiteralValue create_default_value() {
- LiteralValue value = {
- .type = TYPE_INTEGER,
- .data = {
- // .floating_point = 0.0,
- .integer = 0, // main value used for comparisons, etc
- // .string = "",
- }};
+LiteralValue create_default_value(void) {
+ LiteralValue value;
+ memset(&value, 0, sizeof(value));
+ value.type = TYPE_INTEGER;
+ value.data.integer = 0;
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) {
- 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 = interpret_literal(node);
+ 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);
+ // After assignment
+ debug_print_int("===Value stored in 'a': %lld\n",
+ get_variable(env, "a"));
- // Return this value, but also set did_return = true
- return make_result(return_value, true, false);
+ 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;
+ }
+ 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);
+ // Replace LiteralValue with InterpretResult
+ InterpretResult var_res = interpret_variable(node, env);
+ result = var_res;
+ 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
+ return raise_error("Unsupported `ASTNode` type.\n");
}
+
+ 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 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;
}
}
-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);
@@ -173,45 +179,48 @@ 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;
+
+ return make_result(value, false, false);
}
-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
- 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;
+ 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);
@@ -226,7 +235,6 @@ 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;
@@ -234,345 +242,483 @@ InterpretResult interpret_assignment(ASTNode *node, Environment *env) {
LiteralValue new_value = assign_r.value;
- // 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);
+ // 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;
+
+ Variable new_var = {.variable_name = strdup(node->assignment.variable_name),
+ .value = new_value,
+ .is_constant = false};
- return make_result(new_value, false, false);
+ return add_variable(scope_to_modify, new_var);
}
-LiteralValue handle_string_concatenation(LiteralValue left,
- LiteralValue right) {
- LiteralValue result;
- result.type = TYPE_STRING;
+InterpretResult handle_string_concatenation(InterpretResult left,
+ InterpretResult right) {
+ LiteralValue lv_result;
+ lv_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) {
- error_interpreter(
+ return raise_error(
"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 result;
+ 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) {
- error_interpreter(
- "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 {
- error_interpreter(
- "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
+ LiteralValue left = left_res.value;
+ LiteralValue right = right_res.value;
- error_interpreter("Unknown unary operator `%s`\n", op);
- return result; // unreachable
-}
-
-LiteralValue interpret_unary_op(ASTNode *node, Environment *env) {
- if (!node || node->type != AST_UNARY_OP) {
- error_interpreter("Invalid unary operation node.\n");
+ // 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);
- 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;
+
+ LiteralValue result;
+ memset(&result, 0, sizeof(LiteralValue));
- // If interpreting the operand caused a return, propagate it
- if (operand_r.did_return) {
- return operand;
+ // 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 (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);
}
- // Evaluate the unary operator
- return evaluate_unary_operator(op, operand);
+ return make_result(result, false, false);
}
-LiteralValue evaluate_operator(const char *op, LiteralValue left,
- LiteralValue right) {
+// Function to evaluate binary operators
+InterpretResult evaluate_operator(const char *op, InterpretResult left_res,
+ InterpretResult right_res) {
debug_print_int("Operator: `%s`\n", op);
- 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);
+ 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 (left.type != TYPE_BOOLEAN || right.type != TYPE_BOOLEAN) {
- error_interpreter(
- "Logical operator `%s` requires boolean operands.\n", op);
+ 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 result;
- }
-
- // Get numeric values for arithmetic and comparison
- FLOAT_SIZE 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;
- } else {
- left_value = (FLOAT_SIZE)left.data.integer;
- right_value = (FLOAT_SIZE)right.data.integer;
+ return make_result(result, false, false);
}
- // 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 Equality Operators `==` and `!=` Across All Types
+ if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0) {
+ bool comparison_result = false;
- // 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) {
- error_interpreter("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) {
- error_interpreter("Floor division by zero\n");
+ // 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");
+ }
}
- 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) {
- error_interpreter("Modulo by zero\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");
+ }
}
- 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);
+
+ // Apply `!=` logic if operator is "!="
+ if (strcmp(op, "!=") == 0) {
+ comparison_result = !comparison_result;
}
- } 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) {
+
+ // Set the result
+ LiteralValue result;
result.type = TYPE_BOOLEAN;
- result.data.boolean = (left_value != right_value);
- } else {
- error_interpreter("Unknown operator `%s`\n", op);
+ result.data.boolean = comparison_result;
+
+ return make_result(result, false, false);
}
- return result;
-}
+ // 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]);
-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;
-}
+ 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;
+ }
+ }
+
+ if (is_numeric_op) {
+ return handle_numeric_operator(op, left_res, right_res);
+ }
-int is_right_associative(const char *op) {
- return (strcmp(op, "**") == 0); // exponentiation is right-associative
+ 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);
}
-LiteralValue interpret_binary_op(ASTNode *node, Environment *env) {
- if (!node || node->type != AST_BINARY_OP) {
- error_interpreter("Invalid binary operation node.");
+InterpretResult interpret_binary_op(ASTNode *node, Environment *env) {
+ 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);
- LiteralValue left = left_r.value;
- if (left.type == TYPE_ERROR) {
- return left; // propagate errors
+ 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);
- LiteralValue right = right_r.value;
- if (right.type == TYPE_ERROR) {
- return right; // propagate errors
+ 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);
+}
+
+// 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");
+ }
- // 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);
+ // Interpret the operand
+ InterpretResult operand_res = interpret_node(node->unary_op.operand, env);
+ if (operand_res.is_error) {
+ return operand_res;
+ }
- // 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);
+ // 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;
+ }
+
+ 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 evaluate_operator(op, left, right);
+ 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++) {
- 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;
}
-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
@@ -583,29 +729,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
@@ -613,6 +761,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 =
@@ -623,59 +776,71 @@ 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) {
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;
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 +856,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 +874,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;
@@ -770,14 +937,17 @@ 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");
}
// 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;
@@ -786,47 +956,53 @@ 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) {
+ free(loop_var);
+ 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) {
+ 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 {
- error_interpreter("Start expression in `for` loop must be numeric\n");
+ 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 {
- error_interpreter("End expression in `for` loop must be numeric\n");
+ free(loop_var);
+ 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) {
+ 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 {
- error_interpreter(
+ free(loop_var);
+ return raise_error(
"Step expression in `for` loop must be numeric\n");
}
} else {
@@ -840,76 +1016,88 @@ 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");
+ 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)) {
- error_interpreter("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) {
- // Variable does not exist; create it
- var = allocate_variable(env, loop_var);
- 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;
- }
+ free(loop_var);
+ return raise_error("Failed to allocate and retrieve variable `%s`.\n",
+ loop_var);
+ }
+
+ 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 {
- error_interpreter("Loop variable `%s` must be numeric\n", loop_var);
+ free(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) {
+ // Check if the loop should continue
+ 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) {
+ free(loop_var);
+ return body_res;
}
- current_stmt = current_stmt->next;
+ 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
@@ -920,8 +1108,11 @@ LiteralValue interpret_for_loop(ASTNode *node, Environment *env) {
}
}
+ // Clean up
+ free(loop_var);
+
// Loops do not return values
- return create_default_value();
+ return make_result(create_default_value(), false, false);
}
InterpretResult interpret_switch(ASTNode *node, Environment *env) {
@@ -1015,90 +1206,88 @@ 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) {
+/**
+ * Function to call a user-defined function
+ */
+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
+ // Create a new local environment with 'env' as its parent
Environment local_env;
- init_environment(&local_env);
+ init_environment_with_parent(&local_env, 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];
-
- // 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);
- }
-
- // 3) Bind function parameters with arguments
+ // Bind function parameters with arguments
ASTFunctionParameter *param = func_ref->parameters;
ASTNode *arg = call_node->function_call.arguments;
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; // Propagate the error
}
+ 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,
.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;
}
- // 4) Check for argument count mismatch
+ // 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
+ // 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;
}
free_environment(&local_env);
- // If no explicit return, return default value (e.g., 0)
- return result;
+ // If no explicit return, return default value (e.g., `0`)
+ 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) {
- error_interpreter("Invalid function declaration\n");
+ if (!node || !node->function_declaration.name) {
+ fatal_error("Invalid function declaration\n");
}
// Initialize param_list as NULL
@@ -1106,18 +1295,18 @@ void interpret_function_declaration(ASTNode *node, Environment *env) {
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) {
- error_interpreter(
+ 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);
- error_interpreter("Memory allocation failed for parameter name\n");
+ fatal_error("Memory allocation failed for parameter name\n");
}
new_param->next = NULL;
@@ -1134,16 +1323,15 @@ void interpret_function_declaration(ASTNode *node, Environment *env) {
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);
// 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]};
@@ -1152,126 +1340,75 @@ void interpret_function_declaration(ASTNode *node, Environment *env) {
.is_constant = false};
add_variable(env, var);
+
+ return make_result(create_default_value(), false, false);
}
-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;
- // 1. Check if the function name is 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;
- // Call the function using the helper
- return call_user_defined_function(func_ref, node, env);
- }
-
- // 2. Else, check if it's a built-in or globally defined function
+ // 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) {
- // Function not found
- fprintf(stderr, "Error: Undefined function `%s`\n", func_name);
- exit(1);
- return (LiteralValue){.type = TYPE_ERROR};
- }
-
- // If it’s a built-in => call the built-in logic
- 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);
- }
-
- // If no recognized built-in, error
- fprintf(stderr, "Error: Unknown built-in `%s`\n", func->name);
- return (LiteralValue){.type = TYPE_ERROR};
- }
-
- // 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);
- return arg_value;
+ 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);
}
-
- 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
- LiteralValue result = create_default_value();
- 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.value;
- }
- // Else keep going
- stmt = stmt->next;
+ // 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;
+ // Delegate to call_user_defined_function
+ return call_user_defined_function(func_ref, node, env);
}
- free_environment(&local_env);
- return result; // if no explicit return => return `0` (or default)
+ // 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) {
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;
}
@@ -1281,7 +1418,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) {
@@ -1293,3 +1430,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 = make_result(create_default_value(), false, false);
+ 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..f11e16d 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -14,29 +14,33 @@
#include
InterpretResult interpret_node(ASTNode *node, Environment *env);
-LiteralValue interpret_literal(ASTNode *node);
-LiteralValue interpret_variable(ASTNode *node, Environment *env);
+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);
-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_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);
-LiteralValue evaluate_unary_operator(const char *op, LiteralValue operand);
+InterpretResult interpret_function_declaration(ASTNode *node, Environment *env);
+InterpretResult interpret_function_call(ASTNode *node, Environment *env);
+InterpretResult interpret_unary_op(ASTNode *node, Environment *env);
+InterpretResult evaluate_unary_operator(const char *op,
+ InterpretResult operand_res);
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);
+InterpretResult interpret_try(ASTNode *node, Environment *env);
// Interpret program
void interpret_program(ASTNode *program, Environment *env);
// Helpers
+LiteralValue create_default_value(void);
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
diff --git a/src/interpreter/interpreter_types.h b/src/interpreter/interpreter_types.h
index 99cf9ea..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,12 +59,15 @@ typedef struct {
Function *functions; // Array of functions
size_t function_count;
size_t function_capacity;
-} Environment;
+
+ Environment *parent; // Parent 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
diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c
index 915c6a8..883ea4a 100644
--- a/src/interpreter/utils.c
+++ b/src/interpreter/utils.c
@@ -1,16 +1,44 @@
#include "utils.h"
+#include
-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);
- printf("\033[0m\n"); // reset text color
+ // Format the error message into the buffer
+ vsnprintf(error_message, sizeof(error_message), format, args);
+ 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;
+ 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 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);
- fflush(stdout);
exit(1);
}
@@ -35,36 +63,76 @@ 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));
if (!env->variables) {
- error_interpreter("Failed to allocate memory for variables.\n");
+ 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));
if (!env->functions) {
- error_interpreter("Failed to allocate memory for functions.\n");
+ 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
for (size_t i = 0; i < env->function_count; i++) {
+ // Free function name
free(env->functions[i].name);
+
+ // Free function parameters
+ free_parameter_list(env->functions[i].parameters);
+
+ // Free function body if user-defined
+ if (!env->functions[i].is_builtin && env->functions[i].body) {
+ free(env->functions[i].body);
+ }
}
free(env->functions);
}
@@ -88,26 +156,30 @@ ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params) {
ASTFunctionParameter *tail = NULL;
while (params) {
- ASTFunctionParameter *new_param = malloc(sizeof(ASTFunctionParameter));
- if (!new_param) {
- error_interpreter(
- "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);
- error_interpreter(
- "Memory allocation failed for parameter name copy.\n");
+ // Duplicate the parameter name
+ 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;
}
- 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;
@@ -117,29 +189,261 @@ 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) {
- 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));
-
- // Deep copy for fields like `function_call`, `body`, or `arguments`
- if (node->function_call.arguments) {
+ // Initialize the new node to zero to prevent dangling pointers
+ memset(new_node, 0, sizeof(ASTNode));
+
+ // Copy the node type
+ new_node->type = node->type;
+
+ // Deep copy based on node type
+ switch (node->type) {
+ case AST_ASSIGNMENT:
+ if (node->assignment.variable_name) {
+ new_node->assignment.variable_name =
+ strdup(node->assignment.variable_name);
+ if (!new_node->assignment.variable_name) {
+ fatal_error("Memory allocation failed for variable name in "
+ "assignment.\n");
+ }
+ } else {
+ new_node->assignment.variable_name = NULL;
+ }
+ new_node->assignment.value = copy_ast_node(node->assignment.value);
+ break;
+
+ case AST_FUNCTION_DECLARATION:
+ 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;
+ }
+ new_node->function_declaration.parameters =
+ copy_function_parameters(node->function_declaration.parameters);
+ new_node->function_declaration.body =
+ copy_ast_node(node->function_declaration.body);
+ break;
+
+ case AST_FUNCTION_CALL:
+ 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;
+ }
new_node->function_call.arguments =
copy_ast_node(node->function_call.arguments);
+ break;
+
+ case AST_FUNCTION_RETURN:
+ new_node->function_return.return_data =
+ copy_ast_node(node->function_return.return_data);
+ break;
+
+ case AST_LITERAL:
+ new_node->literal.type = node->literal.type;
+ switch (node->literal.type) {
+ case LITERAL_STRING:
+ 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:
+ 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:
+ 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:
+ 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;
+ }
+ new_node->unary_op.operand = copy_ast_node(node->unary_op.operand);
+ break;
+
+ case AST_BINARY_OP:
+ 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;
+ }
+ 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:
+ 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:
+ 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;
+ }
+ 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;
+ 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:
+ new_node->switch_case.expression =
+ copy_ast_node(node->switch_case.expression);
+ 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");
+ }
+ 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 fields to copy
+ break;
+
+ case AST_TERNARY:
+ 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_TRY:
+ new_node->try_block.try_block =
+ copy_ast_node(node->try_block.try_block);
+ new_node->try_block.catch_blocks =
+ copy_catch_node(node->try_block.catch_blocks);
+ 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");
}
- 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
+ new_node->next = copy_ast_node(node->next);
+
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++) {
@@ -155,7 +459,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;
}
@@ -166,14 +470,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
@@ -185,17 +489,83 @@ 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);
}
+/**
+ * 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;
}
+
+// A helper to wrap `LiteralValue` in `InterpretResult`
+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;
+ 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 4c3dfe0..29e4653 100644
--- a/src/interpreter/utils.h
+++ b/src/interpreter/utils.h
@@ -8,14 +8,20 @@
#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);
-void error_interpreter(const char *format, ...);
+// 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,4 +29,17 @@ 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);
+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
diff --git a/src/lexer/keywords.c b/src/lexer/keywords.c
index 9788b62..d5c4fc7 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
@@ -76,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..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");
@@ -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.c b/src/parser/operator_parser.c
index 9608402..bebc3d2 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);
@@ -362,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;
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
diff --git a/src/parser/parser.c b/src/parser/parser.c
index 3b876da..9e94950 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) {
@@ -292,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_return.return_data = parse_expression(state);
node->next = NULL;
expect_token(state, TOKEN_DELIMITER,
@@ -749,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
@@ -763,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,
@@ -779,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;
@@ -820,3 +820,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/parser/utils.c b/src/parser/utils.c
index 610d639..183e1e5 100644
--- a/src/parser/utils.c
+++ b/src/parser/utils.c
@@ -53,11 +53,30 @@ void free_ast(ASTNode *node) {
break;
case AST_FUNCTION_DECLARATION:
+ free(node->function_declaration.name);
+ 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;
+ free(param->parameter_name);
+ free(param);
+ param = next;
+ }
+
+ break;
+
case AST_FUNCTION_CALL:
free(node->function_call.name);
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;
@@ -68,7 +87,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 +114,46 @@ void free_ast(ASTNode *node) {
}
break;
+ }
+
+ case AST_TRY:
+ // Free try block
+ if (node->try_block.try_block) {
+ free_ast(node->try_block.try_block);
+ }
+
+ // 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;
default:
fprintf(stderr,
@@ -135,12 +194,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);
@@ -151,10 +212,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");
@@ -176,8 +237,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 e4593fc..10f616d 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_CATCH,
+ AST_FINALLY
} ASTNodeType;
// Literal Node
@@ -100,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;
@@ -117,6 +128,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 +152,21 @@ 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
+ ASTTernary ternary;
+ ASTFunctionDeclaration function_declaration;
ASTFunctionCall function_call;
+ ASTFunctionReturn function_return;
- // Ternary
- ASTTernary ternary;
+ ASTTry try_block; // Try Block
+ // ASTCatchNode *catch_block; // Rescue Block
+ // struct ASTNode *finally_block; // Finish Block
// Variable
char *variable_name;
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);
diff --git a/src/tests/7_try_catch.flv b/src/tests/7_try_catch.flv
index 7d88e9f..705cd5b 100644
--- a/src/tests/7_try_catch.flv
+++ b/src/tests/7_try_catch.flv
@@ -1,6 +1,16 @@
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("(Expect a) Runtime error: Can't cast string `abc` to int.");
+} finish {
+ serve("This always executes!");
+}
diff --git a/src/tests/all.flv b/src/tests/all.flv
index c02683c..6e20ffb 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("(Expect a) Runtime error: Can't cast string `abc` to int.");
+} finish {
+ serve("This always executes!");
+}
# ==================================================
@@ -188,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
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"