diff options
Diffstat (limited to 'js/scripting-lang/baba-yaga-c/src/interpreter.c')
-rw-r--r-- | js/scripting-lang/baba-yaga-c/src/interpreter.c | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/js/scripting-lang/baba-yaga-c/src/interpreter.c b/js/scripting-lang/baba-yaga-c/src/interpreter.c new file mode 100644 index 0000000..d06eb30 --- /dev/null +++ b/js/scripting-lang/baba-yaga-c/src/interpreter.c @@ -0,0 +1,680 @@ +/** + * @file interpreter.c + * @brief Interpreter implementation for Baba Yaga + * @author eli_oat + * @version 0.0.1 + * @date 2025 + * + * This file implements the main interpreter for the Baba Yaga language. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "baba_yaga.h" + +/* Forward declarations for function types */ +typedef struct { + char* name; + bool is_optional; +} FunctionParam; + +typedef enum { + FUNC_NATIVE, + FUNC_USER +} FunctionType; + +typedef struct { + void* ast_node; + char* source; +} FunctionBody; + +typedef struct { + char* name; + FunctionType type; + FunctionParam* params; + int param_count; + int required_params; + union { + Value (*native_func)(Value*, int); + FunctionBody user_body; + } body; + void* closure_scope; + int ref_count; +} FunctionValue; + +/* Forward declarations */ +Value interpreter_evaluate_expression(void* node, Scope* scope); +static Value interpreter_evaluate_statement(void* node, Scope* scope); + +/* ============================================================================ + * Interpreter Structure + * ============================================================================ */ + +struct Interpreter { + Scope* global_scope; + BabaYagaError* last_error; + DebugLevel debug_level; +}; + +/* ============================================================================ + * Standard Library Registration + * ============================================================================ */ + +/** + * @brief Register standard library functions in the global scope + * + * @param scope Global scope to register functions in + */ +static void register_stdlib(Scope* scope) { + DEBUG_INFO("Registering standard library functions"); + + /* Core combinator */ + Value apply_func = baba_yaga_value_function("apply", stdlib_apply, 10, 1); + scope_define(scope, "apply", apply_func, true); + + /* Predefined variables for testing */ + Value hello_var = baba_yaga_value_string("hello"); + scope_define(scope, "hello", hello_var, true); + + /* Arithmetic functions */ + Value add_func = baba_yaga_value_function("add", stdlib_add, 2, 2); + scope_define(scope, "add", add_func, true); + + Value subtract_func = baba_yaga_value_function("subtract", stdlib_subtract, 2, 2); + scope_define(scope, "subtract", subtract_func, true); + + Value multiply_func = baba_yaga_value_function("multiply", stdlib_multiply, 2, 2); + scope_define(scope, "multiply", multiply_func, true); + + Value divide_func = baba_yaga_value_function("divide", stdlib_divide, 2, 2); + scope_define(scope, "divide", divide_func, true); + + Value modulo_func = baba_yaga_value_function("modulo", stdlib_modulo, 2, 2); + scope_define(scope, "modulo", modulo_func, true); + + Value pow_func = baba_yaga_value_function("pow", stdlib_pow, 2, 2); + scope_define(scope, "pow", pow_func, true); + + Value negate_func = baba_yaga_value_function("negate", stdlib_negate, 1, 1); + scope_define(scope, "negate", negate_func, true); + + /* Comparison functions */ + Value equals_func = baba_yaga_value_function("equals", stdlib_equals, 2, 2); + scope_define(scope, "equals", equals_func, true); + + Value not_equals_func = baba_yaga_value_function("not_equals", stdlib_not_equals, 2, 2); + scope_define(scope, "not_equals", not_equals_func, true); + + Value less_func = baba_yaga_value_function("less", stdlib_less, 2, 2); + scope_define(scope, "less", less_func, true); + + Value less_equal_func = baba_yaga_value_function("less_equal", stdlib_less_equal, 2, 2); + scope_define(scope, "less_equal", less_equal_func, true); + + Value greater_func = baba_yaga_value_function("greater", stdlib_greater, 2, 2); + scope_define(scope, "greater", greater_func, true); + + Value greater_equal_func = baba_yaga_value_function("greater_equal", stdlib_greater_equal, 2, 2); + scope_define(scope, "greater_equal", greater_equal_func, true); + + /* Logical functions */ + Value and_func = baba_yaga_value_function("and", stdlib_and, 2, 2); + scope_define(scope, "and", and_func, true); + + Value or_func = baba_yaga_value_function("or", stdlib_or, 2, 2); + scope_define(scope, "or", or_func, true); + + Value xor_func = baba_yaga_value_function("xor", stdlib_xor, 2, 2); + scope_define(scope, "xor", xor_func, true); + + Value not_func = baba_yaga_value_function("not", stdlib_not, 1, 1); + scope_define(scope, "not", not_func, true); + + /* Function composition */ + Value compose_func = baba_yaga_value_function("compose", stdlib_compose, 4, 2); + scope_define(scope, "compose", compose_func, true); + + /* IO functions */ + Value out_func = baba_yaga_value_function("out", stdlib_out, 1, 1); + scope_define(scope, "out", out_func, true); + + Value in_func = baba_yaga_value_function("in", stdlib_in, 0, 0); + scope_define(scope, "in", in_func, true); + + Value assert_func = baba_yaga_value_function("assert", stdlib_assert, 1, 1); + scope_define(scope, "assert", assert_func, true); + + /* Higher-order functions */ + Value map_func = baba_yaga_value_function("map", stdlib_map, 2, 2); + scope_define(scope, "map", map_func, true); + + Value filter_func = baba_yaga_value_function("filter", stdlib_filter, 2, 2); + scope_define(scope, "filter", filter_func, true); + + Value reduce_func = baba_yaga_value_function("reduce", stdlib_reduce, 3, 3); + scope_define(scope, "reduce", reduce_func, true); + + DEBUG_INFO("Registered %d standard library functions", 20); +} + +/* ============================================================================ + * Core API Functions + * ============================================================================ */ + +Interpreter* baba_yaga_create(void) { + Interpreter* interp = malloc(sizeof(Interpreter)); + if (interp == NULL) { + return NULL; + } + + /* Create global scope */ + interp->global_scope = scope_create(NULL); + if (interp->global_scope == NULL) { + free(interp); + return NULL; + } + + /* Initialize error handling */ + interp->last_error = NULL; + interp->debug_level = DEBUG_NONE; + + /* Register standard library */ + register_stdlib(interp->global_scope); + + DEBUG_INFO("Interpreter created successfully"); + return interp; +} + +void baba_yaga_destroy(Interpreter* interp) { + if (interp == NULL) { + return; + } + + /* Destroy global scope */ + if (interp->global_scope != NULL) { + scope_destroy(interp->global_scope); + } + + /* Destroy last error */ + if (interp->last_error != NULL) { + baba_yaga_error_destroy(interp->last_error); + } + + free(interp); + DEBUG_INFO("Interpreter destroyed"); +} + +Value baba_yaga_execute(Interpreter* interp, const char* source, + size_t source_len, ExecResult* result) { + if (interp == NULL || source == NULL || result == NULL) { + if (result != NULL) { + *result = EXEC_ERROR; + } + return baba_yaga_value_nil(); + } + + DEBUG_INFO("Executing source code (length: %zu)", source_len); + + /* Tokenize */ + void* tokens[1000]; + int token_count = baba_yaga_tokenize(source, source_len, tokens, 1000); + + if (token_count <= 0) { + DEBUG_ERROR("Failed to tokenize source code"); + *result = EXEC_ERROR; + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("Tokenized into %d tokens", token_count); + + /* Parse */ + void* ast = baba_yaga_parse(tokens, token_count); + baba_yaga_free_tokens(tokens, token_count); + + if (ast == NULL) { + DEBUG_ERROR("Failed to parse source code"); + *result = EXEC_ERROR; + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("Parsed AST successfully"); + + if (interp->debug_level >= DEBUG_DEBUG) { + printf("AST:\n"); + baba_yaga_print_ast(ast, 0); + } + + /* Execute */ + Value result_value = interpreter_evaluate_expression(ast, interp->global_scope); + baba_yaga_destroy_ast(ast); + + if (result_value.type == VAL_NIL) { + *result = EXEC_ERROR; + } else { + *result = EXEC_SUCCESS; + } + + DEBUG_INFO("Execution completed"); + return result_value; +} + +Value baba_yaga_execute_file(Interpreter* interp, const char* filename, + ExecResult* result) { + if (interp == NULL || filename == NULL || result == NULL) { + if (result != NULL) { + *result = EXEC_ERROR; + } + return baba_yaga_value_nil(); + } + + DEBUG_INFO("Executing file: %s", filename); + + /* Read file */ + FILE* file = fopen(filename, "r"); + if (file == NULL) { + DEBUG_ERROR("Failed to open file: %s", filename); + *result = EXEC_ERROR; + return baba_yaga_value_nil(); + } + + /* Get file size */ + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (file_size <= 0) { + DEBUG_ERROR("File is empty or invalid: %s", filename); + fclose(file); + *result = EXEC_ERROR; + return baba_yaga_value_nil(); + } + + /* Read content */ + char* source = malloc(file_size + 1); + if (source == NULL) { + DEBUG_ERROR("Failed to allocate memory for file content"); + fclose(file); + *result = EXEC_ERROR; + return baba_yaga_value_nil(); + } + + size_t bytes_read = fread(source, 1, file_size, file); + source[bytes_read] = '\0'; + fclose(file); + + /* Execute */ + Value result_value = baba_yaga_execute(interp, source, bytes_read, result); + free(source); + + return result_value; +} + +/* ============================================================================ + * Expression Evaluation + * ============================================================================ */ + +/** + * @brief Evaluate an expression node + * + * @param node AST node to evaluate + * @param scope Current scope + * @return Result value + */ +Value interpreter_evaluate_expression(void* node, Scope* scope) { + if (node == NULL) { + return baba_yaga_value_nil(); + } + + NodeType node_type = baba_yaga_ast_get_type(node); + DEBUG_TRACE("Evaluating expression: type %d", node_type); + + switch (node_type) { + case NODE_LITERAL: + return baba_yaga_ast_get_literal(node); + + case NODE_IDENTIFIER: { + const char* identifier = baba_yaga_ast_get_identifier(node); + if (identifier == NULL) { + DEBUG_ERROR("Invalid identifier node"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("Looking up identifier: %s", identifier); + + /* Check if this is a function reference (starts with @) */ + if (identifier[0] == '@') { + /* Strip the @ prefix and look up the function */ + const char* func_name = identifier + 1; + DEBUG_DEBUG("Function reference: %s", func_name); + Value value = scope_get(scope, func_name); + DEBUG_DEBUG("Function '%s' lookup result type: %d", func_name, value.type); + if (value.type == VAL_NIL) { + DEBUG_ERROR("Undefined function: %s", func_name); + } + return value; + } else { + /* Regular variable lookup */ + Value value = scope_get(scope, identifier); + DEBUG_DEBUG("Identifier '%s' lookup result type: %d", identifier, value.type); + if (value.type == VAL_NIL) { + DEBUG_ERROR("Undefined variable: %s", identifier); + } + return value; + } + } + + case NODE_FUNCTION_CALL: { + /* Evaluate function */ + void* func_node = baba_yaga_ast_get_function_call_func(node); + Value func_value = interpreter_evaluate_expression(func_node, scope); + + DEBUG_DEBUG("Function call - function value type: %d", func_value.type); + + if (func_value.type != VAL_FUNCTION) { + DEBUG_ERROR("Cannot call non-function value"); + baba_yaga_value_destroy(&func_value); + return baba_yaga_value_nil(); + } + + /* Evaluate arguments */ + int arg_count = baba_yaga_ast_get_function_call_arg_count(node); + Value* args = malloc(arg_count * sizeof(Value)); + if (args == NULL) { + DEBUG_ERROR("Failed to allocate memory for function arguments"); + baba_yaga_value_destroy(&func_value); + return baba_yaga_value_nil(); + } + + for (int i = 0; i < arg_count; i++) { + void* arg_node = baba_yaga_ast_get_function_call_arg(node, i); + args[i] = interpreter_evaluate_expression(arg_node, scope); + } + + /* Call function */ + DEBUG_DEBUG("Calling function with %d arguments", arg_count); + Value result = baba_yaga_function_call(&func_value, args, arg_count, scope); + DEBUG_DEBUG("Function call returned type: %d", result.type); + + /* Cleanup */ + for (int i = 0; i < arg_count; i++) { + baba_yaga_value_destroy(&args[i]); + } + free(args); + baba_yaga_value_destroy(&func_value); + + return result; + } + + case NODE_BINARY_OP: { + void* left_node = baba_yaga_ast_get_binary_op_left(node); + void* right_node = baba_yaga_ast_get_binary_op_right(node); + const char* operator = baba_yaga_ast_get_binary_op_operator(node); + + if (left_node == NULL || right_node == NULL || operator == NULL) { + DEBUG_ERROR("Invalid binary operation node"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("Binary operator: %s", operator); + + Value left = interpreter_evaluate_expression(left_node, scope); + Value right = interpreter_evaluate_expression(right_node, scope); + + /* Create function call for the operator */ + Value func_value = scope_get(scope, operator); + DEBUG_DEBUG("Function lookup for '%s': type %d", operator, func_value.type); + if (func_value.type != VAL_FUNCTION) { + DEBUG_ERROR("Unknown operator: %s", operator); + baba_yaga_value_destroy(&left); + baba_yaga_value_destroy(&right); + return baba_yaga_value_nil(); + } + + Value args[2] = {left, right}; + Value result = baba_yaga_function_call(&func_value, args, 2, scope); + + baba_yaga_value_destroy(&left); + baba_yaga_value_destroy(&right); + baba_yaga_value_destroy(&func_value); + + return result; + } + + case NODE_UNARY_OP: { + void* operand_node = baba_yaga_ast_get_unary_op_operand(node); + const char* operator = baba_yaga_ast_get_unary_op_operator(node); + + if (operand_node == NULL || operator == NULL) { + DEBUG_ERROR("Invalid unary operation node"); + return baba_yaga_value_nil(); + } + + Value operand = interpreter_evaluate_expression(operand_node, scope); + + /* Create function call for the operator */ + Value func_value = scope_get(scope, operator); + if (func_value.type != VAL_FUNCTION) { + DEBUG_ERROR("Unknown operator: %s", operator); + baba_yaga_value_destroy(&operand); + return baba_yaga_value_nil(); + } + + Value args[1] = {operand}; + Value result = baba_yaga_function_call(&func_value, args, 1, scope); + + baba_yaga_value_destroy(&operand); + baba_yaga_value_destroy(&func_value); + + return result; + } + + case NODE_FUNCTION_DEF: { + const char* name = baba_yaga_ast_get_function_def_name(node); + int param_count = baba_yaga_ast_get_function_def_param_count(node); + void* body_node = baba_yaga_ast_get_function_def_body(node); + + if (name == NULL || body_node == NULL) { + DEBUG_ERROR("Invalid function definition node"); + return baba_yaga_value_nil(); + } + + /* Create user-defined function value */ + FunctionValue* func_value = malloc(sizeof(FunctionValue)); + if (func_value == NULL) { + DEBUG_ERROR("Failed to allocate memory for function"); + return baba_yaga_value_nil(); + } + + /* Initialize function value */ + func_value->name = strdup(name); + func_value->type = FUNC_USER; + func_value->param_count = param_count; + func_value->required_params = param_count; + func_value->ref_count = 1; + func_value->closure_scope = NULL; /* TODO: Implement closures */ + + /* Allocate and copy parameters */ + func_value->params = malloc(param_count * sizeof(FunctionParam)); + if (func_value->params == NULL) { + free(func_value->name); + free(func_value); + DEBUG_ERROR("Failed to allocate memory for function parameters"); + return baba_yaga_value_nil(); + } + + for (int i = 0; i < param_count; i++) { + void* param_node = baba_yaga_ast_get_function_def_param(node, i); + if (param_node != NULL && baba_yaga_ast_get_type(param_node) == NODE_IDENTIFIER) { + const char* param_name = baba_yaga_ast_get_identifier(param_node); + func_value->params[i].name = strdup(param_name); + func_value->params[i].is_optional = false; + } else { + func_value->params[i].name = NULL; + func_value->params[i].is_optional = false; + } + } + + /* Store function body */ + func_value->body.user_body.ast_node = body_node; + func_value->body.user_body.source = NULL; /* TODO: Store source for debugging */ + + /* Create function value */ + Value func_val; + func_val.type = VAL_FUNCTION; + func_val.data.function = func_value; + + /* Define in current scope */ + scope_define(scope, name, func_val, false); + + return func_val; + } + + case NODE_VARIABLE_DECL: { + const char* name = baba_yaga_ast_get_variable_decl_name(node); + void* value_node = baba_yaga_ast_get_variable_decl_value(node); + + if (name == NULL || value_node == NULL) { + DEBUG_ERROR("Invalid variable declaration node"); + return baba_yaga_value_nil(); + } + + Value value = interpreter_evaluate_expression(value_node, scope); + scope_define(scope, name, value, false); + return value; + } + + case NODE_SEQUENCE: { + int statement_count = baba_yaga_ast_get_sequence_statement_count(node); + DEBUG_DEBUG("Executing sequence with %d statements", statement_count); + + Value result = baba_yaga_value_nil(); + + /* Execute all statements in sequence */ + for (int i = 0; i < statement_count; i++) { + void* statement_node = baba_yaga_ast_get_sequence_statement(node, i); + if (statement_node == NULL) { + DEBUG_ERROR("Invalid statement node at index %d", i); + continue; + } + + /* Destroy previous result before evaluating next statement */ + baba_yaga_value_destroy(&result); + + /* Evaluate statement */ + result = interpreter_evaluate_expression(statement_node, scope); + DEBUG_DEBUG("Statement %d result type: %d", i, result.type); + } + + return result; /* Return result of last statement */ + } + + case NODE_WHEN_EXPR: { + /* Evaluate the test expression */ + void* test_node = baba_yaga_ast_get_when_expr_test(node); + Value test_value = interpreter_evaluate_expression(test_node, scope); + + /* Get patterns */ + int pattern_count = baba_yaga_ast_get_when_expr_pattern_count(node); + + /* Try each pattern in order */ + for (int i = 0; i < pattern_count; i++) { + void* pattern_node = baba_yaga_ast_get_when_expr_pattern(node, i); + if (pattern_node == NULL) { + continue; + } + + /* Evaluate pattern test */ + void* pattern_test_node = baba_yaga_ast_get_when_pattern_test(pattern_node); + Value pattern_test_value = interpreter_evaluate_expression(pattern_test_node, scope); + + /* Check if pattern matches */ + bool matches = false; + if (pattern_test_value.type == VAL_NUMBER && test_value.type == VAL_NUMBER) { + matches = (pattern_test_value.data.number == test_value.data.number); + } else if (pattern_test_value.type == VAL_STRING && test_value.type == VAL_STRING) { + matches = (strcmp(pattern_test_value.data.string, test_value.data.string) == 0); + } else if (pattern_test_value.type == VAL_BOOLEAN && test_value.type == VAL_BOOLEAN) { + matches = (pattern_test_value.data.boolean == test_value.data.boolean); + } else if (pattern_test_value.type == VAL_STRING && + strcmp(pattern_test_value.data.string, "_") == 0) { + /* Wildcard pattern always matches */ + matches = true; + } + + baba_yaga_value_destroy(&pattern_test_value); + + if (matches) { + /* Pattern matches, evaluate result */ + void* result_node = baba_yaga_ast_get_when_pattern_result(pattern_node); + Value result = interpreter_evaluate_expression(result_node, scope); + baba_yaga_value_destroy(&test_value); + return result; + } + } + + /* No pattern matched */ + baba_yaga_value_destroy(&test_value); + DEBUG_ERROR("No matching pattern in when expression"); + return baba_yaga_value_nil(); + } + + default: + DEBUG_ERROR("Unsupported expression type: %d", node_type); + return baba_yaga_value_nil(); + } +} + +/** + * @brief Evaluate a statement node + * + * @param node AST node to evaluate + * @param scope Current scope + * @return Result value + */ +__attribute__((unused)) static Value interpreter_evaluate_statement(void* node, Scope* scope) { + if (node == NULL) { + return baba_yaga_value_nil(); + } + + NodeType node_type = baba_yaga_ast_get_type(node); + DEBUG_TRACE("Evaluating statement: type %d", node_type); + + switch (node_type) { + case NODE_VARIABLE_DECL: + case NODE_FUNCTION_DEF: + return interpreter_evaluate_expression(node, scope); + + default: + DEBUG_ERROR("Unsupported statement type: %d", node_type); + return baba_yaga_value_nil(); + } +} + +/* ============================================================================ + * Error Handling Functions + * ============================================================================ */ + +BabaYagaError* baba_yaga_get_error(const Interpreter* interp) { + if (interp == NULL) { + return NULL; + } + + return interp->last_error; +} + +void baba_yaga_error_destroy(BabaYagaError* error) { + if (error == NULL) { + return; + } + + if (error->message != NULL) { + free(error->message); + } + if (error->source_file != NULL) { + free(error->source_file); + } + + free(error); +} \ No newline at end of file |