diff --git a/docs/language_design.md b/docs/language_design.md
index a8363e3..a47f54c 100644
--- a/docs/language_design.md
+++ b/docs/language_design.md
@@ -10,7 +10,9 @@ This `docs/` page details the core design of FlavorLang's syntax, the various da
2. [Data Types](#data-types)
-3. [Extended Backus-Naur Form (EBNF)](#ebnf)
+3. [Arrays](#arrays)
+
+4. [Extended Backus-Naur Form (EBNF)](#ebnf)
---
@@ -73,6 +75,32 @@ This `docs/` page details the core design of FlavorLang's syntax, the various da
---
+## Arrays
+
+> [!Note]
+>
+> All array operations do not mutate original array to reduce confusion.
+
+| **Operation** | **Syntax** | **Explanation** |
+| ------------------------ | --------------- | ----------------------------------------------------------------------------------- |
+| **Element Access** | `array[5]` | Accesses element at index `5`. |
+| **Append** | `array[^+]` | Appends an element to end. |
+| **Prepend** | `array[+^]` | Prepends an element to beginning. |
+| **Remove Last Element** | `array[^-]` | Removes last element. |
+| **Remove First Element** | `array[-^]` | Removes first element. |
+| **Slicing** | `array[0:5]` | Slices from index `0` (incl.) to `5` (excl.). |
+| | `array[::-1]` | Reverses using step of `-1`. |
+| | `array[3:]` | Slices from index `3` (incl.) to end. |
+| | `array[:8]` | Slices from start to index `8` (excl.). |
+| | `array[3:8:2]` | Slices from index `3` (incl.) to `8` (excl.), skipping every 2nd element. |
+| | `array[8:3:-2]` | Slices from index `8` (incl.) to `3` (excl.), skipping every 2nd element (reverse). |
+| | `array[3::-1]` | Slices from index `3` (incl.) to start (reversed). |
+| | `array[:8:-1]` | Slices from end towards index `8` (excl., reversed). |
+| | `array[::2]` | Slices entire array, skipping every 2nd element. |
+| | `array[::-3]` | Reverses entire array, skipping every 3rd element. |
+
+---
+
## Extended Backus-Naur Form (EBNF) of FlavorLang's Syntax
```ebnf
diff --git a/src/interpreter/builtins.c b/src/interpreter/builtins.c
index a771a69..1aa4d3b 100644
--- a/src/interpreter/builtins.c
+++ b/src/interpreter/builtins.c
@@ -241,6 +241,45 @@ InterpretResult builtin_random(ASTNode *node, Environment *env) {
return make_result(result, false, false);
}
+void print_literal_value(LiteralValue lv) {
+ switch (lv.type) {
+ case TYPE_FLOAT:
+ if ((INT_SIZE)lv.data.floating_point == lv.data.floating_point) {
+ printf("%.1Lf", lv.data.floating_point);
+ } else {
+ printf("%Lf", lv.data.floating_point);
+ }
+ break;
+ case TYPE_INTEGER:
+ printf("%lld", lv.data.integer);
+ break;
+ case TYPE_STRING:
+ printf("\"%s\"", lv.data.string);
+ break;
+ case TYPE_BOOLEAN:
+ printf("%s", lv.data.boolean ? "True" : "False");
+ break;
+ case TYPE_ARRAY:
+ printf("[");
+ for (size_t i = 0; i < lv.data.array.count; i++) {
+ print_literal_value(lv.data.array.elements[i]);
+ if (i < lv.data.array.count - 1) {
+ printf(", ");
+ }
+ }
+ printf("]");
+ break;
+ case TYPE_FUNCTION:
+ printf("", lv.data.function_ptr->name);
+ break;
+ case TYPE_ERROR:
+ printf("");
+ break;
+ default:
+ printf("");
+ }
+}
+
// Built-in `serve()` function for printing
InterpretResult builtin_output(ASTNode *node, Environment *env) {
debug_print_int("builtin_output() called\n");
@@ -248,6 +287,10 @@ InterpretResult builtin_output(ASTNode *node, Environment *env) {
ASTNode *arg_node = node->function_call.arguments;
while (arg_node != NULL) {
InterpretResult r = interpret_node(arg_node, env);
+ if (r.is_error) {
+ return r; // propagate error
+ }
+
LiteralValue lv = r.value;
switch (lv.type) {
@@ -267,6 +310,18 @@ InterpretResult builtin_output(ASTNode *node, Environment *env) {
case TYPE_BOOLEAN:
printf("%s", lv.data.boolean ? "True" : "False");
break;
+ case TYPE_ARRAY:
+ printf("[");
+ for (size_t i = 0; i < lv.data.array.count; i++) {
+ LiteralValue elem = lv.data.array.elements[i];
+ print_literal_value(elem);
+
+ if (i < lv.data.array.count - 1) {
+ printf(", ");
+ }
+ }
+ printf("]");
+ break;
case TYPE_ERROR:
fprintf(
stderr,
diff --git a/src/interpreter/builtins.h b/src/interpreter/builtins.h
index ae85290..2468bef 100644
--- a/src/interpreter/builtins.h
+++ b/src/interpreter/builtins.h
@@ -39,5 +39,6 @@ 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);
char *process_escape_sequences(const char *input);
+void print_literal_value(LiteralValue lv);
#endif
diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c
index 0426b93..a78bad5 100644
--- a/src/interpreter/interpreter.c
+++ b/src/interpreter/interpreter.c
@@ -24,11 +24,27 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) {
result = interpret_literal(node);
break;
+ case AST_VAR_DECLARATION: {
+ debug_print_int("\tMatched: `AST_VAR_DECLARATION`\n");
+ result = interpret_var_declaration(node, env);
+ break;
+ }
+
+ case AST_CONST_DECLARATION: {
+ debug_print_int("\tMatched: `AST_CONST_DECLARATION`\n");
+ result = interpret_const_declaration(node, env);
+ break;
+ }
+
case AST_ASSIGNMENT:
debug_print_int("\tMatched: `AST_ASSIGNMENT`\n");
result = interpret_assignment(node, env);
break;
+ case AST_VARIABLE_REFERENCE:
+ result = interpret_variable_reference(node, env);
+ break;
+
case AST_UNARY_OP:
debug_print_int("\tMatched: `AST_UNARY_OP`\n");
result = interpret_unary_op(node, env);
@@ -108,19 +124,6 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) {
break;
}
- case AST_VAR_DECLARATION: {
- debug_print_int("\tMatched: `AST_VAR_DECLARATION`\n");
- // Replace LiteralValue with InterpretResult
- result = interpret_variable(node, env);
- break;
- }
-
- case AST_CONST_DECLARATION: {
- debug_print_int("\tMatched: `AST_CONST_DECLARATION`\n");
- result = interpret_constant(node, env);
- break;
- }
-
case AST_SWITCH: {
debug_print_int("\tMatched: `AST_SWITCH`\n");
result = interpret_switch(node, env);
@@ -142,6 +145,26 @@ InterpretResult interpret_node(ASTNode *node, Environment *env) {
result = interpret_try(node, env);
break;
+ case AST_ARRAY_LITERAL:
+ debug_print_int("\tMatched: `AST_ARRAY_LITERAL`\n");
+ result = interpret_array_literal(node, env);
+ break;
+
+ case AST_ARRAY_OPERATION:
+ debug_print_int("\tMatched: `AST_ARRAY_OPERATION`\n");
+ result = interpret_array_operation(node, env);
+ break;
+
+ case AST_ARRAY_INDEX_ACCESS:
+ debug_print_int("\tMatched: `AST_ARRAY_INDEX_ACCESS`\n");
+ result = interpret_array_index_access(node, env);
+ break;
+
+ case AST_ARRAY_SLICE_ACCESS:
+ debug_print_int("\tMatched: `AST_ARRAY_SLICE_ACCESS`\n");
+ result = interpret_array_slice_access(node, env);
+ break;
+
default:
return raise_error("Unsupported `ASTNode` type.\n");
}
@@ -201,7 +224,11 @@ InterpretResult interpret_literal(ASTNode *node) {
return make_result(value, false, false);
}
-InterpretResult interpret_variable(ASTNode *node, Environment *env) {
+InterpretResult interpret_variable_reference(ASTNode *node, Environment *env) {
+ if (node->type != AST_VARIABLE_REFERENCE) {
+ return raise_error("Expected AST_VARIABLE_REFERENCE node.\n");
+ }
+
Variable *var = get_variable(env, node->variable_name);
if (!var) {
return raise_error("Undefined variable `%s`.\n", node->variable_name);
@@ -210,90 +237,133 @@ InterpretResult interpret_variable(ASTNode *node, Environment *env) {
return make_result(var->value, false, false);
}
-InterpretResult interpret_constant(ASTNode *node, Environment *env) {
- if (node->type != AST_CONST_DECLARATION) {
- return raise_error("Invalid node type for constant declaration.\n");
+InterpretResult interpret_var_declaration(ASTNode *node, Environment *env) {
+ if (node->type != AST_VAR_DECLARATION) {
+ return raise_error("Invalid node type for variable declaration.\n");
}
- // Extract the constant name and value
- char *const_name = strdup(node->assignment.variable_name);
- InterpretResult value_res = interpret_node(node->assignment.value, env);
- LiteralValue const_value = value_res.value;
+ char *var_name = node->var_declaration.variable_name;
+ ASTNode *init_expr = node->var_declaration.initializer;
- if (value_res.is_error) {
- return value_res;
+ // Evaluate the initializer expression
+ InterpretResult init_val_res = interpret_node(init_expr, env);
+ if (init_val_res.is_error) {
+ return init_val_res; // propagate the error
+ }
+
+ // If that variable already exists in Environment, possibly handle
+ // re-declaration For now, just add a brand-new variable
+ Variable new_var;
+ new_var.variable_name = strdup(var_name);
+ new_var.value = init_val_res.value;
+ new_var.is_constant = false;
+
+ // Add the variable to Environment
+ InterpretResult add_res = add_variable(env, new_var);
+ if (add_res.is_error) {
+ return add_res;
+ }
+
+ return make_result(init_val_res.value, false, false);
+}
+
+InterpretResult interpret_const_declaration(ASTNode *node, Environment *env) {
+ if (node->type != AST_CONST_DECLARATION) {
+ return raise_error("Invalid node type for constant declaration.\n");
}
- // Check if the constant already exists
- Variable *existing_var = get_variable(env, const_name);
- if (existing_var) {
- return raise_error("Constant `%s` is already defined.\n", const_name);
+ char *const_name = node->const_declaration.constant_name;
+ ASTNode *init_expr = node->const_declaration.initializer;
+
+ // Evaluate initializer
+ InterpretResult init_val_res = interpret_node(init_expr, env);
+ if (init_val_res.is_error) {
+ return init_val_res;
}
// Add the constant to the environment
- Variable new_var = {.variable_name = strdup(const_name),
- .value = const_value,
- .is_constant = true};
- add_variable(env, new_var);
+ Variable new_var;
+ new_var.variable_name = strdup(const_name);
+ new_var.value = init_val_res.value;
+ new_var.is_constant = true;
+
+ InterpretResult add_res = add_variable(env, new_var);
+ if (add_res.is_error) {
+ return add_res;
+ }
- return make_result(const_value, false, false);
+ return make_result(init_val_res.value, false, false);
}
InterpretResult interpret_assignment(ASTNode *node, Environment *env) {
- if (!node->assignment.variable_name) {
- debug_print_int("Assignment variable name missing for node type: %d\n",
- node->type);
- return make_result((LiteralValue){.type = TYPE_ERROR}, false, false);
+ if (node->type != AST_ASSIGNMENT) {
+ return raise_error("Invalid node type for assignment.\n");
}
- // Evaluate the right-hand side
- InterpretResult assign_r = interpret_node(node->assignment.value, env);
-
- // If the RHS triggered an error or break, propagate it
- if (assign_r.is_error || assign_r.did_break) {
- return assign_r;
+ // Interpret the RHS expression
+ InterpretResult rhs_val_res = interpret_node(node->assignment.rhs, env);
+ if (rhs_val_res.is_error) {
+ return rhs_val_res; // propagate the error
}
- LiteralValue new_value = assign_r.value;
+ // Determine the LHS type
+ ASTNode *lhs_node = node->assignment.lhs;
- // 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;
+ switch (lhs_node->type) {
+ case AST_VARIABLE_REFERENCE: {
+ const char *var_name = lhs_node->variable_name;
+
+ Variable *var = get_variable(env, var_name);
+ if (!var) {
+ // Variable not found; optionally handle declaration here
+ Variable new_var;
+ new_var.variable_name = strdup(var_name);
+ if (!new_var.variable_name) {
+ return raise_error(
+ "Memory allocation failed for variable name `%s`.\n",
+ var_name);
+ }
+ new_var.value = rhs_val_res.value;
+ new_var.is_constant = false;
+
+ InterpretResult add_res = add_variable(env, new_var);
+ if (add_res.is_error) {
+ free(new_var.variable_name); // clean up on error
+ return add_res;
}
+
+ return add_res;
}
- if (existing_var) {
- break;
+
+ if (var->is_constant) {
+ return raise_error("Cannot reassign to constant `%s`.\n", var_name);
}
- target_env = target_env->parent;
- }
- // Determine the scope to modify
- Environment *scope_to_modify = existing_var ? target_env : env;
+ // If assigning to a string, free the old string to prevent memory leaks
+ if (var->value.type == TYPE_STRING && var->value.data.string) {
+ free(var->value.data.string);
+ }
- // Create a new variable instance
- Variable new_var = {.variable_name = strdup(node->assignment.variable_name),
- .value = new_value,
- .is_constant = false};
+ var->value = rhs_val_res.value;
- // Assign the variable
- InterpretResult add_res = add_variable(scope_to_modify, new_var);
- if (add_res.is_error) {
- return add_res;
+ return rhs_val_res;
}
- // Now, if a return was triggered during assignment, propagate it
- if (assign_r.did_return) {
- return assign_r;
+ case AST_ARRAY_INDEX_ACCESS: {
+ return interpret_array_index_assignment(lhs_node, env,
+ rhs_val_res.value);
}
- // Otherwise, return the assigned value
- return make_result(new_value, false, false);
+ case AST_ARRAY_SLICE_ACCESS: {
+ return interpret_array_slice_access(node, env);
+ }
+
+ case AST_ARRAY_OPERATION:
+ return interpret_array_operation(node, env);
+
+ default:
+ return raise_error("Invalid LHS in assignment.\n");
+ }
}
InterpretResult handle_string_concatenation(InterpretResult left,
@@ -363,10 +433,12 @@ InterpretResult handle_numeric_operator(const char *op,
(left.type == TYPE_FLOAT || right.type == TYPE_FLOAT);
// 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;
+ FLOAT_SIZE left_val = (left.type == TYPE_FLOAT)
+ ? left.data.floating_point
+ : (FLOAT_SIZE)left.data.integer;
+ FLOAT_SIZE right_val = (right.type == TYPE_FLOAT)
+ ? right.data.floating_point
+ : (FLOAT_SIZE)right.data.integer;
LiteralValue result;
memset(&result, 0, sizeof(LiteralValue));
@@ -406,7 +478,7 @@ InterpretResult handle_numeric_operator(const char *op,
if (right_val == 0.0) {
return raise_error("Floor division by zero.\n");
}
- double div_result = left_val / right_val;
+ FLOAT_SIZE div_result = left_val / right_val;
if (result_is_float) {
result.type = TYPE_FLOAT;
result.data.floating_point = floor(div_result);
@@ -724,6 +796,12 @@ Variable *get_variable(Environment *env, const char *variable_name) {
current_env->variables[i]
.value.data.function_ptr->name);
break;
+ case TYPE_ARRAY:
+ debug_print_int(
+ "Variable found: `%s` with array of %zu elements.\n",
+ variable_name,
+ current_env->variables[i].value.data.array.count);
+ break;
default:
debug_print_int("Variable found: `%s` with unknown type.\n",
variable_name);
@@ -743,6 +821,8 @@ InterpretResult add_variable(Environment *env, Variable var) {
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) {
+ debug_print_int("Attempted to reassign to constant `%s`\n",
+ var.variable_name);
return raise_error("Error: Cannot reassign to constant `%s`.\n",
var.variable_name);
}
@@ -754,7 +834,12 @@ InterpretResult add_variable(Environment *env, Variable var) {
}
env->variables[i].value = var.value;
- env->variables[i].is_constant = var.is_constant;
+ // Do not modify is_constant when updating existing variable
+ // env->variables[i].is_constant = var.is_constant;
+
+ debug_print_int("Updated variable `%s` with is_constant=%d\n",
+ var.variable_name, env->variables[i].is_constant);
+
return make_result(var.value, false, false);
}
}
@@ -803,6 +888,9 @@ InterpretResult add_variable(Environment *env, Variable var) {
env->variables[env->variable_count].is_constant = var.is_constant;
env->variable_count++;
+ debug_print_int("Added variable `%s` with is_constant=%d\n",
+ var.variable_name, var.is_constant);
+
return make_result(var.value, false, false);
}
@@ -993,12 +1081,12 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) {
return end_res;
}
- // Determine start & end as doubles for flexibility
- double start_val, end_val;
+ // Determine start & end as FLOAT_SIZEs for flexibility
+ FLOAT_SIZE 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 = (double)start_res.value.data.integer;
+ start_val = (FLOAT_SIZE)start_res.value.data.integer;
} else {
free(loop_var);
return raise_error("Start expression in `for` loop must be numeric\n");
@@ -1007,14 +1095,14 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) {
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 = (double)end_res.value.data.integer;
+ end_val = (FLOAT_SIZE)end_res.value.data.integer;
} else {
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
+ FLOAT_SIZE step = 1.0; // default
if (step_expr) {
InterpretResult step_res = interpret_node(step_expr, env);
if (step_res.is_error) {
@@ -1025,7 +1113,7 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) {
if (step_res.value.type == TYPE_FLOAT) {
step = step_res.value.data.floating_point;
} else if (step_res.value.type == TYPE_INTEGER) {
- step = (double)step_res.value.data.integer;
+ step = (FLOAT_SIZE)step_res.value.data.integer;
} else {
free(loop_var);
return raise_error(
@@ -1081,11 +1169,11 @@ InterpretResult interpret_for_loop(ASTNode *node, Environment *env) {
loop_var);
}
- double current_val;
+ FLOAT_SIZE current_val;
if (var->value.type == TYPE_FLOAT) {
current_val = var->value.data.floating_point;
} else if (var->value.type == TYPE_INTEGER) {
- current_val = (double)var->value.data.integer;
+ current_val = (FLOAT_SIZE)var->value.data.integer;
} else {
free(loop_var);
return raise_error("Loop variable `%s` must be numeric\n",
@@ -1562,3 +1650,542 @@ InterpretResult interpret_try(ASTNode *node, Environment *env) {
// Normal execution
return result;
}
+
+/**
+ * @brief Interprets array literals like `[1, 2, 3]`.
+ *
+ * @param node
+ * @param env
+ * @return InterpretResult
+ */
+InterpretResult interpret_array_literal(ASTNode *node, Environment *env) {
+ if (node->type != AST_ARRAY_LITERAL) {
+ return raise_error("Expected AST_ARRAY_LITERAL node.\n");
+ }
+
+ ArrayValue array;
+ array.count = 0;
+ array.capacity = node->array_literal.count > 4 ? node->array_literal.count
+ : 4; // initial capacity
+ array.elements = malloc(array.capacity * sizeof(LiteralValue));
+ if (!array.elements) {
+ return raise_error("Memory allocation failed for array elements.\n");
+ }
+
+ for (size_t i = 0; i < node->array_literal.count; i++) {
+ ASTNode *element_node = node->array_literal.elements[i];
+ InterpretResult elem_res = interpret_node(element_node, env);
+ if (elem_res.is_error) {
+ // Free previously allocated elements if necessary
+ // For simplicity, assume no deep copies needed here
+ free(array.elements);
+ return elem_res; // propagate the error
+ }
+
+ // Add the element to the array
+ if (array.count == array.capacity) {
+ size_t new_capacity = array.capacity * 2;
+ LiteralValue *new_elements =
+ realloc(array.elements, new_capacity * sizeof(LiteralValue));
+ if (!new_elements) {
+ free(array.elements);
+ return raise_error(
+ "Memory allocation failed while expanding array.\n");
+ }
+ array.elements = new_elements;
+ array.capacity = new_capacity;
+ }
+
+ array.elements[array.count++] = elem_res.value;
+ }
+
+ LiteralValue result;
+ result.type = TYPE_ARRAY;
+ result.data.array = array; // by value
+
+ return make_result(result, false, false);
+}
+
+InterpretResult interpret_array_operation(ASTNode *node, Environment *env) {
+ if (node->type == AST_ASSIGNMENT) {
+ // Handle assignment-based array operations (append, prepend)
+ ASTNode *lhs_node = node->assignment.lhs;
+ ASTNode *rhs_node = node->assignment.rhs;
+
+ if (lhs_node->type != AST_ARRAY_OPERATION) {
+ return raise_error("Expected AST_ARRAY_OPERATION in assignment.\n");
+ }
+
+ const char *operator= lhs_node->array_operation.operator;
+ ASTNode *array_node = lhs_node->array_operation.array;
+
+ if (array_node->type != AST_VARIABLE_REFERENCE) {
+ return raise_error("Array operation requires a variable reference "
+ "as the array.\n");
+ }
+
+ const char *var_name = array_node->variable_name;
+
+ // Retrieve variable from environment
+ Variable *var = get_variable(env, var_name);
+ if (!var) {
+ return raise_error("Undefined variable `%s`.\n", var_name);
+ }
+
+ if (var->value.type != TYPE_ARRAY) {
+ return raise_error("Array operation requires an array variable.\n");
+ }
+
+ // Check for `const`-ness
+ if (var->is_constant) {
+ return raise_error("Cannot mutate a constant array `%s`.\n",
+ var_name);
+ }
+
+ // Access the ArrayValue by reference
+ ArrayValue *array = &var->value.data.array;
+
+ // Interpret the operand (RHS of assignment)
+ InterpretResult operand_res = interpret_node(rhs_node, env);
+ if (operand_res.is_error) {
+ return operand_res;
+ }
+
+ // Perform operation based on operator
+ if (strcmp(operator, "^+") == 0) { // Append
+ if (array->count == array->capacity) {
+ size_t new_capacity = array->capacity * 2;
+ LiteralValue *new_elements = realloc(
+ array->elements, new_capacity * sizeof(LiteralValue));
+ if (!new_elements) {
+ return raise_error(
+ "Memory allocation failed while expanding array.\n");
+ }
+ array->elements = new_elements;
+ array->capacity = new_capacity;
+ }
+ array->elements[array->count++] = operand_res.value;
+ // Return the modified array
+ return make_result(var->value, false, false);
+ } else if (strcmp(operator, "+^") == 0) { // Prepend
+ if (array->count == array->capacity) {
+ size_t new_capacity = array->capacity * 2;
+ LiteralValue *new_elements = realloc(
+ array->elements, new_capacity * sizeof(LiteralValue));
+ if (!new_elements) {
+ return raise_error(
+ "Memory allocation failed while expanding array.\n");
+ }
+ array->elements = new_elements;
+ array->capacity = new_capacity;
+ }
+
+ // Shift elements to the right
+ memmove(&array->elements[1], &array->elements[0],
+ array->count * sizeof(LiteralValue));
+ array->elements[0] = operand_res.value;
+ array->count++;
+ // Return the modified array
+ return make_result(var->value, false, false);
+ } else {
+ return raise_error(
+ "Unsupported array operation operator `%s` in assignment.\n",
+ operator);
+ }
+ } else if (node->type == AST_ARRAY_OPERATION) {
+ // Handle standalone array operations (remove last, remove first)
+ const char *operator= node->array_operation.operator;
+ ASTNode *array_node = node->array_operation.array;
+
+ if (array_node->type != AST_VARIABLE_REFERENCE) {
+ return raise_error("Array operation requires a variable reference "
+ "as the array.\n");
+ }
+
+ const char *var_name = array_node->variable_name;
+
+ // Retrieve variable from environment
+ Variable *var = get_variable(env, var_name);
+ if (!var) {
+ return raise_error("Undefined variable `%s`.\n", var_name);
+ }
+
+ if (var->value.type != TYPE_ARRAY) {
+ return raise_error("Array operation requires an array variable.\n");
+ }
+
+ // Check for `const`-ness
+ if (var->is_constant) {
+ return raise_error("Cannot mutate a constant array `%s`.\n",
+ var_name);
+ }
+
+ // Access the ArrayValue by reference
+ ArrayValue *array = &var->value.data.array;
+
+ // Perform operation based on operator
+ if (strcmp(operator, "^-") == 0) { // Remove Last Element
+ if (array->count == 0) {
+ return raise_error("Cannot remove from an empty array.\n");
+ }
+ LiteralValue removed = array->elements[array->count - 1];
+ array->count--;
+ return make_result(removed, false, false);
+ } else if (strcmp(operator, "-^") == 0) { // Remove First Element
+ if (array->count == 0) {
+ return raise_error("Cannot remove from an empty array.\n");
+ }
+ LiteralValue removed = array->elements[0];
+ // Shift elements to the left
+ memmove(&array->elements[0], &array->elements[1],
+ (array->count - 1) * sizeof(LiteralValue));
+ array->count--;
+ return make_result(removed, false, false);
+ } else {
+ return raise_error(
+ "Unsupported array operation operator `%s`.\n", operator);
+ }
+ } else {
+ return raise_error("Unsupported node type for array operation.\n");
+ }
+}
+
+/**
+ * @brief Handles accessing array elements like `array[2]`.
+ *
+ * @param node
+ * @param env
+ * @return InterpretResult
+ */
+InterpretResult interpret_array_index_access(ASTNode *node, Environment *env) {
+ if (node->type != AST_ARRAY_INDEX_ACCESS) {
+ return raise_error("Expected AST_ARRAY_INDEX_ACCESS node.\n");
+ }
+
+ ASTNode *array_node = node->array_index_access.array;
+ ASTNode *index_node = node->array_index_access.index;
+
+ // Interpret the array
+ InterpretResult array_res = interpret_node(array_node, env);
+ if (array_res.is_error) {
+ return array_res;
+ }
+
+ if (array_res.value.type != TYPE_ARRAY) {
+ return raise_error("Index access requires an array operand.\n");
+ }
+
+ // Access the ArrayValue by reference
+ ArrayValue *array = &array_res.value.data.array;
+
+ // Interpret the index
+ InterpretResult index_res = interpret_node(index_node, env);
+ if (index_res.is_error) {
+ return index_res;
+ }
+
+ // Index must be integer
+ if (index_res.value.type != TYPE_INTEGER) {
+ return raise_error("Array index must be an integer.\n");
+ }
+
+ INT_SIZE index = index_res.value.data.integer;
+
+ // Handle negative indices (e.g., -1 refers to the last element)
+ if (index < 0) {
+ index = (INT_SIZE)array->count + index;
+ }
+
+ if (index < 0 || (size_t)index >= array->count) {
+ return raise_error("Array index `%lld` out of bounds.\n", index);
+ }
+
+ // Access the element
+ LiteralValue element = array->elements[index];
+ return make_result(element, false, false);
+}
+
+InterpretResult interpret_array_index_assignment(ASTNode *node,
+ Environment *env,
+ LiteralValue new_value) {
+ if (node->type != AST_ARRAY_INDEX_ACCESS) {
+ return raise_error("Expected AST_ARRAY_INDEX_ACCESS node.\n");
+ }
+
+ ASTNode *array_node = node->array_index_access.array;
+ ASTNode *index_node = node->array_index_access.index;
+
+ // Interpret the array
+ InterpretResult array_res = interpret_node(array_node, env);
+ if (array_res.is_error) {
+ return array_res;
+ }
+
+ if (array_res.value.type != TYPE_ARRAY) {
+ return raise_error("Index access requires an array operand.\n");
+ }
+
+ // Retrieve the variable to check for `const`-ness
+ if (array_node->type != AST_VARIABLE_REFERENCE) {
+ return raise_error("Index assignment requires a variable reference.\n");
+ }
+ const char *var_name = array_node->variable_name;
+ Variable *var = get_variable(env, var_name);
+ if (!var) {
+ return raise_error("Undefined variable `%s`.\n", var_name);
+ }
+
+ if (var->is_constant) {
+ debug_print_int("Attempted to assign to const array `%s`\n", var_name);
+ return raise_error("Cannot mutate a constant array `%s`.\n", var_name);
+ }
+
+ // Access the ArrayValue by reference
+ ArrayValue *array = &var->value.data.array;
+
+ // Interpret the index
+ InterpretResult index_res = interpret_node(index_node, env);
+ if (index_res.is_error) {
+ return index_res;
+ }
+
+ // Index must be integer
+ if (index_res.value.type != TYPE_INTEGER) {
+ return raise_error("Array index must be an integer.\n");
+ }
+
+ INT_SIZE index = index_res.value.data.integer;
+
+ // Handle negative indices (e.g., -1 refers to the last element)
+ if (index < 0) {
+ index = (INT_SIZE)array->count + index;
+ }
+
+ if (index < 0 || (size_t)index >= array->count) {
+ return raise_error("Array index `%lld` out of bounds.\n", index);
+ }
+
+ // Assign the new value to the array element
+ array->elements[index] = new_value;
+ return make_result(new_value, false, false);
+}
+
+// Helper function to get the index of a variable in the environment
+int get_variable_index(Environment *env, const char *variable_name) {
+ for (size_t i = 0; i < env->variable_count; i++) {
+ if (strcmp(env->variables[i].variable_name, variable_name) == 0) {
+ return (int)i;
+ }
+ }
+
+ // Not found
+ return -1;
+}
+
+/**
+ * @brief Handles slicing arrays like `array[1:4]`.
+ *
+ * @param node
+ * @param env
+ * @return InterpretResult
+ */
+InterpretResult interpret_array_slice_access(ASTNode *node, Environment *env) {
+ if (node->type != AST_ARRAY_SLICE_ACCESS) {
+ return raise_error("Expected AST_ARRAY_SLICE_ACCESS node.\n");
+ }
+
+ ASTNode *array_node = node->array_slice_access.array;
+ ASTNode *start_node = node->array_slice_access.start;
+ ASTNode *end_node = node->array_slice_access.end;
+ ASTNode *step_node = node->array_slice_access.step;
+
+ // Interpret the array
+ InterpretResult array_res = interpret_node(array_node, env);
+ if (array_res.is_error) {
+ return array_res;
+ }
+
+ if (array_res.value.type != TYPE_ARRAY) {
+ return raise_error("Slice access requires an array operand.\n");
+ }
+
+ // Access the ArrayValue by reference
+ ArrayValue *array = &array_res.value.data.array;
+ size_t array_count = array->count;
+
+ debug_print_int("Array `%s` has %zu elements.\n", array_node->variable_name,
+ array_count);
+
+ // Interpret the step first to determine default start and end
+ FLOAT_SIZE step_val = 1.0; // default step
+ if (step_node) {
+ InterpretResult step_res = interpret_node(step_node, env);
+ if (step_res.is_error) {
+ return step_res;
+ }
+ if (step_res.value.type != TYPE_INTEGER &&
+ step_res.value.type != TYPE_FLOAT) {
+ return raise_error("Slice step must be integer or float.\n");
+ }
+ step_val = (step_res.value.type == TYPE_INTEGER)
+ ? (FLOAT_SIZE)step_res.value.data.integer
+ : step_res.value.data.floating_point;
+ }
+
+ if (step_val == 0.0) {
+ return raise_error("Slice step cannot be zero.\n");
+ }
+
+ debug_print_int(
+ "Slice parameters: start_node=%p, end_node=%p, step_val=%Lf\n",
+ start_node, end_node, step_val);
+
+ // Set default start and end based on step direction
+ FLOAT_SIZE start_val;
+ FLOAT_SIZE end_val;
+ if (step_val > 0) {
+ start_val = 0.0; // Default start for positive step
+ end_val = (FLOAT_SIZE)array_count; // Default end for positive step
+ } else { // step_val < 0
+ start_val =
+ (FLOAT_SIZE)(array_count - 1); // Default start for negative step
+ end_val = -1.0; // Default end for negative step
+ }
+
+ // Interpret start if provided
+ if (start_node) {
+ InterpretResult start_res = interpret_node(start_node, env);
+ if (start_res.is_error) {
+ return start_res;
+ }
+ if (start_res.value.type != TYPE_INTEGER &&
+ start_res.value.type != TYPE_FLOAT) {
+ return raise_error("Slice start must be integer or float.\n");
+ }
+ start_val = (start_res.value.type == TYPE_INTEGER)
+ ? (FLOAT_SIZE)start_res.value.data.integer
+ : (step_val > 0)
+ ? floor(start_res.value.data.floating_point)
+ : ceil(start_res.value.data.floating_point);
+ }
+
+ // Interpret end if provided
+ if (end_node) {
+ InterpretResult end_res = interpret_node(end_node, env);
+ if (end_res.is_error) {
+ return end_res;
+ }
+ if (end_res.value.type != TYPE_INTEGER &&
+ end_res.value.type != TYPE_FLOAT) {
+ return raise_error("Slice end must be integer or float.\n");
+ }
+ end_val = (end_res.value.type == TYPE_INTEGER)
+ ? (FLOAT_SIZE)end_res.value.data.integer
+ : (step_val > 0) ? ceil(end_res.value.data.floating_point)
+ : floor(end_res.value.data.floating_point);
+ }
+
+ debug_print_int("Interpreted slice values: start_val=%Lf, end_val=%Lf\n",
+ start_val, end_val);
+
+ // Convert to integer indices
+ INT_SIZE start_index = (INT_SIZE)floor(start_val);
+ INT_SIZE end_index = (INT_SIZE)ceil(end_val);
+ INT_SIZE step = (INT_SIZE)step_val;
+
+ // Handle negative indices based on step direction
+ if (step > 0) {
+ if (start_index < 0)
+ start_index += (INT_SIZE)array_count;
+ if (end_index < 0)
+ end_index += (INT_SIZE)array_count;
+ } else { // step < 0
+ if (start_index < 0)
+ start_index += (INT_SIZE)array_count;
+ // Do NOT adjust end_index for negative steps
+ }
+
+ debug_print_int(
+ "Converted indices: start_index=%lld, end_index=%lld, step=%lld\n",
+ start_index, end_index, step);
+
+ // Clamp indices based on step direction
+ if (step > 0) {
+ if (start_index < 0)
+ start_index = 0;
+ if ((size_t)start_index > array_count)
+ start_index = array_count;
+ if (end_index < 0)
+ end_index = 0;
+ if ((size_t)end_index > array_count)
+ end_index = array_count;
+ } else { // step < 0
+ if (start_index >= (INT_SIZE)array_count)
+ start_index = (INT_SIZE)array_count - 1;
+ if (start_index < 0)
+ start_index = (INT_SIZE)array_count - 1;
+ if (end_index < -1)
+ end_index = -1;
+ if (end_index >= (INT_SIZE)array_count)
+ end_index = -1;
+ }
+
+ // Determine the number of elements in the slice
+ size_t slice_count = 0;
+ if (step > 0) {
+ if (start_index >= end_index) {
+ slice_count = 0;
+ } else {
+ slice_count = (size_t)((end_index - start_index + step - 1) / step);
+ }
+ } else { // step < 0
+ if (start_index <= end_index) {
+ slice_count = 0;
+ } else {
+ slice_count =
+ (size_t)((start_index - end_index - step - 1) / (-step));
+ }
+ }
+
+ debug_print_int("Calculated slice_count=%zu\n", slice_count);
+
+ // Initialize the slice array
+ ArrayValue slice;
+ slice.count = 0;
+ slice.capacity = slice_count > 4 ? slice_count : 4;
+ slice.elements = malloc(slice.capacity * sizeof(LiteralValue));
+ if (!slice.elements) {
+ return raise_error("Memory allocation failed for array slice.\n");
+ }
+
+ // Populate the slice
+ for (INT_SIZE i = start_index;
+ (step > 0 && i < end_index) || (step < 0 && i > end_index);
+ i += step) {
+ debug_print_int("Loop iteration: i=%lld\n", i);
+
+ if (i < 0 || (size_t)i >= array_count) {
+ debug_print_int("Index out of bounds: i=%lld, array_count=%zu\n", i,
+ array_count);
+ break;
+ }
+ slice.elements[slice.count++] = array->elements[i];
+ if (slice.elements[slice.count - 1].type == TYPE_INTEGER) {
+ debug_print_int("Added element: %lld (count=%zu)\n",
+ slice.elements[slice.count - 1].data.integer,
+ slice.count);
+ } else {
+ debug_print_int("Added element of non-integer type (count=%zu)\n",
+ slice.count);
+ }
+ }
+
+ debug_print_int("Final slice count=%zu\n", slice.count);
+
+ // Create the slice LiteralValue
+ LiteralValue result;
+ result.type = TYPE_ARRAY;
+ result.data.array = slice; // by value
+
+ return make_result(result, false, false);
+}
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index f11e16d..6849eb3 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -15,8 +15,9 @@
InterpretResult interpret_node(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_variable_reference(ASTNode *node, Environment *env);
+InterpretResult interpret_var_declaration(ASTNode *node, Environment *env);
+InterpretResult interpret_const_declaration(ASTNode *node, Environment *env);
InterpretResult interpret_assignment(ASTNode *node, Environment *env);
InterpretResult interpret_binary_op(ASTNode *node, Environment *env);
InterpretResult interpret_conditional(ASTNode *node, Environment *env);
@@ -33,6 +34,13 @@ InterpretResult call_user_defined_function(Function *func_ref,
ASTNode *call_node,
Environment *env);
InterpretResult interpret_try(ASTNode *node, Environment *env);
+InterpretResult interpret_array_literal(ASTNode *node, Environment *env);
+InterpretResult interpret_array_operation(ASTNode *node, Environment *env);
+InterpretResult interpret_array_index_access(ASTNode *node, Environment *env);
+InterpretResult interpret_array_index_assignment(ASTNode *node,
+ Environment *env,
+ LiteralValue new_value);
+InterpretResult interpret_array_slice_access(ASTNode *node, Environment *env);
// Interpret program
void interpret_program(ASTNode *program, Environment *env);
diff --git a/src/interpreter/interpreter_types.h b/src/interpreter/interpreter_types.h
index d0ffa97..3a9c590 100644
--- a/src/interpreter/interpreter_types.h
+++ b/src/interpreter/interpreter_types.h
@@ -1,68 +1,91 @@
#ifndef INTERPRETER_TYPES_H
#define INTERPRETER_TYPES_H
+#include
+#include
+#include
+#include
+#include
+#include
+
// Forward declarations
struct ASTFunctionParameter;
struct ASTNode;
struct Function;
-
-// Forward declaration and type alias for Environment
typedef struct Environment Environment;
+struct LiteralValue;
+// Enum for Literal Types
typedef enum {
TYPE_BOOLEAN,
TYPE_FLOAT,
TYPE_INTEGER,
TYPE_STRING,
+ TYPE_ARRAY,
TYPE_FUNCTION,
TYPE_ERROR
} LiteralType;
+// Enum for Return Types
typedef enum { RETURN_NORMAL, RETURN_ERROR } ReturnType;
-typedef struct {
+// Structure for Array Values
+typedef struct ArrayValue {
+ struct LiteralValue *elements; // Dynamic array of `LiteralValue`s
+ size_t count; // Number of elements
+ size_t capacity; // Allocated capacity
+} ArrayValue;
+
+// Structure for Literal Values
+typedef struct LiteralValue {
LiteralType type;
union {
char *string;
- FLOAT_SIZE floating_point;
- INT_SIZE integer;
+ long double floating_point; // Assuming FLOAT_SIZE is long double
+ long long integer; // Assuming INT_SIZE is long long
bool boolean;
struct Function *function_ptr;
+ struct ArrayValue array; // **By value**
} data;
} LiteralValue;
+// Structure for Function Results
typedef struct {
LiteralValue value;
bool should_return; // Flag to indicate if function should return early
bool is_error; // Flag to indicate if this is an error return (burn)
} FunctionResult;
+// Structure for Variables
typedef struct {
char *variable_name;
LiteralValue value;
bool is_constant;
} Variable;
+// Structure for Functions
typedef struct Function {
char *name;
- ASTFunctionParameter *parameters; // Linked list of parameters
- ASTNode *body; // Function body
+ struct ASTFunctionParameter *parameters; // Linked list of parameters
+ struct ASTNode *body; // Function body
FunctionResult return_value;
bool is_builtin;
} Function;
+// Structure for Environment
struct Environment {
Variable *variables;
size_t variable_count;
- size_t capacity; // to handle dynamic resizing
+ size_t capacity; // To handle dynamic resizing
Function *functions; // Array of functions
size_t function_count;
size_t function_capacity;
- Environment *parent; // Parent environment
+ Environment *parent; // Parent environment for nested scopes
};
+// Structure for Interpret Results
typedef struct {
LiteralValue value; // Result of interpreting a node
bool did_return; // True if this node caused a function return to bubble up
diff --git a/src/interpreter/utils.c b/src/interpreter/utils.c
index f31f0d3..b740105 100644
--- a/src/interpreter/utils.c
+++ b/src/interpreter/utils.c
@@ -188,52 +188,70 @@ ASTFunctionParameter *copy_function_parameters(ASTFunctionParameter *params) {
return new_params;
}
-ASTNode *copy_ast_node(ASTNode *node) {
- if (!node) {
+// Helper function for safely duplicating strings
+char *safe_strdup(const char *str) {
+ if (!str)
+ return NULL;
+ char *copy = strdup(str);
+ if (!copy) {
+ fatal_error("Memory allocation failed for string duplication.\n");
+ }
+ return copy;
+}
+
+// Helper function for copying ASTCaseNode linked lists
+ASTCaseNode *copy_ast_case_node(ASTCaseNode *case_node) {
+ if (!case_node)
return NULL;
+
+ ASTCaseNode *new_head = NULL, *new_tail = NULL;
+
+ while (case_node) {
+ ASTCaseNode *new_case = malloc(sizeof(ASTCaseNode));
+ if (!new_case) {
+ fatal_error("Memory allocation failed for ASTCaseNode.\n");
+ }
+
+ new_case->condition = copy_ast_node(case_node->condition);
+ new_case->body = copy_ast_node(case_node->body);
+ new_case->next = NULL;
+
+ if (!new_head) {
+ new_head = new_tail = new_case;
+ } else {
+ new_tail->next = new_case;
+ new_tail = new_case;
+ }
+
+ case_node = case_node->next;
}
+ return new_head;
+}
+
+ASTNode *copy_ast_node(ASTNode *node) {
+ if (!node)
+ return NULL;
+
debug_print_int("Copying ASTNode of type %d at %p\n", node->type,
(void *)node);
- ASTNode *new_node = malloc(sizeof(ASTNode));
+ ASTNode *new_node = calloc(1, sizeof(ASTNode));
if (!new_node) {
fatal_error("Memory allocation failed in `copy_ast_node`\n");
}
- // 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);
+ new_node->assignment.lhs = copy_ast_node(node->assignment.lhs);
+ new_node->assignment.rhs = copy_ast_node(node->assignment.rhs);
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.name =
+ safe_strdup(node->function_declaration.name);
new_node->function_declaration.parameters =
copy_function_parameters(node->function_declaration.parameters);
new_node->function_declaration.body =
@@ -241,15 +259,7 @@ ASTNode *copy_ast_node(ASTNode *node) {
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.name = safe_strdup(node->function_call.name);
new_node->function_call.arguments =
copy_ast_node(node->function_call.arguments);
break;
@@ -261,31 +271,12 @@ ASTNode *copy_ast_node(ASTNode *node) {
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");
+ if (node->literal.type == LITERAL_STRING) {
+ new_node->literal.value.string =
+ safe_strdup(node->literal.value.string);
+ } else {
+ new_node->literal.value =
+ node->literal.value; // Copy union directly
}
break;
@@ -298,26 +289,12 @@ ASTNode *copy_ast_node(ASTNode *node) {
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.operator= safe_strdup(node->unary_op.operator);
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.operator= safe_strdup(node->binary_op.operator);
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;
@@ -331,16 +308,8 @@ ASTNode *copy_ast_node(ASTNode *node) {
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.loop_variable =
+ safe_strdup(node->for_loop.loop_variable);
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);
@@ -352,43 +321,22 @@ ASTNode *copy_ast_node(ASTNode *node) {
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;
+ new_node->switch_case.cases =
+ copy_ast_case_node(node->switch_case.cases);
break;
case AST_VAR_DECLARATION:
- case AST_CONST_DECLARATION:
- 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;
- }
+ new_node->var_declaration.variable_name =
+ safe_strdup(node->var_declaration.variable_name);
+ new_node->var_declaration.initializer =
+ copy_ast_node(node->var_declaration.initializer);
break;
- case AST_BREAK:
- // No fields to copy
+ case AST_CONST_DECLARATION:
+ new_node->const_declaration.constant_name =
+ safe_strdup(node->const_declaration.constant_name);
+ new_node->const_declaration.initializer =
+ copy_ast_node(node->const_declaration.initializer);
break;
case AST_TERNARY:
@@ -406,6 +354,57 @@ ASTNode *copy_ast_node(ASTNode *node) {
copy_ast_node(node->try_block.finally_block);
break;
+ case AST_BREAK:
+ case AST_CATCH:
+ case AST_FINALLY:
+ // Nothing to copy
+ break;
+
+ case AST_ARRAY_LITERAL:
+ new_node->array_literal.count = node->array_literal.count;
+ new_node->array_literal.elements =
+ malloc(sizeof(ASTNode *) * node->array_literal.count);
+ if (!new_node->array_literal.elements) {
+ fatal_error(
+ "Memory allocation failed for array literal elements.\n");
+ }
+ for (size_t i = 0; i < node->array_literal.count; i++) {
+ new_node->array_literal.elements[i] =
+ copy_ast_node(node->array_literal.elements[i]);
+ }
+ break;
+
+ case AST_ARRAY_OPERATION:
+ new_node->array_operation.operator=
+ safe_strdup(node->array_operation.operator);
+ new_node->array_operation.array =
+ copy_ast_node(node->array_operation.array);
+ new_node->array_operation.operand =
+ copy_ast_node(node->array_operation.operand);
+ break;
+
+ case AST_ARRAY_INDEX_ACCESS:
+ new_node->array_index_access.array =
+ copy_ast_node(node->array_index_access.array);
+ new_node->array_index_access.index =
+ copy_ast_node(node->array_index_access.index);
+ break;
+
+ case AST_ARRAY_SLICE_ACCESS:
+ new_node->array_slice_access.array =
+ copy_ast_node(node->array_slice_access.array);
+ new_node->array_slice_access.start =
+ copy_ast_node(node->array_slice_access.start);
+ new_node->array_slice_access.end =
+ copy_ast_node(node->array_slice_access.end);
+ new_node->array_slice_access.step =
+ copy_ast_node(node->array_slice_access.step);
+ break;
+
+ case AST_VARIABLE_REFERENCE:
+ new_node->variable_name = safe_strdup(node->variable_name);
+ break;
+
default:
fatal_error("Unknown ASTNodeType encountered in `copy_ast_node`.\n");
}
diff --git a/src/interpreter/utils.h b/src/interpreter/utils.h
index 29e4653..a5e81c5 100644
--- a/src/interpreter/utils.h
+++ b/src/interpreter/utils.h
@@ -32,6 +32,8 @@ 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);
+ASTCaseNode *copy_ast_case_node(ASTCaseNode *case_node);
+char *safe_strdup(const char *str);
// Type Helpers
bool is_numeric_type(LiteralType type);
diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c
index e1edfb8..958ff03 100644
--- a/src/lexer/lexer.c
+++ b/src/lexer/lexer.c
@@ -72,6 +72,12 @@ Token *tokenize(const char *source) {
continue;
}
+ // Array
+ if (strchr("[]", c)) {
+ scan_array(&state, &tokens, &token_count, &capacity);
+ continue;
+ }
+
// Identifier
if (is_valid_identifier_start(c)) {
scan_identifier_or_keyword(&state, &tokens, &token_count,
diff --git a/src/lexer/scanner.c b/src/lexer/scanner.c
index 78218c2..2109d3f 100644
--- a/src/lexer/scanner.c
+++ b/src/lexer/scanner.c
@@ -20,49 +20,182 @@ void scan_number(ScannerState *state, Token **tokens, size_t *token_count,
TokenType prev_type = (*tokens)[*token_count - 1].type;
if (prev_type == TOKEN_INTEGER || prev_type == TOKEN_FLOAT ||
prev_type == TOKEN_IDENTIFIER || prev_type == TOKEN_STRING ||
- prev_type == TOKEN_PAREN_CLOSE) {
+ prev_type == TOKEN_PAREN_CLOSE ||
+ prev_type == TOKEN_SQ_BRACKET_CLOSE) {
is_start_of_expression = false;
}
}
if (is_start_of_expression) {
is_negative = true;
- state->pos++; // move past the negative sign
+ state->pos++; // Move past the negative sign
} else {
+ // The '-' is not part of a negative number; treat it as an operator
scan_operator(state, tokens, token_count, capacity);
return;
}
}
+ // After handling '-', the next character must be a digit
if (state->pos >= state->length || !isdigit(state->source[state->pos])) {
if (is_negative) {
- state->pos = start; // reset position
+ // The '-' was not followed by a digit; treat it as an operator
+ state->pos = start; // Reset position to include '-'
scan_operator(state, tokens, token_count, capacity);
return;
}
token_error("Invalid number format", state->line);
}
+ // Continue scanning digits and possible decimal point
while (state->pos < state->length &&
(isdigit(state->source[state->pos]) ||
(state->source[state->pos] == '.' && !has_decimal_point))) {
if (state->source[state->pos] == '.') {
if (state->pos + 1 >= state->length ||
!isdigit(state->source[state->pos + 1])) {
- break; // if there's no digit after the decimal point, treat the
- // decimal as a separate token
+ break; // If there's no digit after '.', treat '.' as a separate
+ // token
}
has_decimal_point = true;
}
state->pos++;
}
- char *lexeme = strndup(&state->source[start], state->pos - start);
+ // Calculate the end position
+ size_t end = state->pos;
+
+ // Allocate and construct the lexeme
+ char *lexeme;
+ if (is_negative) {
+ // Allocate space for '-' and the number
+ size_t lexeme_length = end - start;
+ lexeme = malloc(lexeme_length + 1); // +1 for null terminator
+ if (!lexeme) {
+ token_error("Memory allocation failed for lexeme", state->line);
+ }
+ lexeme[0] = '-';
+ // Copy the number part after '-'
+ strncpy(lexeme + 1, &state->source[start + 1], lexeme_length - 1);
+ lexeme[lexeme_length] = '\0';
+ } else {
+ // Positive number; no '-' sign
+ lexeme = strndup(&state->source[start], end - start);
+ if (!lexeme) {
+ token_error("Memory allocation failed for lexeme", state->line);
+ }
+ }
+
+ // Determine the token type based on the presence of a decimal point
TokenType type = has_decimal_point ? TOKEN_FLOAT : TOKEN_INTEGER;
+
+ // Append the token to the tokens array
append_token(tokens, token_count, capacity, type, lexeme, state->line);
+
+ // Free the lexeme if append_token duplicates it
free(lexeme);
}
+void scan_array(ScannerState *state, Token **tokens, size_t *token_count,
+ size_t *capacity) {
+ char c = state->source[state->pos];
+
+ if (c == '[') {
+ // Add opening bracket token
+ append_token(tokens, token_count, capacity, TOKEN_SQ_BRACKET_OPEN, "[",
+ state->line);
+ state->pos++; // Move past `[`
+
+ // Scan inside the brackets
+ while (state->pos < state->length && state->source[state->pos] != ']') {
+ char inner_c = state->source[state->pos];
+
+ // Handle whitespace
+ if (is_whitespace(inner_c)) {
+ if (inner_c == '\n') {
+ state->line++;
+ }
+ state->pos++;
+ continue;
+ }
+
+ // Handle special array operations first
+ if (inner_c == '^' || inner_c == '+' || inner_c == '-') {
+ // Peek the next character
+ char next_c = (state->pos + 1 < state->length)
+ ? state->source[state->pos + 1]
+ : '\0';
+
+ // Handle two-character array operators (e.g., ^+, +^, ^-, -^)
+ if (next_c == '^' || next_c == '+' || next_c == '-') {
+ // Two-character array operator
+ char *op = strndup(&state->source[state->pos], 2);
+ append_token(tokens, token_count, capacity, TOKEN_ARRAY_OP,
+ op, state->line);
+ free(op);
+ state->pos += 2; // Move past the two-character operator
+ continue;
+ }
+ // **Handle negative numbers (e.g., -1) within slicing**
+ else if (inner_c == '-' && (state->pos + 1 < state->length) &&
+ isdigit(state->source[state->pos + 1])) {
+ // Treat '-' as part of a negative number
+ scan_number(state, tokens, token_count, capacity);
+ continue;
+ } else {
+ // Single-character array operator (if valid)
+ char op[2] = {inner_c, '\0'};
+ append_token(tokens, token_count, capacity, TOKEN_ARRAY_OP,
+ op, state->line);
+ state->pos++;
+ continue;
+ }
+ }
+
+ // Handle numbers (including negative numbers) after array operators
+ if (isdigit(inner_c) ||
+ (inner_c == '-' && state->pos + 1 < state->length &&
+ isdigit(state->source[state->pos + 1]))) {
+ scan_number(state, tokens, token_count, capacity);
+ continue;
+ }
+
+ // Handle separators `,`
+ if (inner_c == ',') {
+ append_token(tokens, token_count, capacity, TOKEN_DELIMITER,
+ ",", state->line);
+ state->pos++;
+ continue;
+ }
+
+ // Handle slicing syntax `:`
+ if (inner_c == ':') {
+ append_token(tokens, token_count, capacity, TOKEN_COLON, ":",
+ state->line);
+ state->pos++;
+ continue;
+ }
+
+ // Unexpected character
+ fprintf(stderr,
+ "Unexpected character `%c` at position %zu (line %d)\n",
+ inner_c, state->pos, state->line);
+ token_error("Unexpected character in array", state->line);
+ }
+
+ // Check for closing bracket
+ if (state->pos < state->length && state->source[state->pos] == ']') {
+ append_token(tokens, token_count, capacity, TOKEN_SQ_BRACKET_CLOSE,
+ "]", state->line);
+ state->pos++; // Move past `]`.
+ } else {
+ token_error("Unmatched opening bracket `[`", state->line);
+ }
+ } else if (c == ']') {
+ token_error("Unmatched closing bracket `]`", state->line);
+ }
+}
+
void scan_string(ScannerState *state, Token **tokens, size_t *token_count,
size_t *capacity) {
size_t start = ++(state->pos); // skip opening quote
diff --git a/src/lexer/scanner.h b/src/lexer/scanner.h
index bde640a..0b382e6 100644
--- a/src/lexer/scanner.h
+++ b/src/lexer/scanner.h
@@ -60,6 +60,25 @@ void scan_number(ScannerState *state, Token **tokens, size_t *token_count,
void scan_string(ScannerState *state, Token **tokens, size_t *token_count,
size_t *capacity);
+/**
+ * Processes an array literal or access in the source code.
+ *
+ * This function is triggered when encountering an opening or closing square
+ * bracket (`[` or `]`). It determines whether the brackets represent an array
+ * literal, an index access, or slicing operations. It ensures proper matching
+ * of brackets and validates the syntax.
+ *
+ * If mismatched or unexpected brackets are encountered, it triggers an error.
+ *
+ * @param state The current scanning state, which includes the source code,
+ * the current position, and the line number.
+ * @param tokens A pointer to the token array being built.
+ * @param token_count The current number of tokens.
+ * @param capacity The allocated capacity for the token array.
+ */
+void scan_array(ScannerState *state, Token **tokens, size_t *token_count,
+ size_t *capacity);
+
/**
* Processes a boolean literal in the source code.
*
diff --git a/src/parser/array_parser.c b/src/parser/array_parser.c
new file mode 100644
index 0000000..c81a5de
--- /dev/null
+++ b/src/parser/array_parser.c
@@ -0,0 +1,178 @@
+#include "array_parser.h"
+
+ASTNode *parse_array_literal(ParserState *state) {
+ ASTNode *node = malloc(sizeof(ASTNode));
+ if (!node) {
+ parser_error("Memory allocation failed for array literal",
+ get_current_token(state));
+ }
+ node->type = AST_ARRAY_LITERAL;
+ node->array_literal.elements = NULL;
+ node->array_literal.count = 0;
+ node->next = NULL;
+
+ expect_token(state, TOKEN_SQ_BRACKET_OPEN,
+ "Expected `[` to start array literal");
+
+ // Initialize dynamic array for elements
+ size_t capacity = 4; // initial capacity
+ node->array_literal.elements = malloc(sizeof(ASTNode *) * capacity);
+ if (!node->array_literal.elements) {
+ parser_error("Memory allocation failed for array elements",
+ get_current_token(state));
+ }
+
+ // Handle empty array
+ if (get_current_token(state)->type == TOKEN_SQ_BRACKET_CLOSE) {
+ expect_token(state, TOKEN_SQ_BRACKET_CLOSE,
+ "Expected `]` to close array literal");
+ return node;
+ }
+
+ while (get_current_token(state)->type != TOKEN_SQ_BRACKET_CLOSE) {
+ // Parse the next expression
+ ASTNode *element = parse_expression(state);
+ if (!element) {
+ parser_error("Failed to parse array element",
+ get_current_token(state));
+ }
+
+ // Add the element to the array
+ if (node->array_literal.count >= capacity) {
+ capacity *= 2;
+ ASTNode **new_elements = realloc(node->array_literal.elements,
+ sizeof(ASTNode *) * capacity);
+ if (!new_elements) {
+ parser_error(
+ "Memory allocation failed while resizing array elements",
+ get_current_token(state));
+ }
+ node->array_literal.elements = new_elements;
+ }
+ node->array_literal.elements[node->array_literal.count++] = element;
+
+ // Check for comma `,` separator
+ if (get_current_token(state)->type == TOKEN_DELIMITER &&
+ strcmp(get_current_token(state)->lexeme, ",") == 0) {
+ advance_token(state); // consume comma `,`
+ // Allow trailing comma before closing bracket
+ if (get_current_token(state)->type == TOKEN_SQ_BRACKET_CLOSE) {
+ break;
+ }
+ } else {
+ // No more elements
+ break;
+ }
+ }
+
+ expect_token(state, TOKEN_SQ_BRACKET_CLOSE,
+ "Expected `]` to close array literal");
+
+ return node;
+}
+
+ASTNode *parse_index_access(ASTNode *array, ParserState *state) {
+ ASTNode *node = malloc(sizeof(ASTNode));
+ if (!node) {
+ parser_error("Memory allocation failed for index access",
+ get_current_token(state));
+ }
+
+ // Ensure we start with `[`
+ expect_token(state, TOKEN_SQ_BRACKET_OPEN,
+ "Expected `[` for array indexing or slicing");
+
+ Token *current = get_current_token(state);
+
+ // Check if it's a slice (contains `:`)
+ bool is_slice = false;
+ size_t temp_token = state->current_token;
+
+ // Iterate through tokens until `]` to detect a `:` for slicing
+ while (state->tokens[temp_token].type != TOKEN_SQ_BRACKET_CLOSE &&
+ state->tokens[temp_token].type != TOKEN_EOF) {
+ if (state->tokens[temp_token].type == TOKEN_COLON) {
+ is_slice = true;
+ break;
+ }
+ temp_token++;
+ }
+
+ if (is_slice) {
+ // It's a slice
+ node->type = AST_ARRAY_SLICE_ACCESS;
+ node->array_slice_access.array = array;
+ node->array_slice_access.start = NULL;
+ node->array_slice_access.end = NULL;
+ node->array_slice_access.step = NULL;
+
+ // Parse start expression (optional)
+ if (get_current_token(state)->type != TOKEN_COLON) {
+ node->array_slice_access.start = parse_expression(state);
+ }
+
+ // Expect and consume first `:`
+ expect_token(state, TOKEN_COLON, "Expected `:` in slice expression");
+
+ // Parse end expression (optional)
+ if (get_current_token(state)->type != TOKEN_COLON &&
+ get_current_token(state)->type != TOKEN_SQ_BRACKET_CLOSE) {
+ node->array_slice_access.end = parse_expression(state);
+ }
+
+ // Check for optional step
+ if (get_current_token(state)->type == TOKEN_COLON) {
+ advance_token(state); // consume second `:`
+ if (get_current_token(state)->type != TOKEN_SQ_BRACKET_CLOSE) {
+ node->array_slice_access.step = parse_expression(state);
+ } else {
+ // Handle cases like `[::]`
+ node->array_slice_access.step = NULL;
+ }
+ }
+
+ // Expect and consume `]`
+ expect_token(state, TOKEN_SQ_BRACKET_CLOSE,
+ "Expected `]` to close slice expression");
+ node->next = NULL;
+ } else if (is_array_operator(current)) {
+ // It's an array operator like `^+`, `+^`, `^-`, `-^`
+ node->type = AST_ARRAY_OPERATION;
+ node->array_operation.operator= strdup(current->lexeme);
+ if (!node->array_operation.operator) {
+ parser_error("Memory allocation failed for array operator",
+ current);
+ }
+
+ node->array_operation.array = array;
+ node->next = NULL;
+ advance_token(state); // consume the operator
+
+ // Expect and consume `]`
+ expect_token(state, TOKEN_SQ_BRACKET_CLOSE,
+ "Expected `]` after array operator");
+ } else {
+ // It's a simple index
+ node->type = AST_ARRAY_INDEX_ACCESS;
+ node->array_index_access.array = array;
+ node->array_index_access.index = parse_expression(state);
+
+ // Expect and consume `]`
+ expect_token(state, TOKEN_SQ_BRACKET_CLOSE,
+ "Expected `]` to close index expression");
+ node->next = NULL;
+ }
+
+ return node;
+}
+
+// Helper function to check if a token is an array operator
+bool is_array_operator(Token *token) {
+ if (token->type != TOKEN_ARRAY_OP) {
+ return false;
+ }
+
+ return strcmp(token->lexeme, "^+") == 0 ||
+ strcmp(token->lexeme, "+^") == 0 ||
+ strcmp(token->lexeme, "^-") == 0 || strcmp(token->lexeme, "-^") == 0;
+}
diff --git a/src/parser/array_parser.h b/src/parser/array_parser.h
new file mode 100644
index 0000000..5e4b280
--- /dev/null
+++ b/src/parser/array_parser.h
@@ -0,0 +1,17 @@
+#ifndef ARRAY_PARSER_H
+#define ARRAY_PARSER_H
+
+#include "../shared/ast_types.h"
+#include "../shared/token_types.h"
+#include "parser.h"
+#include "parser_state.h"
+#include "utils.h"
+#include
+
+ASTNode *parse_array_literal(ParserState *state);
+ASTNode *parse_index_access(ASTNode *array, ParserState *state);
+
+// Helpers
+bool is_array_operator(Token *token);
+
+#endif
diff --git a/src/parser/operator_parser.c b/src/parser/operator_parser.c
index d4ba84d..68c7290 100644
--- a/src/parser/operator_parser.c
+++ b/src/parser/operator_parser.c
@@ -5,6 +5,7 @@
#include
#include "../shared/ast_types.h"
+#include "array_parser.h"
#include "parser_state.h"
// Implementation of the main expression parser
@@ -166,18 +167,17 @@ ASTNode *parse_unary(ParserState *state) {
return parse_primary(state);
}
-// Primary Expressions: literals, variables, function calls, parentheses
+// Primary Expressions: literals, variables, function calls, parentheses, arrays
ASTNode *parse_primary(ParserState *state) {
+ // Identify the "base" expression
+ ASTNode *node = NULL;
Token *current = get_current_token(state);
if (current->type == TOKEN_INTEGER || current->type == TOKEN_FLOAT ||
current->type == TOKEN_STRING || current->type == TOKEN_BOOLEAN) {
- ASTNode *node = create_literal_node(current);
+ node = create_literal_node(current);
advance_token(state);
- return node;
- }
-
- if (current->type == TOKEN_FUNCTION_NAME) {
+ } else if (current->type == TOKEN_FUNCTION_NAME) {
// Parse function call
char *func_name = strdup(current->lexeme);
if (!func_name) {
@@ -193,11 +193,8 @@ ASTNode *parse_primary(ParserState *state) {
expect_token(state, TOKEN_PAREN_CLOSE,
"Expected ')' after function arguments");
- ASTNode *node = create_function_call_node(func_name, args);
- return node;
- }
-
- if (current->type == TOKEN_IDENTIFIER) {
+ node = create_function_call_node(func_name, args);
+ } else if (current->type == TOKEN_IDENTIFIER) {
// Check if identifier is followed by '(' indicating a function call
Token *next = peek_next_token(state);
if (next && next->type == TOKEN_PAREN_OPEN) {
@@ -220,22 +217,27 @@ ASTNode *parse_primary(ParserState *state) {
ASTNode *node = create_function_call_node(func_name, args);
return node;
} else {
- // It's a variable
- ASTNode *node = create_variable_node(current->lexeme);
+ // It's a variable reference
+ node = create_variable_reference_node(current->lexeme);
advance_token(state);
- return node;
}
- }
-
- if (current->type == TOKEN_PAREN_OPEN) {
+ } else if (current->type == TOKEN_PAREN_OPEN) {
advance_token(state); // consume `(`
- ASTNode *node = parse_operator_expression(state);
+ node = parse_operator_expression(state);
expect_token(state, TOKEN_PAREN_CLOSE, "Expected `)` after expression");
- return node;
+ } else if (current->type == TOKEN_SQ_BRACKET_OPEN) {
+ node = parse_array_literal(state);
+ node->type = AST_ARRAY_LITERAL;
+ } else {
+ parser_error("Expected expression", current);
}
- parser_error("Expected expression", current);
- return NULL; // unreachable
+ // Handle chained indexing/slicing
+ while (get_current_token(state)->type == TOKEN_SQ_BRACKET_OPEN) {
+ node = parse_index_access(node, state);
+ }
+
+ return node;
}
ASTNode *parse_argument_list(ParserState *state) {
@@ -315,9 +317,10 @@ ASTNode *create_literal_node(Token *token) {
if (!node) {
parser_error("Memory allocation failed for literal node", token);
}
+
node->type = AST_LITERAL;
+ node->literal.type = LITERAL_INTEGER;
- // Assign the correct literal type and value based on the token type
switch (token->type) {
case TOKEN_INTEGER:
node->literal.type = LITERAL_INTEGER;
@@ -325,20 +328,24 @@ ASTNode *create_literal_node(Token *token) {
break;
case TOKEN_FLOAT:
node->literal.type = LITERAL_FLOAT;
- node->literal.value.floating_point = strtold(token->lexeme, NULL);
+ node->literal.value.floating_point = atof(token->lexeme);
break;
case TOKEN_STRING:
node->literal.type = LITERAL_STRING;
node->literal.value.string = strdup(token->lexeme);
+ if (!node->literal.value.string) {
+ free(node);
+ parser_error("Memory allocation failed for string literal", token);
+ }
break;
case TOKEN_BOOLEAN:
node->literal.type = LITERAL_BOOLEAN;
- node->literal.value.boolean = strcmp(token->lexeme, "True") == 0;
+ node->literal.value.boolean = (strcmp(token->lexeme, "True") == 0);
break;
default:
- parser_error("Unsupported literal type", token);
+ free(node);
+ parser_error("Unknown literal type", token);
}
-
node->next = NULL;
return node;
}
diff --git a/src/parser/parser.c b/src/parser/parser.c
index fff9309..b39e761 100644
--- a/src/parser/parser.c
+++ b/src/parser/parser.c
@@ -32,6 +32,7 @@ ASTNode *parse_statement(ParserState *state) {
debug_print_par("Current Token: Type=`%d`, Lexeme=`%s`\n", token->type,
token->lexeme);
+
if (match_token(state, "let"))
return parse_variable_declaration(state);
if (match_token(state, "const"))
@@ -61,119 +62,130 @@ ASTNode *parse_statement(ParserState *state) {
return node;
}
- // Handle variable assignments
- if (token->type == TOKEN_IDENTIFIER) {
- Token *next_token = peek_next_token(state);
- if (next_token) {
- debug_print_par("Peek Next Token: Type=`%d`, Lexeme=`%s`\n",
- next_token->type, next_token->lexeme);
- if (next_token->type == TOKEN_OPERATOR &&
- strcmp(next_token->lexeme, "=") == 0) {
- return parse_variable_assignment(state);
- }
- }
- return parse_expression_statement(state);
+ // Handle variable assignments using the helper function
+ if (token->type == TOKEN_IDENTIFIER && is_assignment(state)) {
+ return parse_variable_assignment(state);
}
- // If none of the above, raise an error
- parser_error("Unexpected token at start of statement", token);
- return NULL;
+ // Handle expression statements (e.g., array operations without assignment)
+ ASTNode *expr_stmt = parse_expression_statement(state);
+ expect_token(state, TOKEN_DELIMITER,
+ "Expected `;` after expression statement");
+ return expr_stmt;
}
ASTNode *parse_expression_statement(ParserState *state) {
// Parse the expression (which can handle binary ops, variables, etc.)
- ASTNode *expr_node = parse_expression(state);
- expect_token(state, TOKEN_DELIMITER,
- "Expected `;` after expression statement");
- return expr_node;
+ // ASTNode *expr_node = parse_expression(state);
+ // expect_token(state, TOKEN_DELIMITER,
+ // "Expected `;` after expression statement");
+ // return expr_node;
+ return parse_expression(state);
}
ASTNode *parse_declaration(ParserState *state, ASTNodeType type) {
+ // Parse keyword (`let` or `const`)
if (type == AST_CONST_DECLARATION) {
debug_print_par("Starting constant declaration parse\n");
expect_token(state, TOKEN_KEYWORD, "Expected `const` keyword");
- } else {
+ } else if (type == AST_VAR_DECLARATION) {
debug_print_par("Starting variable declaration parse\n");
expect_token(state, TOKEN_KEYWORD, "Expected `let` keyword");
+ } else {
+ parser_error("Unknown declaration type", get_current_token(state));
}
// Parse variable/constant name
Token *name = get_current_token(state);
- if (type == AST_CONST_DECLARATION) {
- expect_token(state, TOKEN_IDENTIFIER, "Expected constant name");
- expect_token(state, TOKEN_OPERATOR, "Expected `=` after constant name");
- } else {
- expect_token(state, TOKEN_IDENTIFIER, "Expected variable name");
- expect_token(state, TOKEN_OPERATOR, "Expected `=` after variable name");
+ if (name->type != TOKEN_IDENTIFIER && name->type != TOKEN_FUNCTION_NAME) {
+ parser_error("Expected name in declaration", name);
}
-
- // Create AST node
- ASTNode *node = malloc(sizeof(ASTNode));
- if (!node) {
- parser_error("Memory allocation failed", get_current_token(state));
+ char *decl_name = strdup(name->lexeme);
+ if (!decl_name) {
+ parser_error("Memory allocation failed for declaration name", name);
}
- node->type = type;
+ advance_token(state); // Consume name
- // Create a deep copy of the variable/constant name
- if (name && name->lexeme) {
- node->assignment.variable_name = strdup(name->lexeme);
- if (!node->assignment.variable_name) {
- parser_error(AST_CONST_DECLARATION
- ? "Memory allocation failed for constant name"
- : "Memory allocation failed for variable name",
- name);
- }
- } else {
- parser_error(AST_CONST_DECLARATION ? "Invalid constant name token"
- : "Invalid variable name token",
- name);
- }
+ // Expect `=` operator
+ expect_token(state, TOKEN_OPERATOR, "Expected `=` after name");
- node->assignment.value = parse_expression(state);
- node->next = NULL;
+ // Parse initializer expression
+ ASTNode *initializer = parse_expression(state);
+ if (!initializer) {
+ parser_error("Expected initializer expression",
+ get_current_token(state));
+ }
+ // Expect `;` delimiter
expect_token(state, TOKEN_DELIMITER,
- AST_CONST_DECLARATION
+ type == AST_CONST_DECLARATION
? "Expected `;` after constant declaration"
: "Expected `;` after variable declaration");
+ // Create AST node based on type
+ ASTNode *node = malloc(sizeof(ASTNode));
+ if (!node) {
+ parser_error("Memory allocation failed for declaration node",
+ get_current_token(state));
+ }
+ node->type = type;
+ node->next = NULL;
+
+ if (type == AST_VAR_DECLARATION) {
+ node->var_declaration.variable_name = decl_name;
+ node->var_declaration.initializer = initializer;
+ } else if (type == AST_CONST_DECLARATION) {
+ node->const_declaration.constant_name = decl_name;
+ node->const_declaration.initializer = initializer;
+ }
+
return node;
}
ASTNode *parse_variable_declaration(ParserState *state) {
- return parse_declaration(state, AST_ASSIGNMENT);
+ return parse_declaration(state, AST_VAR_DECLARATION);
}
ASTNode *parse_constant_declaration(ParserState *state) {
return parse_declaration(state, AST_CONST_DECLARATION);
}
+ASTNode *create_variable_reference_node(char *name) {
+ ASTNode *node = calloc(
+ 1, sizeof(ASTNode)); // use `calloc` instead of `malloc` to ensure
+ // all fields are initialized to zero (`NULL`)
+ if (!node) {
+ parser_error("Memory allocation failed for variable reference node",
+ NULL);
+ }
+ node->type = AST_VARIABLE_REFERENCE;
+ node->variable_name = strdup(name);
+ if (!node->variable_name) {
+ free(node);
+ parser_error("Memory allocation failed for variable name", NULL);
+ }
+ node->next = NULL;
+ return node;
+}
+
ASTNode *parse_variable_assignment(ParserState *state) {
debug_print_par("Starting variable assignment parse\n");
- // Parse variable name
- Token *name = get_current_token(state);
- debug_print_par("Variable assignment name: `%s`\n", name->lexeme);
- advance_token(state); // consume variable name
+ // Parse the left-hand side (LHS) expression
+ ASTNode *lhs = parse_expression(state);
// Expect `=` operator
Token *op_token = get_current_token(state);
- debug_print_par("Expected operator `=`, found: `%s`\n", op_token->lexeme);
- expect_token(state, TOKEN_OPERATOR, "Expected `=` after variable name");
-
- // Parse the expression on the right-hand side
- ASTNode *value_node = parse_expression(state);
- debug_print_par("Parsed expression for assignment\n");
-
- ASTNode *node = malloc(sizeof(ASTNode));
- if (!node) {
- parser_error("Memory allocation failed", get_current_token(state));
+ if (op_token->type != TOKEN_OPERATOR ||
+ strcmp(op_token->lexeme, "=") != 0) {
+ parser_error("Expected `=` operator after variable name or slice",
+ op_token);
}
+ advance_token(state); // consume `=`
- node->type = AST_ASSIGNMENT;
- node->assignment.variable_name = strdup(name->lexeme);
- node->assignment.value = value_node;
- node->next = NULL;
+ // Parse the expression on the right-hand side (RHS)
+ ASTNode *rhs = parse_expression(state);
+ debug_print_par("Parsed expression for assignment\n");
// Expect `;` delimiter
Token *delimiter = get_current_token(state);
@@ -186,6 +198,18 @@ ASTNode *parse_variable_assignment(ParserState *state) {
"Expected `;` after variable assignment");
debug_print_par("Consumed `;` after variable assignment\n");
+ // Create AST Assignment Node
+ ASTNode *node = malloc(sizeof(ASTNode));
+ if (!node) {
+ parser_error("Memory allocation failed for assignment node",
+ get_current_token(state));
+ }
+
+ node->type = AST_ASSIGNMENT;
+ node->assignment.lhs = lhs;
+ node->assignment.rhs = rhs;
+ node->next = NULL;
+
return node;
}
@@ -225,7 +249,7 @@ ASTNode *parse_literal_or_identifier(ParserState *state) {
}
}
- // Handle literals and identifiers
+ // Handle literals
if (current->type == TOKEN_FLOAT || current->type == TOKEN_INTEGER ||
current->type == TOKEN_STRING || current->type == TOKEN_BOOLEAN) {
ASTNode *node = malloc(sizeof(ASTNode));
@@ -244,12 +268,12 @@ ASTNode *parse_literal_or_identifier(ParserState *state) {
} else if (current->type == TOKEN_STRING) {
node->literal.type = LITERAL_STRING;
node->literal.value.string = strdup(current->lexeme);
- } else if (current->type == TOKEN_BOOLEAN) { // Handle boolean
+ } else if (current->type == TOKEN_BOOLEAN) {
node->literal.type = LITERAL_BOOLEAN;
if (strcmp(current->lexeme, "True") == 0) {
- node->literal.value.boolean = 1;
+ node->literal.value.boolean = true;
} else {
- node->literal.value.boolean = 0;
+ node->literal.value.boolean = false;
}
}
@@ -258,21 +282,19 @@ ASTNode *parse_literal_or_identifier(ParserState *state) {
return node;
} else if (current->type == TOKEN_FUNCTION_NAME ||
current->type == TOKEN_IDENTIFIER) {
- // Check if `(` indicates a call
- Token *next = peek_next_token(state);
- if (next && next->type == TOKEN_PAREN_OPEN) {
- // It's a call
- ASTNode *node = parse_function_call(state);
- return node;
- } else {
- // It's just a variable (or a function reference used as a variable)
- ASTNode *node = create_variable_node(current->lexeme);
- advance_token(state);
- return node;
+ // Handle variable or function call
+ ASTNode *node = create_variable_node(current->lexeme);
+ advance_token(state);
+
+ // Handle array indexing or slicing
+ while (get_current_token(state)->type == TOKEN_SQ_BRACKET_OPEN) {
+ node = parse_index_access(node, state);
}
+
+ return node;
} else if (current->type == TOKEN_PAREN_OPEN) {
advance_token(state); // consume `(`
- ASTNode *node = parse_operator_expression(state);
+ ASTNode *node = parse_expression(state);
expect_token(state, TOKEN_PAREN_CLOSE, "Expected `)` after expression");
return node;
}
@@ -566,7 +588,8 @@ ASTNode *parse_case_body(ParserState *state) {
while (1) {
Token *current = get_current_token(state);
- // Stop parsing the body if any of `is`, `else`, or `}` get encountered
+ // Stop parsing the body if any of `is`, `else`, or `}` get
+ // encountered
if ((current->type == TOKEN_KEYWORD &&
(strcmp(current->lexeme, "is") == 0 ||
strcmp(current->lexeme, "else") == 0)) ||
@@ -914,3 +937,64 @@ ASTNode *parse_finally_block(ParserState *state) {
expect_token(state, TOKEN_BRACE_CLOSE, "Expected `}` to end finish block");
return finally_body;
}
+
+/**
+ * Peeks ahead 'n' tokens from the current position.
+ *
+ * @param state The current parser state.
+ * @param n The number of tokens to peek ahead.
+ * @return A pointer to the token 'n' positions ahead, or the last token if out
+ * of bounds.
+ */
+Token *peek_ahead(ParserState *state, size_t n) {
+ size_t target = state->current_token + n;
+ // Assuming tokens are terminated with TOKEN_EOF
+ // Return the last token if target exceeds the array
+ return &state->tokens[target];
+}
+
+/**
+ * Determines if the current statement is an assignment.
+ *
+ * An assignment can be:
+ * - identifier = expression
+ * - identifier [ array_operator ] = expression
+ *
+ * @param state The current parser state.
+ * @return `true` if it's an assignment, `false` otherwise.
+ */
+bool is_assignment(ParserState *state) {
+ // Ensure the current token is an identifier
+ if (state->current->type != TOKEN_IDENTIFIER)
+ return false;
+
+ // Peek the next token
+ Token *next = peek_next_token(state);
+ if (!next)
+ return false;
+
+ // Case 1: identifier '='
+ if (next->type == TOKEN_OPERATOR && strcmp(next->lexeme, "=") == 0)
+ return true;
+
+ // Case 2: identifier '[' array_operator ']' '='
+ if (next->type == TOKEN_SQ_BRACKET_OPEN) {
+ // Peek two tokens ahead (array operator)
+ Token *array_op = peek_ahead(state, 2);
+ if (array_op && is_array_operator(array_op)) {
+ // Peek three tokens ahead (']')
+ Token *after_bracket = peek_ahead(state, 3);
+ if (after_bracket &&
+ after_bracket->type == TOKEN_SQ_BRACKET_CLOSE) {
+ // Peek four tokens ahead ('=')
+ Token *equals = peek_ahead(state, 4);
+ if (equals && equals->type == TOKEN_OPERATOR &&
+ strcmp(equals->lexeme, "=") == 0)
+ return true;
+ }
+ }
+ }
+
+ // Not an assignment
+ return false;
+}
diff --git a/src/parser/parser.h b/src/parser/parser.h
index 6d0a5d5..b08d3cb 100644
--- a/src/parser/parser.h
+++ b/src/parser/parser.h
@@ -2,6 +2,7 @@
#define PARSER_H
#include "../shared/ast_types.h"
+#include "array_parser.h"
#include "operator_parser.h"
#include "parser_state.h"
@@ -34,13 +35,17 @@ ASTNode *parse_expression(ParserState *state);
ASTNode *parse_literal_or_identifier(ParserState *state);
ASTNode *parse_block(ParserState *state);
+ASTNode *create_variable_reference_node(char *name);
+
// Helper functions
ASTNode *parse_declaration(ParserState *state, ASTNodeType type);
bool match_token(ParserState *state, const char *lexeme);
Token *peek_next_token(ParserState *state);
+Token *peek_ahead(ParserState *state, size_t n);
ASTNode *parse_expression_statement(ParserState *state);
ASTNode *parse_block(ParserState *state);
ASTNode *parse_case_body(ParserState *state);
ASTNode *parse_function_body(ParserState *state);
+bool is_assignment(ParserState *state);
#endif
diff --git a/src/parser/utils.c b/src/parser/utils.c
index 9a8b203..1aef54e 100644
--- a/src/parser/utils.c
+++ b/src/parser/utils.c
@@ -5,10 +5,10 @@ void free_ast(ASTNode *node) {
ASTNode *next = node->next;
switch (node->type) {
-
case AST_ASSIGNMENT:
- free(node->assignment.variable_name);
- free_ast(node->assignment.value);
+ // free(node->assignment.variable_name);
+ free_ast(node->assignment.lhs);
+ free_ast(node->assignment.rhs);
break;
case AST_LITERAL:
@@ -47,25 +47,27 @@ void free_ast(ASTNode *node) {
free_ast(node->for_loop.body);
break;
- case AST_CONST_DECLARATION:
case AST_VAR_DECLARATION:
- free(node->variable_name);
+ free(node->var_declaration.variable_name);
+ break;
+
+ case AST_CONST_DECLARATION:
+ free(node->const_declaration.constant_name);
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;
+ free_ast(node->function_declaration.body);
+ {
+ 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:
@@ -77,87 +79,80 @@ void free_ast(ASTNode *node) {
free_ast(node->function_return.return_data);
break;
- case AST_BREAK:
- // No cleanup needed!
- break;
-
case AST_TERNARY:
free_ast(node->ternary.condition);
free_ast(node->ternary.true_expr);
free_ast(node->ternary.false_expr);
break;
- case AST_SWITCH: {
- if (node->switch_case.expression) {
- free_ast(node->switch_case.expression);
- }
-
- if (node->switch_case.cases) {
+ case AST_SWITCH:
+ free_ast(node->switch_case.expression);
+ {
ASTCaseNode *case_node = node->switch_case.cases;
-
while (case_node) {
ASTCaseNode *next = case_node->next;
-
- // Free the condition ASTNode if it exists
- if (case_node->condition) {
- free_ast(case_node->condition);
- }
-
- // Free the body
- if (case_node->body) {
- free_ast(case_node->body);
- }
-
+ free_ast(case_node->condition);
+ free_ast(case_node->body);
free(case_node);
case_node = next;
}
}
-
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) {
+ free_ast(node->try_block.try_block);
+ {
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_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;
+ case AST_ARRAY_LITERAL:
+ for (size_t i = 0; i < node->array_literal.count; i++) {
+ free_ast(node->array_literal.elements[i]);
+ }
+ free(node->array_literal.elements);
+ break;
+
+ case AST_ARRAY_OPERATION:
+ free(node->array_operation.operator);
+ free_ast(node->array_operation.array);
+ break;
+
+ case AST_ARRAY_INDEX_ACCESS:
+ free_ast(node->array_index_access.array);
+ free_ast(node->array_index_access.index);
+ break;
+
+ case AST_ARRAY_SLICE_ACCESS:
+ free_ast(node->array_slice_access.array);
+ free_ast(node->array_slice_access.start);
+ free_ast(node->array_slice_access.end);
+ free_ast(node->array_slice_access.step);
+ break;
+
// Already handled by `AST_TRY`
case AST_CATCH:
case AST_FINALLY:
+ case AST_VARIABLE_REFERENCE:
+ case AST_BREAK:
+ // No dynamic memory to free
break;
default:
- fprintf(stderr,
- "Error: Unknown `ASTNode` type `%d` in `free_ast`.\n",
+ fprintf(stderr, "Unknown ASTNode type `%d` in free_ast.\n",
node->type);
exit(1);
}
@@ -168,7 +163,7 @@ void free_ast(ASTNode *node) {
}
// Print indentation based on depth
-static void print_indentation(int depth) {
+void print_indent(int depth) {
for (int i = 0; i < depth; i++) {
printf(" ");
}
@@ -176,213 +171,300 @@ static void print_indentation(int depth) {
// Print AST nodes
void print_ast(ASTNode *node, int depth) {
- if (!debug_flag) {
- return;
- }
+ if (debug_flag) {
+ while (node != NULL) {
+ print_indent(depth);
+ switch (node->type) {
+ case AST_VAR_DECLARATION:
+ printf("Variable Declaration: %s\n",
+ node->var_declaration.variable_name);
+ if (node->var_declaration.initializer) {
+ print_indent(depth + 1);
+ printf("Initializer:\n");
+ print_ast(node->var_declaration.initializer, depth + 2);
+ }
+ break;
- while (node != NULL) {
- print_indentation(depth);
+ case AST_CONST_DECLARATION:
+ printf("Constant Declaration: %s\n",
+ node->const_declaration.constant_name);
+ if (node->const_declaration.initializer) {
+ print_indent(depth + 1);
+ printf("Initializer:\n");
+ print_ast(node->const_declaration.initializer, depth + 2);
+ }
+ break;
- switch (node->type) {
- case AST_ASSIGNMENT:
- printf("Assignment:\n");
- print_indentation(depth + 1);
- printf("Variable: `%s`\n", node->assignment.variable_name);
- print_indentation(depth + 1);
- printf("Value:\n");
- print_ast(node->assignment.value, depth + 2);
- break;
+ case AST_ASSIGNMENT:
+ printf("Assignment:\n");
+ print_indent(depth + 1);
+ printf("LHS:\n");
+ print_ast(node->assignment.lhs, depth + 2);
+ print_indent(depth + 1);
+ printf("RHS:\n");
+ print_ast(node->assignment.rhs, depth + 2);
+ break;
- case AST_FUNCTION_DECLARATION:
- printf("Function Declaration: `%s`\n",
- node->function_declaration.name);
- // Print Parameters
- if (node->function_declaration.parameters != NULL) {
- print_indentation(depth + 1);
+ case AST_FUNCTION_DECLARATION:
+ printf("Function Declaration: %s\n",
+ node->function_declaration.name);
+ print_indent(depth + 1);
printf("Parameters:\n");
- ASTFunctionParameter *param =
- node->function_declaration.parameters;
- while (param != NULL) {
- print_indentation(depth + 2);
- printf("- `%s`\n", param->parameter_name);
- param = param->next;
+ {
+ ASTFunctionParameter *param =
+ node->function_declaration.parameters;
+ while (param != NULL) {
+ print_indent(depth + 2);
+ printf("- %s\n", param->parameter_name);
+ param = param->next;
+ }
}
- } else {
- print_indentation(depth + 1);
- printf("Parameters: None\n");
- }
- // Print Body
- if (node->function_declaration.body != NULL) {
- print_indentation(depth + 1);
+ print_indent(depth + 1);
printf("Body:\n");
print_ast(node->function_declaration.body, depth + 2);
- } else {
- print_indentation(depth + 1);
- printf("Body: None\n");
- }
- break;
+ break;
- case AST_FUNCTION_CALL:
- printf("Function Call: `%s`\n", node->function_call.name);
- // Print Arguments
- if (node->function_call.arguments != NULL) {
- print_indentation(depth + 1);
+ case AST_FUNCTION_CALL:
+ printf("Function Call: %s\n", node->function_call.name);
+ print_indent(depth + 1);
printf("Arguments:\n");
print_ast(node->function_call.arguments, depth + 2);
- } else {
- print_indentation(depth + 1);
- printf("Arguments: None\n");
- }
- break;
+ break;
- case AST_FUNCTION_RETURN:
- printf("Function Return:\n");
- if (node->function_return.return_data != NULL) {
+ case AST_FUNCTION_RETURN:
+ printf("Function Return:\n");
print_ast(node->function_return.return_data, depth + 1);
- } else {
- print_indentation(depth + 1);
- printf("Return Data: None\n");
- }
- break;
-
- case AST_LITERAL:
- printf("Literal: ");
- switch (node->literal.type) {
- case LITERAL_INTEGER:
- printf("`%lld`\n", node->literal.value.integer);
break;
- case LITERAL_FLOAT:
- printf("`%Lf`\n", node->literal.value.floating_point);
+
+ case AST_LITERAL:
+ printf("Literal: ");
+ switch (node->literal.type) {
+ case LITERAL_STRING:
+ printf("String (%s)\n", node->literal.value.string);
+ break;
+ case LITERAL_FLOAT:
+ printf("Float (%Lf)\n", node->literal.value.floating_point);
+ break;
+ case LITERAL_INTEGER:
+ printf("Integer (%lld)\n",
+ (long long)node->literal.value.integer);
+ break;
+ case LITERAL_BOOLEAN:
+ printf("Boolean (%s)\n",
+ node->literal.value.boolean ? "true" : "false");
+ break;
+ default:
+ printf("Unknown Literal Type\n");
+ }
break;
- case LITERAL_STRING:
- printf("`\"%s\"`\n", node->literal.value.string);
+
+ case AST_CONDITIONAL:
+ printf("Conditional:\n");
+ print_indent(depth + 1);
+ printf("Condition:\n");
+ print_ast(node->conditional.condition, depth + 2);
+ print_indent(depth + 1);
+ printf("Body:\n");
+ print_ast(node->conditional.body, depth + 2);
+ if (node->conditional.else_branch) {
+ print_indent(depth + 1);
+ printf("Else Branch:\n");
+ print_ast(node->conditional.else_branch, depth + 2);
+ }
break;
- case LITERAL_BOOLEAN:
- printf(node->literal.value.boolean ? "True\n" : "False\n");
+
+ case AST_UNARY_OP:
+ printf("Unary Operation: %s\n", node->unary_op.operator);
+ print_indent(depth + 1);
+ printf("Operand:\n");
+ print_ast(node->unary_op.operand, depth + 2);
break;
- default:
- printf("Unknown Literal Type\n");
- }
- break;
- case AST_CONDITIONAL:
- printf("Conditional Statement:\n");
- print_indentation(depth + 1);
- printf("Condition:\n");
- print_ast(node->conditional.condition, depth + 2);
- print_indentation(depth + 1);
- printf("Body:\n");
- print_ast(node->conditional.body, depth + 2);
- if (node->conditional.else_branch != NULL) {
- print_indentation(depth + 1);
- printf("Else Branch:\n");
- print_ast(node->conditional.else_branch, depth + 2);
- }
- break;
+ case AST_BINARY_OP:
+ printf("Binary Operation: %s\n", node->binary_op.operator);
+ print_indent(depth + 1);
+ printf("Left:\n");
+ print_ast(node->binary_op.left, depth + 2);
+ print_indent(depth + 1);
+ printf("Right:\n");
+ print_ast(node->binary_op.right, depth + 2);
+ break;
- case AST_UNARY_OP:
- printf("Unary Operation: `%s`\n", node->unary_op.operator);
- print_ast(node->unary_op.operand, depth + 1);
- break;
+ case AST_WHILE_LOOP:
+ printf("While Loop:\n");
+ print_indent(depth + 1);
+ printf("Condition:\n");
+ print_ast(node->while_loop.condition, depth + 2);
+ print_indent(depth + 1);
+ printf("Body:\n");
+ print_ast(node->while_loop.body, depth + 2);
+ printf("Re-evaluate Condition: %s\n",
+ node->while_loop.re_evaluate_condition ? "true"
+ : "false");
+ break;
- case AST_BINARY_OP:
- printf("Binary Operation: `%s`\n", node->binary_op.operator);
- print_indentation(depth + 1);
- printf("Left Operand:\n");
- print_ast(node->binary_op.left, depth + 2);
- print_indentation(depth + 1);
- printf("Right Operand:\n");
- print_ast(node->binary_op.right, depth + 2);
- break;
+ case AST_FOR_LOOP:
+ printf("For Loop:\n");
+ print_indent(depth + 1);
+ printf("Loop Variable: %s\n", node->for_loop.loop_variable);
+ print_indent(depth + 1);
+ printf("Start Expression:\n");
+ print_ast(node->for_loop.start_expr, depth + 2);
+ print_indent(depth + 1);
+ printf("End Expression:\n");
+ print_ast(node->for_loop.end_expr, depth + 2);
+ print_indent(depth + 1);
+ printf("Inclusive: %s\n",
+ node->for_loop.inclusive ? "true" : "false");
+ if (node->for_loop.step_expr) {
+ print_indent(depth + 1);
+ printf("Step Expression:\n");
+ print_ast(node->for_loop.step_expr, depth + 2);
+ }
+ print_indent(depth + 1);
+ printf("Body:\n");
+ print_ast(node->for_loop.body, depth + 2);
+ break;
- case AST_WHILE_LOOP:
- printf("While Loop:\n");
- print_indentation(depth + 1);
- printf("Condition:\n");
- print_ast(node->while_loop.condition, depth + 2);
- print_indentation(depth + 1);
- printf("Body:\n");
- print_ast(node->while_loop.body, depth + 2);
- break;
+ case AST_SWITCH:
+ printf("Switch Statement:\n");
+ print_indent(depth + 1);
+ printf("Expression:\n");
+ print_ast(node->switch_case.expression, depth + 2);
+ print_indent(depth + 1);
+ printf("Cases:\n");
+ {
+ ASTCaseNode *case_node = node->switch_case.cases;
+ while (case_node != NULL) {
+ print_indent(depth + 2);
+ printf("Case:\n");
+ if (case_node->condition) {
+ print_indent(depth + 3);
+ printf("Condition:\n");
+ print_ast(case_node->condition, depth + 4);
+ } else {
+ print_indent(depth + 3);
+ printf("Condition: None (Default)\n");
+ }
+ print_indent(depth + 3);
+ printf("Body:\n");
+ print_ast(case_node->body, depth + 4);
+ case_node = case_node->next;
+ }
+ }
+ break;
- case AST_FOR_LOOP:
- printf("For Loop:\n");
- print_indentation(depth + 1);
- printf("Loop Variable: `%s`\n", node->for_loop.loop_variable);
- print_indentation(depth + 1);
- printf("Start Expression:\n");
- print_ast(node->for_loop.start_expr, depth + 2);
- print_indentation(depth + 1);
- printf("End Expression:\n");
- print_ast(node->for_loop.end_expr, depth + 2);
- print_indentation(depth + 1);
- printf("Inclusive: `%s`\n",
- node->for_loop.inclusive ? "True" : "False");
- if (node->for_loop.step_expr != NULL) {
- print_indentation(depth + 1);
- printf("Step Expression:\n");
- print_ast(node->for_loop.step_expr, depth + 2);
- } else {
- print_indentation(depth + 1);
- printf("Step Expression: None\n");
- }
- print_indentation(depth + 1);
- printf("Body:\n");
- print_ast(node->for_loop.body, depth + 2);
- break;
+ case AST_BREAK:
+ printf("Break Statement\n");
+ break;
- case AST_SWITCH:
- printf("Switch Statement:\n");
- print_indentation(depth + 1);
- printf("Expression:\n");
- print_ast(node->switch_case.expression, depth + 2);
- print_indentation(depth + 1);
- printf("Cases:\n");
- ASTCaseNode *case_node = node->switch_case.cases;
- size_t case_num = 1;
- while (case_node != NULL) {
- print_indentation(depth + 2);
- printf("Case %zu:\n", case_num++);
- print_indentation(depth + 3);
+ case AST_TERNARY:
+ printf("Ternary Operation:\n");
+ print_indent(depth + 1);
printf("Condition:\n");
- print_ast(case_node->condition, depth + 4);
- print_indentation(depth + 3);
- printf("Body:\n");
- print_ast(case_node->body, depth + 4);
- case_node = case_node->next;
- }
- break;
+ print_ast(node->ternary.condition, depth + 2);
+ print_indent(depth + 1);
+ printf("True Expression:\n");
+ print_ast(node->ternary.true_expr, depth + 2);
+ print_indent(depth + 1);
+ printf("False Expression:\n");
+ print_ast(node->ternary.false_expr, depth + 2);
+ break;
- case AST_BREAK:
- printf("Break Statement\n");
- break;
+ case AST_TRY:
+ printf("Try Block:\n");
+ print_indent(depth + 1);
+ printf("Try Block Content:\n");
+ print_ast(node->try_block.try_block, depth + 2);
+ // Handle catch blocks
+ if (node->try_block.catch_blocks) {
+ ASTCatchNode *catch_node = node->try_block.catch_blocks;
+ while (catch_node != NULL) {
+ print_indent(depth + 1);
+ printf("Catch Block (Error Variable: %s):\n",
+ catch_node->error_variable
+ ? catch_node->error_variable
+ : "None");
+ print_ast(catch_node->body, depth + 2);
+ catch_node = catch_node->next;
+ }
+ }
+ // Handle finally block
+ if (node->try_block.finally_block) {
+ print_indent(depth + 1);
+ printf("Finally Block:\n");
+ print_ast(node->try_block.finally_block, depth + 2);
+ }
+ break;
- case AST_TERNARY:
- printf("Ternary Operation:\n");
- print_indentation(depth + 1);
- printf("Condition:\n");
- print_ast(node->ternary.condition, depth + 2);
- print_indentation(depth + 1);
- printf("True Expression:\n");
- print_ast(node->ternary.true_expr, depth + 2);
- print_indentation(depth + 1);
- printf("False Expression:\n");
- print_ast(node->ternary.false_expr, depth + 2);
- break;
+ case AST_ARRAY_LITERAL:
+ printf("Array Literal: [\n");
+ for (size_t i = 0; i < node->array_literal.count; i++) {
+ print_ast(node->array_literal.elements[i], depth + 1);
+ }
+ print_indent(depth);
+ printf("]\n");
+ break;
- case AST_VAR_DECLARATION:
- printf("Variable: `%s`\n", node->variable_name);
- break;
+ case AST_ARRAY_OPERATION:
+ printf("Array Operation: %s\n", node->array_operation.operator);
+ print_indent(depth + 1);
+ printf("Array:\n");
+ print_ast(node->array_operation.array, depth + 2);
+ break;
- case AST_CONST_DECLARATION:
- printf("Constant: `%s`\n", node->variable_name);
- break;
+ case AST_ARRAY_INDEX_ACCESS:
+ printf("Array Index Access:\n");
+ print_indent(depth + 1);
+ printf("Array:\n");
+ print_ast(node->array_index_access.array, depth + 2);
+ print_indent(depth + 1);
+ printf("Index:\n");
+ print_ast(node->array_index_access.index, depth + 2);
+ break;
- default:
- printf("Unknown Node Type: `%d`\n", node->type);
- }
+ case AST_ARRAY_SLICE_ACCESS:
+ printf("Array Slice Access:\n");
+ print_indent(depth + 1);
+ printf("Array:\n");
+ print_ast(node->array_slice_access.array, depth + 2);
+ if (node->array_slice_access.start) {
+ print_indent(depth + 1);
+ printf("Start:\n");
+ print_ast(node->array_slice_access.start, depth + 2);
+ } else {
+ print_indent(depth + 1);
+ printf("Start: NULL\n");
+ }
+ if (node->array_slice_access.end) {
+ print_indent(depth + 1);
+ printf("End:\n");
+ print_ast(node->array_slice_access.end, depth + 2);
+ } else {
+ print_indent(depth + 1);
+ printf("End: NULL\n");
+ }
+ if (node->array_slice_access.step) {
+ print_indent(depth + 1);
+ printf("Step:\n");
+ print_ast(node->array_slice_access.step, depth + 2);
+ } else {
+ print_indent(depth + 1);
+ printf("Step: NULL\n");
+ }
+ break;
+
+ case AST_VARIABLE_REFERENCE:
+ printf("Variable Reference: %s\n", node->variable_name);
+ break;
+
+ default:
+ printf("Unknown AST Node Type: %d\n", node->type);
+ break;
+ }
- // Move to the next node in the linked list
- node = node->next;
+ // Move to the next node in the linked list, if any
+ node = node->next;
+ }
}
}
diff --git a/src/parser/utils.h b/src/parser/utils.h
index 0551714..d9ab7eb 100644
--- a/src/parser/utils.h
+++ b/src/parser/utils.h
@@ -8,7 +8,7 @@
void free_ast(ASTNode *node);
// Print indentation based on depth
-static void print_indentation(int depth);
+void print_indent(int depth);
// Print AST nodes
void print_ast(ASTNode *node, int depth);
diff --git a/src/shared/ast_types.h b/src/shared/ast_types.h
index eba21e0..61502f0 100644
--- a/src/shared/ast_types.h
+++ b/src/shared/ast_types.h
@@ -5,15 +5,15 @@
#include
#include
-#define MAX_ARGUMENTS 1024
-
// AST Node Types
typedef enum {
+ AST_VAR_DECLARATION,
+ AST_CONST_DECLARATION,
AST_ASSIGNMENT,
+ AST_LITERAL,
AST_FUNCTION_DECLARATION,
AST_FUNCTION_CALL,
AST_FUNCTION_RETURN,
- AST_LITERAL,
AST_CONDITIONAL,
AST_UNARY_OP,
AST_BINARY_OP,
@@ -22,11 +22,14 @@ typedef enum {
AST_SWITCH,
AST_BREAK,
AST_TERNARY,
- AST_VAR_DECLARATION,
- AST_CONST_DECLARATION,
AST_TRY,
AST_CATCH,
- AST_FINALLY
+ AST_FINALLY,
+ AST_ARRAY_LITERAL,
+ AST_ARRAY_OPERATION,
+ AST_ARRAY_INDEX_ACCESS,
+ AST_ARRAY_SLICE_ACCESS,
+ AST_VARIABLE_REFERENCE
} ASTNodeType;
// Literal Node
@@ -97,9 +100,7 @@ typedef struct {
// AST Function Parameter
typedef struct ASTFunctionParameter {
- char *parameter_name; // Parameter name
- // LiteralNode *type; // Optional: parameter type (e.g.,
- // int, string, etc.)
+ char *parameter_name; // Parameter name
struct ASTFunctionParameter *next; // Linked list for multiple parameters
} ASTFunctionParameter;
@@ -128,48 +129,95 @@ typedef struct {
struct ASTNode *false_expr;
} ASTTernary;
-// AST Rescue Node (optional error object)
+// AST Catch Node
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)
+ struct ASTCatchNode *next; // For multiple `rescue` clauses
} 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
+ struct ASTNode *finally_block; // Optional `finish` block
} ASTTry;
+// AST Array Literal Node
+typedef struct {
+ struct ASTNode **elements; // Array of element expressions
+ size_t count; // Number of elements
+} ASTArrayLiteral;
+
+// AST Index Access Node
+typedef struct {
+ struct ASTNode *array; // The array expression
+ struct ASTNode *index; // The index expression
+} ASTArrayIndexAccess;
+
+// AST Slice Access Node
+typedef struct {
+ struct ASTNode *array; // The array expression
+ struct ASTNode *start; // Start index (can be NULL)
+ struct ASTNode *end; // End index (can be NULL)
+ struct ASTNode *step; // Step value (can be NULL)
+} ASTArraySliceAccess;
+
+// AST Array Operation Node
+typedef struct {
+ char *operator; // Array operation operator (e.g., `^+`, `+^`, `^-`, `-^`)
+ struct ASTNode *array; // Array on which the operation is performed
+ struct ASTNode *operand; // Element to operate with (e.g., to append)
+} ASTArrayOperation;
+
// AST Node Structure
typedef struct ASTNode {
ASTNodeType type;
union {
- // Assignment
+ // Variable Declaration
struct {
char *variable_name;
- struct ASTNode *value;
+ struct ASTNode *initializer;
+ } var_declaration;
+
+ // Constant Declaration
+ struct {
+ char *constant_name;
+ struct ASTNode *initializer;
+ } const_declaration;
+
+ // Assignment
+ struct {
+ struct ASTNode *lhs;
+ struct ASTNode *rhs;
} assignment;
- LiteralNode literal;
- ASTUnaryOp unary_op;
- ASTBinaryOp binary_op;
+ // Function Nodes
+ ASTFunctionDeclaration function_declaration;
+ ASTFunctionCall function_call;
+ ASTFunctionReturn function_return;
+
+ // Control Flow Nodes
ASTConditional conditional;
ASTSwitch switch_case;
ASTWhileLoop while_loop;
ASTForLoop for_loop;
ASTTernary ternary;
- ASTFunctionDeclaration function_declaration;
- ASTFunctionCall function_call;
- ASTFunctionReturn function_return;
+ ASTTry try_block;
- ASTTry try_block; // Try Block
- // ASTCatchNode *catch_block; // Rescue Block
- // struct ASTNode *finally_block; // Finish Block
+ // Operations
+ ASTUnaryOp unary_op;
+ ASTBinaryOp binary_op;
- // Variable
- char *variable_name;
+ // Array Nodes
+ ASTArrayLiteral array_literal;
+ ASTArrayOperation array_operation;
+ ASTArrayIndexAccess array_index_access;
+ ASTArraySliceAccess array_slice_access;
+
+ // Literal and Reference
+ LiteralNode literal;
+ char *variable_name; // For AST_VARIABLE_REFERENCE
};
struct ASTNode *next;
diff --git a/src/shared/token_types.h b/src/shared/token_types.h
index 6ccf368..9f87488 100644
--- a/src/shared/token_types.h
+++ b/src/shared/token_types.h
@@ -23,6 +23,9 @@ typedef enum {
TOKEN_BRACE_OPEN,
TOKEN_BRACE_CLOSE,
TOKEN_COLON,
+ TOKEN_ARRAY_OP,
+ TOKEN_SQ_BRACKET_OPEN,
+ TOKEN_SQ_BRACKET_CLOSE,
TOKEN_EOF
} TokenType;
diff --git a/src/tests/18_arrays.flv b/src/tests/18_arrays.flv
new file mode 100644
index 0000000..1b8d40d
--- /dev/null
+++ b/src/tests/18_arrays.flv
@@ -0,0 +1,93 @@
+# Initialize test array
+let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+serve("Initial array:", array);
+
+# Element Access
+serve("Accessing 6th element (index 5):", array[5]);
+
+# Append
+array[^+] = 11;
+serve("Array after appending 11:", array);
+
+# Prepend
+array[+^] = 0;
+serve("Array after prepending 0:", array);
+
+# Remove Last Element
+serve("Removing last element:", array[^-]);
+serve("Array after removing last element:", array);
+
+# Remove First Element
+serve("Removing first element:", array[-^]);
+serve("Array after removing first element:", array);
+
+# Slicing
+serve("Slice from index 0 to 5:", array[0:5]);
+serve("Reversed array:", array[::-1]);
+serve("Slice from index 3 to end:", array[3:]);
+serve("Slice from start to index 8:", array[:8]);
+serve("Slice from index 3 to 8, skipping every 2nd element:", array[3:8:2]);
+serve("Slice from index 8 to 3, skipping every 2nd element (reverse):", array[8:3:-2]);
+serve("Slice from index 3 to start (reversed):", array[3::-1]);
+serve("Slice from end towards index 8 (exclusive, reversed):", array[:8:-1]);
+serve("Slice entire array, skipping every 2nd element:", array[::2]);
+serve("Reverse array, skipping every 3rd element:", array[::-3]);
+
+# Test completed
+serve("Array operation tests complete!");
+
+# Initialize const (immutable) array
+const const_array = [10, 20, 30, 40, 50];
+serve("Initial const array:", const_array);
+
+# Attempt mutation
+try {
+ const_array[^+] = 60; # Trying to append to const array
+ serve("This should not be shown; const array was mutated!");
+} rescue {
+ serve("Caught error: Cannot mutate a const array.");
+}
+
+# Confirm immutability
+serve("Const array remains unchanged:", const_array);
+
+# Try to remove element
+try {
+ const_array[^-]; # Attempting to remove the last element
+ serve("This should not be shown; const array was mutated!");
+} rescue {
+ serve("Caught error: Cannot mutate a const array.");
+}
+
+# Final confirmation
+serve("Const array is still:", const_array);
+
+# Test completed
+serve("Immutable array tests complete!");
+
+
+# EXPECTED OUTPUT
+#
+# Initial array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+# Accessing 6th element (index 5): 6
+# Array after appending 11: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
+# Array after prepending 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
+# Array after removing last element: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+# Array after removing first element: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+# Slice from index 0 to 5: [1, 2, 3, 4, 5]
+# Reversed array: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
+# Slice from index 3 to end: [4, 5, 6, 7, 8, 9, 10]
+# Slice from start to index 8: [1, 2, 3, 4, 5, 6, 7, 8]
+# Slice from index 3 to 8, skipping every 2nd element: [4, 6, 8]
+# Slice from index 8 to 3, skipping every 2nd element (reverse): [9, 7, 5]
+# Slice from index 3 to start (reversed): [4, 3, 2, 1]
+# Slice from end towards index 8 (exclusive, reversed): [10, 9]
+# Slice entire array, skipping every 2nd element: [1, 3, 5, 7, 9]
+# Reverse array, skipping every 3rd element: [10, 7, 4, 1]
+# Array operation tests complete!
+# Initial const array: [10, 20, 30, 40, 50]
+# Caught error: Cannot mutate a const array.
+# Const array remains unchanged: [10, 20, 30, 40, 50]
+# Caught error: Cannot mutate a const array.
+# Const array is still: [10, 20, 30, 40, 50]
+# Immutable array tests complete!
diff --git a/src/tests/all.flv b/src/tests/all.flv
index 6e20ffb..a03199a 100644
--- a/src/tests/all.flv
+++ b/src/tests/all.flv
@@ -276,3 +276,73 @@ create test(num, func_a, func_b) {
let a = test(10, times_2, times_3); # 180
serve(a);
+
+# ==================================================
+# 18
+# ==================================================
+# Initialize test array
+let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+serve("Initial array:", array);
+
+# Element Access
+serve("Accessing 6th element (index 5):", array[5]);
+
+# Append
+array[^+] = 11;
+serve("Array after appending 11:", array);
+
+# Prepend
+array[+^] = 0;
+serve("Array after prepending 0:", array);
+
+# Remove Last Element
+serve("Removing last element:", array[^-]);
+serve("Array after removing last element:", array);
+
+# Remove First Element
+serve("Removing first element:", array[-^]);
+serve("Array after removing first element:", array);
+
+# Slicing
+serve("Slice from index 0 to 5:", array[0:5]);
+serve("Reversed array:", array[::-1]);
+serve("Slice from index 3 to end:", array[3:]);
+serve("Slice from start to index 8:", array[:8]);
+serve("Slice from index 3 to 8, skipping every 2nd element:", array[3:8:2]);
+serve("Slice from index 8 to 3, skipping every 2nd element (reverse):", array[8:3:-2]);
+serve("Slice from index 3 to start (reversed):", array[3::-1]);
+serve("Slice from end towards index 8 (exclusive, reversed):", array[:8:-1]);
+serve("Slice entire array, skipping every 2nd element:", array[::2]);
+serve("Reverse array, skipping every 3rd element:", array[::-3]);
+
+# Test completed
+serve("Array operation tests complete!");
+
+# Initialize const (immutable) array
+const const_array = [10, 20, 30, 40, 50];
+serve("Initial const array:", const_array);
+
+# Attempt mutation
+try {
+ const_array[^+] = 60; # Trying to append to const array
+ serve("This should not be shown; const array was mutated!");
+} rescue {
+ serve("Caught error: Cannot mutate a const array.");
+}
+
+# Confirm immutability
+serve("Const array remains unchanged:", const_array);
+
+# Try to remove element
+try {
+ const_array[^-]; # Attempting to remove the last element
+ serve("This should not be shown; const array was mutated!");
+} rescue {
+ serve("Caught error: Cannot mutate a const array.");
+}
+
+# Final confirmation
+serve("Const array is still:", const_array);
+
+# Test completed
+serve("Immutable array tests complete!");